diff --git a/changelog.md b/changelog.md index 5c9eb5f..3cc85b8 100644 --- a/changelog.md +++ b/changelog.md @@ -2,12 +2,16 @@ ## [0.27.0] - UNRELEASED ### Breaking Changes * Bumping `urllib3` to `v2.5.0`, and pulling in `zepben.auth` via the SDK. +* EAS must support unspecified `allocationLimitPerYear` and `yearRange` in the intervention config. ### New Features * None. ### Enhancements -* None. +* To reduce confusion when running certain classes of intervention, the following fields are no longe required in `InterventionConfig`, + and are defaulted to sensible values server-side: + * `yearRange` + * `allocation_limit_per_year` ### Fixes * None. diff --git a/src/zepben/eas/client/eas_client.py b/src/zepben/eas/client/eas_client.py index 1cb5034..ec9f292 100644 --- a/src/zepben/eas/client/eas_client.py +++ b/src/zepben/eas/client/eas_client.py @@ -282,7 +282,7 @@ def generator_config_to_json(self, generator_config: Optional[GeneratorConfig]) } } - def work_package_config_to_json(self, work_package: Optional[WorkPackageConfig]) -> Optional[dict]: + def work_package_config_to_json(self, work_package: WorkPackageConfig) -> dict: return { "feederConfigs": { "configs": [ @@ -379,44 +379,54 @@ def work_package_config_to_json(self, work_package: Optional[WorkPackageConfig]) } } }, - "intervention": work_package.intervention and { - "baseWorkPackageId": work_package.intervention.base_work_package_id, - "yearRange": { - "maxYear": work_package.intervention.year_range.max_year, - "minYear": work_package.intervention.year_range.min_year - }, - "allocationLimitPerYear": work_package.intervention.allocation_limit_per_year, - "interventionType": work_package.intervention.intervention_type.name, - "candidateGeneration": work_package.intervention.candidate_generation and { - "type": work_package.intervention.candidate_generation.type.name, - "interventionCriteriaName": work_package.intervention.candidate_generation.intervention_criteria_name, - "voltageDeltaAvgThreshold": work_package.intervention.candidate_generation.voltage_delta_avg_threshold, - "voltageUnderLimitHoursThreshold": work_package.intervention.candidate_generation.voltage_under_limit_hours_threshold, - "voltageOverLimitHoursThreshold": work_package.intervention.candidate_generation.voltage_over_limit_hours_threshold, - "tapWeightingFactorLowerThreshold": work_package.intervention.candidate_generation.tap_weighting_factor_lower_threshold, - "tapWeightingFactorUpperThreshold": work_package.intervention.candidate_generation.tap_weighting_factor_upper_threshold, - }, - "allocationCriteria": work_package.intervention.allocation_criteria, - "specificAllocationInstance": work_package.intervention.specific_allocation_instance, - "phaseRebalanceProportions": work_package.intervention.phase_rebalance_proportions and { - "a": work_package.intervention.phase_rebalance_proportions.a, - "b": work_package.intervention.phase_rebalance_proportions.b, - "c": work_package.intervention.phase_rebalance_proportions.c - }, - "dvms": work_package.intervention.dvms and { - "lowerLimit": work_package.intervention.dvms.lower_limit, - "upperLimit": work_package.intervention.dvms.upper_limit, - "lowerPercentile": work_package.intervention.dvms.lower_percentile, - "upperPercentile": work_package.intervention.dvms.upper_percentile, - "maxIterations": work_package.intervention.dvms.max_iterations, - "regulatorConfig": { - "puTarget": work_package.intervention.dvms.regulator_config.pu_target, - "puDeadbandPercent": work_package.intervention.dvms.regulator_config.pu_deadband_percent, - "maxTapChangePerStep": work_package.intervention.dvms.regulator_config.max_tap_change_per_step, - "allowPushToLimit": work_package.intervention.dvms.regulator_config.allow_push_to_limit + "intervention": work_package.intervention and ( + { + "baseWorkPackageId": work_package.intervention.base_work_package_id, + "interventionType": work_package.intervention.intervention_type.name, + "candidateGeneration": work_package.intervention.candidate_generation and { + "type": work_package.intervention.candidate_generation.type.name, + "interventionCriteriaName": work_package.intervention.candidate_generation.intervention_criteria_name, + "voltageDeltaAvgThreshold": work_package.intervention.candidate_generation.voltage_delta_avg_threshold, + "voltageUnderLimitHoursThreshold": work_package.intervention.candidate_generation.voltage_under_limit_hours_threshold, + "voltageOverLimitHoursThreshold": work_package.intervention.candidate_generation.voltage_over_limit_hours_threshold, + "tapWeightingFactorLowerThreshold": work_package.intervention.candidate_generation.tap_weighting_factor_lower_threshold, + "tapWeightingFactorUpperThreshold": work_package.intervention.candidate_generation.tap_weighting_factor_upper_threshold, + }, + "allocationCriteria": work_package.intervention.allocation_criteria, + "specificAllocationInstance": work_package.intervention.specific_allocation_instance, + "phaseRebalanceProportions": work_package.intervention.phase_rebalance_proportions and { + "a": work_package.intervention.phase_rebalance_proportions.a, + "b": work_package.intervention.phase_rebalance_proportions.b, + "c": work_package.intervention.phase_rebalance_proportions.c + }, + "dvms": work_package.intervention.dvms and { + "lowerLimit": work_package.intervention.dvms.lower_limit, + "upperLimit": work_package.intervention.dvms.upper_limit, + "lowerPercentile": work_package.intervention.dvms.lower_percentile, + "upperPercentile": work_package.intervention.dvms.upper_percentile, + "maxIterations": work_package.intervention.dvms.max_iterations, + "regulatorConfig": { + "puTarget": work_package.intervention.dvms.regulator_config.pu_target, + "puDeadbandPercent": work_package.intervention.dvms.regulator_config.pu_deadband_percent, + "maxTapChangePerStep": work_package.intervention.dvms.regulator_config.max_tap_change_per_step, + "allowPushToLimit": work_package.intervention.dvms.regulator_config.allow_push_to_limit + } } - } - } + } | + ( + { "allocationLimitPerYear": work_package.intervention.allocation_limit_per_year } + if work_package.intervention.allocation_limit_per_year is not None else {} + ) | + ( + { + "yearRange": { + "maxYear": work_package.intervention.year_range.max_year, + "minYear": work_package.intervention.year_range.min_year + } + } + if work_package.intervention.year_range is not None else {} + ) + ) } def run_hosting_capacity_work_package(self, work_package: WorkPackageConfig): diff --git a/src/zepben/eas/client/work_package.py b/src/zepben/eas/client/work_package.py index 7311326..caa60e9 100644 --- a/src/zepben/eas/client/work_package.py +++ b/src/zepben/eas/client/work_package.py @@ -855,18 +855,18 @@ class InterventionConfig: The new work package should process a subset of its feeders, scenarios, and years. """ - year_range: YearRange + intervention_type: InterventionClass + """The class of intervention to apply.""" + + year_range: Optional[YearRange] = None """ The range of years to search for and apply interventions. All years within this range should be included in the work package. """ - allocation_limit_per_year: int + allocation_limit_per_year: Optional[int] = None """The maximum number of interventions that can be applied per year.""" - intervention_type: InterventionClass - """The class of intervention to apply.""" - candidate_generation: Optional[CandidateGenerationConfig] = None """ The method of generating candidates for the intervention. diff --git a/test/test_eas_client.py b/test/test_eas_client.py index 83bc6f8..e8b68a3 100644 --- a/test/test_eas_client.py +++ b/test/test_eas_client.py @@ -17,7 +17,7 @@ from werkzeug import Response from zepben.ewb.auth import ZepbenTokenFetcher -from zepben.eas import EasClient, Study, SolveConfig +from zepben.eas import EasClient, Study, SolveConfig, InterventionConfig, YearRange from zepben.eas import FeederConfig, ForecastConfig, FixedTimeLoadOverride from zepben.eas.client.ingestor import IngestorConfigInput, IngestorRunsSortCriteriaInput, IngestorRunsFilterInput, \ IngestorRunState, IngestorRuntimeKind @@ -26,7 +26,7 @@ Order from zepben.eas.client.study import Result from zepben.eas.client.work_package import FeederConfigs, TimePeriodLoadOverride, \ - FixedTime, NodeLevelResultsConfig, PVVoltVARVoltWattConfig + FixedTime, NodeLevelResultsConfig, PVVoltVARVoltWattConfig, InterventionClass from zepben.eas.client.work_package import WorkPackageConfig, TimePeriod, GeneratorConfig, ModelConfig, \ FeederScenarioAllocationStrategy, LoadPlacement, MeterPlacementConfig, SwitchMeterPlacementConfig, SwitchClass, \ SolveMode, RawResultsConfig @@ -1682,3 +1682,65 @@ def test_get_ingestor_run_list_all_filters_no_verify_success(httpserver: HTTPSer ) httpserver.check_assertions() assert res == {"result": "success"} + + +def test_work_package_config_to_json_omits_server_defaulted_fields_if_unspecified(httpserver: HTTPServer): + eas_client = EasClient( + LOCALHOST, + httpserver.port, + verify_certificate=False + ) + + wp_config = WorkPackageConfig( + name="wp", + syf_config=FeederConfigs([]), + intervention=InterventionConfig( + base_work_package_id="abc", + intervention_type=InterventionClass.COMMUNITY_BESS + ) + ) + json_config = eas_client.work_package_config_to_json(wp_config) + + assert json_config["intervention"] == { + "baseWorkPackageId": "abc", + "interventionType": "COMMUNITY_BESS", + "candidateGeneration": None, + "allocationCriteria": None, + "specificAllocationInstance": None, + "phaseRebalanceProportions": None, + "dvms": None + } + +def test_work_package_config_to_json_includes_server_defaulted_fields_if_specified(httpserver: HTTPServer): + eas_client = EasClient( + LOCALHOST, + httpserver.port, + verify_certificate=False + ) + + wp_config = WorkPackageConfig( + name="wp", + syf_config=FeederConfigs([]), + intervention=InterventionConfig( + base_work_package_id="abc", + year_range=YearRange(2020, 2025), + intervention_type=InterventionClass.COMMUNITY_BESS, + allocation_limit_per_year=5 + ) + ) + json_config = eas_client.work_package_config_to_json(wp_config) + + assert json_config["intervention"] == { + "baseWorkPackageId": "abc", + "yearRange": { + "maxYear": 2025, + "minYear": 2020 + }, + "interventionType": "COMMUNITY_BESS", + "candidateGeneration": None, + "allocationCriteria": None, + "specificAllocationInstance": None, + "phaseRebalanceProportions": None, + "dvms": None, + "allocationLimitPerYear": 5 + }