diff --git a/docs/docs/creating_and_uploading_studies.mdx b/docs/docs/creating_and_uploading_studies.mdx index 1385f27..2cd4485 100644 --- a/docs/docs/creating_and_uploading_studies.mdx +++ b/docs/docs/creating_and_uploading_studies.mdx @@ -102,9 +102,9 @@ ec_geojson = [] Now, we will create a `Result` object named "Energy Consumers" with a GeoJSON overlay. The GeoJSON overlay includes the collected GeoJSON features in a `FeatureCollection` and a style named "ec-heatmap". ```python -ec_result = Result( +ec_result = StudyResultInput( name="Energy Consumers", - geo_json_overlay=GeoJsonOverlay( + geoJsonOverlay=GeoJsonOverlayInput( data=FeatureCollection(ec_geojson), styles=["ec-heatmap"] # Select which Mapbox layers to show for this result ) @@ -154,9 +154,9 @@ lv_lines_geojson = [] Now, we will create a `Result` object named "LV Lines" with a GeoJSON overlay. The GeoJSON overlay includes the collected GeoJSON features in a FeatureCollection. ```python -lv_lines_result = Result( +lv_lines_result = StudyResultInput( name="LV Lines", - geo_json_overlay=GeoJsonOverlay( + geoJsonOverlay=GeoJsonOverlayInput( data=FeatureCollection(lv_lines_geojson), styles=["lv-lines", "lv-lengths"] # Select which Mapbox layers to show for this result ) @@ -231,7 +231,7 @@ Once you have created the Results for the studies, the next step is to create an A data class (`Study`) represents an Evolve App Server study. The user need to provide information such as name, descriptions, tags, results, and styles to successfully create a study. ```python -study = Study( +study = StudyInput( name="Example Study", description="Example study with two results.", tags=["example"], @@ -253,8 +253,8 @@ Note: Each layer may have an entry in the legend via the metadata["zb:legend"] f The final step after creating the study is to upload it on the EAS and close the EAS client, as follows. ```python -await eas_client.async_upload_study(study) -await eas_client.aclose() +await eas_client.upload_study(study) +await eas_client.close() ``` ## Output (Studies) diff --git a/pyproject.toml b/pyproject.toml index 7782439..3c4e84f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ authors = [ {name = "Zeppelin Bend", email = "oss@zepben.com"} ] dependencies = [ - "zepben.eas==0.27.0b1", + "zepben.eas==1.0.0b1", "zepben.ewb==1.1.0b14", "numba==0.60.0", "geojson==2.5.0", diff --git a/src/zepben/examples/export_open_dss_model.py b/src/zepben/examples/export_open_dss_model.py index c528ab2..d449c7d 100644 --- a/src/zepben/examples/export_open_dss_model.py +++ b/src/zepben/examples/export_open_dss_model.py @@ -6,10 +6,9 @@ import json from datetime import datetime -from zepben.eas.client.opendss import OpenDssConfig -from zepben.eas.client.work_package import GeneratorConfig, ModelConfig, FeederScenarioAllocationStrategy, SolveConfig, RawResultsConfig, \ - MeterPlacementConfig, SwitchMeterPlacementConfig, SwitchClass -from zepben.eas import EasClient, TimePeriod, FixedTime +from zepben.eas import EasClient, OpenDssModelInput, OpenDssModulesConfigInput, OpenDssModelGenerationSpecInput, OpenDssModelOptionsInput, \ + OpenDssCommonConfigInput, HcGeneratorConfigInput, TimePeriodInput, FixedTimeInput, HcModelConfigInput, HcSolveConfigInput, HcNodeLevelResultsConfigInput, \ + HcRawResultsConfigInput, HcMeterPlacementConfigInput, HcSwitchMeterPlacementConfigInput, HcSwitchClass, HcFeederScenarioAllocationStrategy from time import sleep import requests @@ -69,62 +68,81 @@ def open_dss_export(export_file_name: str): eas_client = EasClient( host=c["host"], port=c["rpc_port"], - access_token=c["access_token"] + access_token=c["access_token"], ) # Run an opendss export print("Sending OpenDss model export request to EAS") response = eas_client.run_opendss_export( - OpenDssConfig( - scenario="base", - year=2025, - feeder="", - load_time=TimePeriod( - start_time=datetime.fromisoformat("2024-04-01T00:00"), - end_time=datetime.fromisoformat("2025-04-01T00:00") - ), - # For fixed time export example, pass load_time a FixedTime object - #load_time=FixedTime( - # time=datetime.fromisoformat("2024-04-01T00:00") - #), - generator_config=GeneratorConfig( - model=ModelConfig( - meter_placement_config=MeterPlacementConfig( - feeder_head=True, - dist_transformers=True, - # Include meters for any switch that has a name that starts with 'LV Circuit Head' and is a Fuse or Disconnector - switch_meter_placement_configs=[SwitchMeterPlacementConfig( - meter_switch_class=SwitchClass.DISCONNECTOR, - name_pattern="LV Circuit Head.*" - ), SwitchMeterPlacementConfig( - meter_switch_class=SwitchClass.FUSE, - name_pattern="LV Circuit Head.*" - )] - ), - load_vmax_pu=1.2, - load_vmin_pu=0.8, - p_factor_base_exports=-1, - p_factor_base_imports=1, - p_factor_forecast_pv=0.98, - fix_single_phase_loads=True, - max_single_phase_load=15000.0, - max_load_service_line_ratio=1.0, - max_load_lv_line_ratio=2.0, - max_load_tx_ratio=2.0, - max_gen_tx_ratio=4.0, - fix_overloading_consumers=True, - fix_undersized_service_lines=True, - feeder_scenario_allocation_strategy=FeederScenarioAllocationStrategy.ADDITIVE, - closed_loop_v_reg_enabled=True, - closed_loop_v_reg_set_point=0.9825, - seed=123, + OpenDssModelInput( + generationSpec=OpenDssModelGenerationSpecInput( + modelOptions=OpenDssModelOptionsInput( + scenario="base", + year=2025, + feeder="", ), - solve=SolveConfig(step_size_minutes=30.0), - raw_results=RawResultsConfig(True, True, True, True, True) + modulesConfiguration=OpenDssModulesConfigInput( + common=OpenDssCommonConfigInput( + timePeriod=TimePeriodInput( + startTime=datetime.fromisoformat("2024-04-01T00:00"), + endTime=datetime.fromisoformat("2025-04-01T00:00") + ), + # For fixed time export example, pass load_time a FixedTimeInput object + # fixedTime=FixedTimeInput( + # loadTime=datetime.fromisoformat("2024-04-01T00:00") + # ) + ), + generator=HcGeneratorConfigInput( + model=HcModelConfigInput( + meterPlacementConfig=HcMeterPlacementConfigInput( + feederHead=True, + distTransformers=True, + # Include meters for any switch that has a name that starts with 'LV Circuit Head' and is a Fuse or Disconnector + switchMeterPlacementConfigs=[ + HcSwitchMeterPlacementConfigInput( + meterSwitchClass=HcSwitchClass.DISCONNECTOR, + namePattern="LV Circuit Head.*" + ), HcSwitchMeterPlacementConfigInput( + meterSwitchClass=HcSwitchClass.FUSE, + namePattern="LV Circuit Head.*" + ) + ] + ), + loadVMaxPu=1.2, + loadVMinPu=0.8, + pFactorBaseExports=-1, + pFactorBaseImports=1, + pFactorForecastPv=0.98, + fixSinglePhaseLoads=True, + maxSinglePhaseLoad=15000.0, + maxLoadServiceLineRatio=1.0, + maxLoadLvLineRatio=2.0, + maxLoadTxRatio=2.0, + maxGenTxRatio=4.0, + fixOverloadingConsumers=True, + fixUndersizedServiceLines=True, + feederScenarioAllocationStrategy=HcFeederScenarioAllocationStrategy.ADDITIVE, + closedLoopVRegEnabled=True, + closedLoopVRegSetPoint=0.9825, + seed=123, + + ), + solve=HcSolveConfigInput( + stepSizeMinutes=30 + ), + rawResults=HcRawResultsConfigInput( + energyMetersRaw=True, + energyMeterVoltagesRaw=True, + overloadsRaw=True, + resultsPerMeter=True, + voltageExceptionsRaw=True, + ), + ), + ) ), - model_name=export_file_name, - is_public=True + isPublic=True, + modelName=export_file_name, ) ) print(f"Raw 'run_opendss_export' response: '{response}'") diff --git a/src/zepben/examples/request_feeder_load_analysis_study.py b/src/zepben/examples/request_feeder_load_analysis_study.py index 0e095e1..8198ada 100644 --- a/src/zepben/examples/request_feeder_load_analysis_study.py +++ b/src/zepben/examples/request_feeder_load_analysis_study.py @@ -6,7 +6,7 @@ import asyncio import sys -from zepben.eas.client.feeder_load_analysis_input import FeederLoadAnalysisInput +from zepben.eas import FeederLoadAnalysisInput from zepben.examples.utils import get_config_dir, get_client @@ -18,18 +18,18 @@ async def main(argv): eas_client = get_client(config_dir) print("Connection established..") # Fire off a feeder load analysis study - feeder_load_analysis_token = await eas_client.async_run_feeder_load_analysis_report( + feeder_load_analysis_token = await eas_client.run_feeder_load_analysis_report( FeederLoadAnalysisInput( feeders=["feeder1", "feeder2"], substations=None, - sub_geographical_regions=None, - geographical_regions=None, - start_date="2022-04-01", - end_date="2022-12-31", - fetch_lv_network=True, - process_feeder_loads=True, - process_coincident_loads=True, - aggregate_at_feeder_level=False, + subGeographicalRegions=None, + geographicalRegions=None, + startDate="2022-04-01", + endDate="2022-12-31", + fetchLvNetwork=True, + processFeederLoads=True, + processCoincidentLoads=True, + aggregateAtFeederLevel=False, output="Test" ) ) diff --git a/src/zepben/examples/request_forecast_feeder_load_analysis_study.py b/src/zepben/examples/request_forecast_feeder_load_analysis_study.py index faa4d50..60529f8 100644 --- a/src/zepben/examples/request_forecast_feeder_load_analysis_study.py +++ b/src/zepben/examples/request_forecast_feeder_load_analysis_study.py @@ -6,8 +6,7 @@ import asyncio import sys -from zepben.eas.client.feeder_load_analysis_input import FeederLoadAnalysisInput -from zepben.eas.client.fla_forecast_config import FlaForecastConfig +from zepben.eas import FeederLoadAnalysisInput, FlaForecastConfigInput from zepben.examples.utils import get_config_dir, get_client @@ -22,18 +21,18 @@ async def main(argv): feeder_load_analysis_token = await eas_client.async_run_feeder_load_analysis_report( FeederLoadAnalysisInput( feeders=["feeder1", "feeder2"], - start_date="2022-04-01", - end_date="2022-12-31", - fetch_lv_network=True, - process_feeder_loads=True, - process_coincident_loads=True, - aggregate_at_feeder_level=False, + startDate="2022-04-01", + endDate="2022-12-31", + fetchLvNetwork=True, + processFeederLoads=True, + processCoincidentLoads=True, + aggregateAtFeederLevel=False, output="Test", - fla_forecast_config=FlaForecastConfig( - scenario_id='1', + flaForecastConfig=FlaForecastConfigInput( + scenarioID='1', year=2029, - pv_upgrade_threshold=6500, ##Premise with existing PV will gain additional PV until the threshold wattage is reached. - bess_upgrade_threshold=6500, ##Premise with existing battery will gain additional battery until the threshold wattage is reached. + pvUpgradeThreshold=6500, ##Premise with existing PV will gain additional PV until the threshold wattage is reached. + bessUpgradeThreshold=6500, ##Premise with existing battery will gain additional battery until the threshold wattage is reached. seed=12345 ##Seed can be set to keep modification of forecast network consistent between studies. ) ) @@ -43,7 +42,7 @@ async def main(argv): # Feeder Load Analysis Study results can be retrieved from back end storage set up with EAS. - await eas_client.aclose() + await eas_client.close() if __name__ == "__main__": diff --git a/src/zepben/examples/studies/creating_and_uploading_study.py b/src/zepben/examples/studies/creating_and_uploading_study.py index 201b89d..bdeb9e1 100644 --- a/src/zepben/examples/studies/creating_and_uploading_study.py +++ b/src/zepben/examples/studies/creating_and_uploading_study.py @@ -7,7 +7,7 @@ import json from geojson import Feature, LineString, FeatureCollection, Point -from zepben.eas import Study, Result, GeoJsonOverlay, EasClient +from zepben.eas import StudyInput, StudyResultInput, GeoJsonOverlayInput, EasClient, Mutation from zepben.ewb import AcLineSegment, EnergyConsumer, connect_with_token, NetworkConsumerClient, IncludedEnergizedContainers # A study is a geographical visualisation of data that is drawn on top of the network. # This data is typically the result of a load flow simulation. @@ -24,7 +24,12 @@ async def main(): # Fetch network model from Energy Workbench's gRPC service (see ../connecting_to_grpc_service.py for examples on different connection functions) print("Connecting to EWB..") - grpc_channel = connect_with_token(host=c["host"], access_token=c["access_token"], rpc_port=c["rpc_port"]) + grpc_channel = connect_with_token( + host=c["host"], + access_token=c["access_token"], + rpc_port=c["rpc_port"] + ) + feeder_mrid = "WD24" grpc_client = NetworkConsumerClient(grpc_channel) print("Connection established..") @@ -43,9 +48,9 @@ async def main(): ) ec_geojson.append(ec_feature) - ec_result = Result( + ec_result = StudyResultInput( name="Energy Consumers", - geo_json_overlay=GeoJsonOverlay( + geoJsonOverlay=GeoJsonOverlayInput( data=FeatureCollection(ec_geojson), styles=["ec-heatmap"] # Select which Mapbox layers to show for this result ) @@ -64,16 +69,16 @@ async def main(): ) lv_lines_geojson.append(line_feature) - lv_lines_result = Result( + lv_lines_result = StudyResultInput( name="LV Lines", - geo_json_overlay=GeoJsonOverlay( + geoJsonOverlay=GeoJsonOverlayInput( data=FeatureCollection(lv_lines_geojson), styles=["lv-lines", "lv-lengths"] # Select which Mapbox layers to show for this result ) ) # Create and upload the study. - study = Study( + study = StudyInput( name="Example Study", description="Example study with two results.", tags=["example"], # Tags make it easy to search for studies in a large list of them. @@ -84,16 +89,22 @@ async def main(): ) print("Study created..") print("Connecting to EAS..") - eas_client = EasClient(host=c["host"], port=c["rpc_port"], protocol="https", access_token=c["access_token"]) + eas_client = EasClient( + host=c["host"], + port=c["rpc_port"], + protocol="https", + access_token=c["access_token"], + asynchronous=True + ) print("Connection established..") print("Uploading study...") - response = await eas_client.async_upload_study(study) + response = await eas_client.mutation(Mutation.add_studies([study])) print(response) print("Study uploaded! Please check the Evolve Web App.") - await eas_client.aclose() + await eas_client.close() if __name__ == "__main__": diff --git a/src/zepben/examples/studies/suspect_end_of_line.py b/src/zepben/examples/studies/suspect_end_of_line.py index abcaf6f..d96b423 100644 --- a/src/zepben/examples/studies/suspect_end_of_line.py +++ b/src/zepben/examples/studies/suspect_end_of_line.py @@ -12,8 +12,7 @@ from geojson import FeatureCollection, Feature from geojson.geometry import Geometry, LineString, Point -from zepben.eas.client.eas_client import EasClient -from zepben.eas.client.study import Study, Result, GeoJsonOverlay +from zepben.eas import EasClient, StudyInput, StudyResultInput, GeoJsonOverlayInput, Mutation from zepben.ewb import PowerTransformer, ConductingEquipment, EnergyConsumer, AcLineSegment, \ NetworkConsumerClient, PhaseCode, PowerElectronicsConnection, Feeder, PowerSystemResource, Location, \ connect_with_token, NetworkTraceStep, Tracing, downstream, upstream, IncludedEnergizedContainers @@ -80,7 +79,7 @@ async def main(): tags=["suspect_end_of_line", "-".join(zone_mrids)], styles=json.load(open("style_eol.json", "r")) ) - await eas_client.aclose() + await eas_client.close() print(f"Uploaded Study") print(f"Finish time: {datetime.now()}") @@ -179,23 +178,27 @@ async def upload_suspect_end_of_line_study( AcLineSegment: {"name": lambda ec: ec.name}, } feature_collection = to_geojson_feature_collection(pts, class_to_properties) - response = await eas_client.async_upload_study( - Study( - name=name, - description=description, - tags=tags, - results=[ - Result( - name=name, - geo_json_overlay=GeoJsonOverlay( - data=feature_collection, - styles=[s['id'] for s in styles] + + response = await eas_client.mutation(Mutation.add_studies( + [ + StudyInput( + name=name, + description=description, + tags=tags, + results=[ + StudyResultInput( + name=name, + geoJsonOverlay=GeoJsonOverlayInput( + data=feature_collection, + styles=[s['id'] for s in styles] + ) ) - ) - ], - styles=styles - ) - ) + ], + styles=styles + ) + ] + )) + print(f"Study response: {response}") diff --git a/src/zepben/examples/utils.py b/src/zepben/examples/utils.py index fefec1e..d5e0e5e 100644 --- a/src/zepben/examples/utils.py +++ b/src/zepben/examples/utils.py @@ -6,7 +6,7 @@ import json from typing import Dict -from zepben.eas.client.eas_client import EasClient +from zepben.eas import EasClient import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s') @@ -25,7 +25,7 @@ def read_json_config(config_file_path: str) -> Dict: return config_dict -def get_client(config_dir): +def get_client(config_dir, async_=True): # Change sample_auth_config.json to any other file name auth_config = read_json_config(f"{config_dir}/sample_auth_config.json") @@ -35,5 +35,6 @@ def get_client(config_dir): protocol=auth_config["eas_server"]["protocol"], access_token=auth_config["eas_server"]["access_token"], verify_certificate=auth_config["eas_server"].get("verify_certificate", True), - ca_filename=auth_config["eas_server"].get("ca_filename") + ca_filename=auth_config["eas_server"].get("ca_filename"), + asynchronous=async_ )