gridstate 2.5.0__tar.gz → 2.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gridstate-2.5.0 → gridstate-2.7.0}/CHANGELOG.md +36 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/PKG-INFO +1 -1
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/_version.py +3 -3
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algorithms/ipm_pd.py +13 -1
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/api.py +5 -6
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/ipm_hints.py +1 -1
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/config.py +32 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/passes.py +3 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/preprocessing/ipm_setup.py +55 -10
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/PKG-INFO +1 -1
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/scm_version.json +2 -2
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ipm_balance_modes.py +67 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/.github/workflows/ci.yml +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/.github/workflows/release.yml +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/.gitignore +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/.pre-commit-config.yaml +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/LICENSE +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/Makefile +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/README.md +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/build_cython.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/__pyinstaller/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/__pyinstaller/hook-gridstate.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/_arrays.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/adapters/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/adapters/from_pandapower.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algebra/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algebra/_selinv_kernels.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algebra/base.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algebra/estimators.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algebra/jacobian_pattern.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algebra/normalized_residuals.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algorithms/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algorithms/_gram_numba.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algorithms/ipm.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algorithms/kkt_solver.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algorithms/status.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/algorithms/wls.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/bad_data_repass.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/bounds.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/constants.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/_legacy.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/containers.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/derived.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/derived_tables.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/serialize.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/tables.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/validate.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/contract/version.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/load_characteristics.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/losses.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/measurement_ids.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/context.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/manifest.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/orchestration.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/preparation.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/runner.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/pipeline/steps.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/post_processing.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/preprocessing/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/preprocessing/_scan.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/preprocessing/meas_rows.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/preprocessing/mirror_voltage.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/preprocessing/node_props.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/preprocessing/pseudo_measurements.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/py.typed +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/quality_summary.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/resolve_pass.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/result.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/runtime.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/shunt_sanity.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/solution_hints.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/state.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/_filters.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/apply_resolved.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/generators.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/kinds.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/load_only_injection.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/loss_filter.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/measurements.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/noise.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/on_line.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/quality.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/rpn.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/shunts.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/sigma_floor.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/units.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/voltage_filter.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/telemetry/voltage_nominal.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/topology.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/units.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/utils.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/v_mirror.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/v_refine.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/validation/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/validation/_diagnostics.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/validation/bad_data.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/validation/chi2_test.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/validation/observability.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/working.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/ybus.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate/z_vector.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/SOURCES.txt +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/dependency_links.txt +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/entry_points.txt +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/requires.txt +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/scm_file_list.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/gridstate.egg-info/top_level.txt +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/pyproject.toml +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/setup.cfg +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/__init__.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/_golden.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/conftest.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_adapters_lazy_import.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_aggregate_generators.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_algebra_base.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_anti_overshoot.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_arrays_helpers.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_bad_data.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_bad_data_repass.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_bounds.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_boxed_balance.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_breaker_reactance.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_chain_mirror.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_contract.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_contract_edge_cases.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_contract_runtime.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case118.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case118.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case118_degraded.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case118_degraded.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case14.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case14.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case14_degraded.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case14_degraded.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case30.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case30.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case30_degraded.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/case30_degraded.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/golden/manifest.json +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/ieee/case118.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/ieee/case118_truth.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/ieee/case14.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/ieee/case14_truth.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/ieee/case30.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_data/ieee/case30_truth.npz +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_derived_tables.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_enum_codes.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_flow_sigma_floor.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_from_pandapower.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_generator_shunt_kernels.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_golden.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_gram_numba.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ieee_cases.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ipm.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ipm_hints.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ipm_injection_roles.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ipm_integration.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ipm_pd.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_jacobian_pattern.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_kkt_solver.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_load_characteristic_segments.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_load_only_injection.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_losses_breakdown.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_losses_q_convention.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_losses_trafo_classification.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_materialize_kernel.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_measurement_filter_kernels.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_measurement_ids.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_mirror_voltage_kernel.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_near_critical_ranking.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_neutral_wording.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_noise_model.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_normalized_residuals.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_numpy_only_runtime.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_observability.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_on_line_kernel.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_perf_synthetic_network.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pipeline.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pipeline_config_validation.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pipeline_idempotent.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pipeline_invariant_errors.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pipeline_step_order.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pipeline_warm_start.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pipeline_working_layer.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_post_processing.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_preparation_steps.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_prepare_network.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pseudo_unknown_load.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_pseudo_v_unobservable.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_quality_summary_paths.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_reconcile_balance.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_refine_merge.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_resolve_pass.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_result_quality_summary.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_rn_selected_inversion.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_serialize_npz.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_shunt_sanity.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_solution_hints.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_solve_status.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_state.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_state_range.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_telemetry_apply_kernel.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_telemetry_bounds.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_telemetry_physics_kernels.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_tm_code_quality.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_topology_kernels.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_topology_port.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_transformer_from_shunt.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_units.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_units_array_kernels.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_v_mirror.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_v_refine.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_variance_charging.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_version.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_voltage_calibration_gen_nodes.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_voltage_range_filter_fallback.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_wls.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_wls_robustness.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_working_container.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_ybus.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tests/test_z_vector.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tools/gen_ieee_fixtures.py +0 -0
- {gridstate-2.5.0 → gridstate-2.7.0}/tools/golden.py +0 -0
|
@@ -9,6 +9,42 @@ types of the merged pull requests; breaking and behavior-changing items are
|
|
|
9
9
|
listed separately under "Breaking changes" and "Behavior changes". A release is
|
|
10
10
|
cut by tagging the merged main commit; the version is derived from that tag.
|
|
11
11
|
|
|
12
|
+
## v2.7.0 (2026-10-18)
|
|
13
|
+
|
|
14
|
+
### Behavior changes
|
|
15
|
+
|
|
16
|
+
- `ipm_pd` stops the Huber reweighting once the complementarity μ is at or
|
|
17
|
+
below `huber_freeze_mu` (new solver argument, default 1e-4) and keeps the
|
|
18
|
+
last weights. Estimates move slightly (≤ 0.005 kV median |ΔV| on the test
|
|
19
|
+
models, individual nodes up to ~2e-3 p.u.); iteration counts drop.
|
|
20
|
+
|
|
21
|
+
### Perf
|
|
22
|
+
|
|
23
|
+
- `ipm_pd` end game with Huber: rows whose residual sits at the threshold
|
|
24
|
+
flipped their weight every iteration, the merit search halved every other
|
|
25
|
+
step and the solve zigzagged at a linear rate. Equality rows (the hard
|
|
26
|
+
transit balance) made it markedly worse: on the test models the hard mode
|
|
27
|
+
needed up to 109 iterations per solve where the soft one needs 18. With the
|
|
28
|
+
weights frozen: 28; the hard mode now runs within ~10–15 % of the soft one.
|
|
29
|
+
|
|
30
|
+
## v2.6.0 (2026-10-18)
|
|
31
|
+
|
|
32
|
+
### Behavior changes
|
|
33
|
+
|
|
34
|
+
- `ipm_pd` gets the 300-iteration budget with equality rows too (it was 100
|
|
35
|
+
before the primal fallback), including the global `ipm_balance_mode="hard"`:
|
|
36
|
+
with the adaptive box damping and the measurement-space stopping test of
|
|
37
|
+
v2.4.1 the long budget converges where the short one fell back.
|
|
38
|
+
|
|
39
|
+
### Feat
|
|
40
|
+
|
|
41
|
+
- `ipm_transit_balance_mode` (new, default `soft`): `hard` makes the P and Q
|
|
42
|
+
balance rows of transit nodes (no `exist_gen` / `exist_load`) equality
|
|
43
|
+
constraints of the solver, to `ipm_transit_balance_tol_p_mw` (new, default
|
|
44
|
+
0.01) / `ipm_transit_balance_tol_q_mvar` (new, default 0.1); the other
|
|
45
|
+
balance rows stay soft. The global hard balance overrides it. `IPMSetup`
|
|
46
|
+
gains `eq_rows` / `eq_tol`.
|
|
47
|
+
|
|
12
48
|
## v2.5.0 (2026-10-18)
|
|
13
49
|
|
|
14
50
|
### Behavior changes
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.
|
|
22
|
-
__version_tuple__ = version_tuple = (2,
|
|
21
|
+
__version__ = version = '2.7.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 7, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g9810ca9e9'
|
|
@@ -244,6 +244,7 @@ def solve_ipm_pd(
|
|
|
244
244
|
huber_use_mad: bool = False,
|
|
245
245
|
huber_adaptive_k: float = 6.0,
|
|
246
246
|
huber_warmup_iters: int = 5,
|
|
247
|
+
huber_freeze_mu: float = 1e-4,
|
|
247
248
|
kkt_solver: KKTSolver | None = None,
|
|
248
249
|
eq_prox: float = 1e-9,
|
|
249
250
|
box_prox: float = 1e-6,
|
|
@@ -286,6 +287,15 @@ def solve_ipm_pd(
|
|
|
286
287
|
huber_*: SHGM-IRLS reweighting of the rows of ``huber_mask``, as in
|
|
287
288
|
``solve_ipm``; the adaptive scale is recomputed every iteration
|
|
288
289
|
(μ changes every iteration).
|
|
290
|
+
huber_freeze_mu: the reweighting stops once the complementarity μ is
|
|
291
|
+
at or below it and the last weights are kept. Rows whose residual
|
|
292
|
+
sits at the Huber threshold flip their weight from one iteration to
|
|
293
|
+
the next, so the objective the step is computed for changes every
|
|
294
|
+
iteration and the merit search halves every other step; with the
|
|
295
|
+
weights frozen the end game is a plain weighted least squares and
|
|
296
|
+
converges at the Newton rate. Equality rows push the data
|
|
297
|
+
conflicts into the residuals and make this markedly worse. ``0``
|
|
298
|
+
reweights to the end.
|
|
289
299
|
kkt_solver: Newton-system solver (``None`` — scipy).
|
|
290
300
|
eq_prox: with equality rows, a proximal term ``eq_prox·max(ρ)`` on the
|
|
291
301
|
diagonal of the box coordinates. A hard balance fixes only the net
|
|
@@ -414,8 +424,9 @@ def solve_ipm_pd(
|
|
|
414
424
|
status = "stalled"
|
|
415
425
|
message = ""
|
|
416
426
|
it = 0
|
|
427
|
+
mu_last = float("inf")
|
|
417
428
|
for it in range(max_iter):
|
|
418
|
-
if use_huber and it >= huber_warmup_iters:
|
|
429
|
+
if use_huber and it >= huber_warmup_iters and mu_last > huber_freeze_mu:
|
|
419
430
|
reweighter.reset_adaptive()
|
|
420
431
|
w = w_base * reweighter.weights(r)
|
|
421
432
|
w_data = np.where(is_eq, 0.0, w)
|
|
@@ -423,6 +434,7 @@ def solve_ipm_pd(
|
|
|
423
434
|
r_l = g[il] - lo[il] - s_l
|
|
424
435
|
r_u = hi[iu] - g[iu] - s_u
|
|
425
436
|
mu = (float(s_l @ y_l) + float(s_u @ y_u)) / n_pairs if n_pairs else 0.0
|
|
437
|
+
mu_last = mu
|
|
426
438
|
eq_inf = float(np.max(np.abs(c))) if c.size else 0.0
|
|
427
439
|
feas_inf = float(np.max(np.abs(np.concatenate([r_l, r_u])))) if n_pairs else 0.0
|
|
428
440
|
if (
|
|
@@ -826,7 +826,7 @@ def _run_ipm(
|
|
|
826
826
|
delta, v, _pg, _qg, _pn, _qn = unpack_full(x, layout_ipm)
|
|
827
827
|
return algebra.evaluate_jacobian(v, delta)
|
|
828
828
|
|
|
829
|
-
eq_idx = setup.
|
|
829
|
+
eq_idx = setup.eq_rows
|
|
830
830
|
pd_result: IPMResult | None = None
|
|
831
831
|
if solver == "ipm_pd":
|
|
832
832
|
mb_rows = mb_lo = mb_hi = None
|
|
@@ -852,13 +852,12 @@ def _run_ipm(
|
|
|
852
852
|
meas_box_lo=mb_lo,
|
|
853
853
|
meas_box_hi=mb_hi,
|
|
854
854
|
eq_idx=eq_idx,
|
|
855
|
-
eq_tol=setup.
|
|
855
|
+
eq_tol=setup.eq_tol,
|
|
856
856
|
mu_init=1.0,
|
|
857
857
|
step_tol=max(tolerance, 1e-4),
|
|
858
858
|
# Weakly determined units need up to ~260 iterations with separate
|
|
859
|
-
# injection parts
|
|
860
|
-
|
|
861
|
-
max_iter=max(max_iterations, 100 if eq_idx is not None else 300),
|
|
859
|
+
# injection parts, with or without equality rows.
|
|
860
|
+
max_iter=max(max_iterations, 300),
|
|
862
861
|
tr_radius=0.5,
|
|
863
862
|
huber_c=huber_c,
|
|
864
863
|
huber_mask=huber_mask,
|
|
@@ -910,7 +909,7 @@ def _run_ipm(
|
|
|
910
909
|
huber_adaptive_k=2.0,
|
|
911
910
|
kkt_solver=kkt,
|
|
912
911
|
eq_idx=eq_idx,
|
|
913
|
-
eq_tol=setup.
|
|
912
|
+
eq_tol=setup.eq_tol,
|
|
914
913
|
)
|
|
915
914
|
if solver == "ipm_pd":
|
|
916
915
|
result.message = f"ipm_pd fallback to ipm ({pd_message}): {result.message}"
|
|
@@ -219,7 +219,7 @@ def ipm_convergence_hints(
|
|
|
219
219
|
meas_index=setup.meas_index,
|
|
220
220
|
network_pu=network_pu,
|
|
221
221
|
# Equality rows: their σ is a penalty, not a measurement error.
|
|
222
|
-
skip=setup.
|
|
222
|
+
skip=setup.eq_rows,
|
|
223
223
|
part_rows=setup.role_rows,
|
|
224
224
|
top=top,
|
|
225
225
|
)
|
|
@@ -610,6 +610,38 @@ class PipelineConfig:
|
|
|
610
610
|
"median(σ²_data) / factor. Their injection is known to be zero; a loose balance "
|
|
611
611
|
"turns data conflicts into a fictitious injection. 0 — off.",
|
|
612
612
|
)
|
|
613
|
+
ipm_transit_balance_mode: str = _param(
|
|
614
|
+
"soft",
|
|
615
|
+
group=_G_IPM,
|
|
616
|
+
label="Balance mode of transit nodes",
|
|
617
|
+
control="select",
|
|
618
|
+
choices=["soft", "hard"],
|
|
619
|
+
depends={"algorithm": "ipm"},
|
|
620
|
+
help="soft — the balance of a transit node (no exist_gen/exist_load) is a "
|
|
621
|
+
"weighted row like the others (ipm_transit_balance_weight_factor); hard — an "
|
|
622
|
+
"equality constraint to ipm_transit_balance_tol_p_mw / _q_mvar, the other "
|
|
623
|
+
"balance rows stay soft. No effect when ipm_balance_mode=hard.",
|
|
624
|
+
)
|
|
625
|
+
ipm_transit_balance_tol_p_mw: float = _param(
|
|
626
|
+
0.01,
|
|
627
|
+
group=_G_IPM,
|
|
628
|
+
label="Transit balance tolerance P, MW",
|
|
629
|
+
control="number",
|
|
630
|
+
min=1e-6,
|
|
631
|
+
max=1e3,
|
|
632
|
+
depends={"ipm_transit_balance_mode": "hard"},
|
|
633
|
+
help="Admissible |P injection| of a transit node under the hard transit balance.",
|
|
634
|
+
)
|
|
635
|
+
ipm_transit_balance_tol_q_mvar: float = _param(
|
|
636
|
+
0.1,
|
|
637
|
+
group=_G_IPM,
|
|
638
|
+
label="Transit balance tolerance Q, MVAr",
|
|
639
|
+
control="number",
|
|
640
|
+
min=1e-6,
|
|
641
|
+
max=1e3,
|
|
642
|
+
depends={"ipm_transit_balance_mode": "hard"},
|
|
643
|
+
help="Admissible |Q injection| of a transit node under the hard transit balance.",
|
|
644
|
+
)
|
|
613
645
|
ipm_prior_sigma2_bus_equiv_pu: float = _param(
|
|
614
646
|
0.01,
|
|
615
647
|
group=_G_IPM,
|
|
@@ -52,6 +52,9 @@ def _ipm_kwargs(cfg: PipelineConfig) -> dict:
|
|
|
52
52
|
"prior_sigma2_bus_equiv_pu": cfg.ipm_prior_sigma2_bus_equiv_pu,
|
|
53
53
|
"boxed_balance_weight_factor": cfg.ipm_boxed_balance_weight_factor,
|
|
54
54
|
"transit_balance_weight_factor": cfg.ipm_transit_balance_weight_factor,
|
|
55
|
+
"transit_balance_mode": cfg.ipm_transit_balance_mode,
|
|
56
|
+
"transit_balance_tol_p_mw": cfg.ipm_transit_balance_tol_p_mw,
|
|
57
|
+
"transit_balance_tol_q_mvar": cfg.ipm_transit_balance_tol_q_mvar,
|
|
55
58
|
"trust_region_scope": cfg.ipm_trust_region_scope,
|
|
56
59
|
"balance_mode": cfg.ipm_balance_mode,
|
|
57
60
|
"balance_tol_p_mw": cfg.ipm_balance_tol_p_mw,
|
|
@@ -282,6 +282,11 @@ class IPMSetup:
|
|
|
282
282
|
# ``balance_tol`` holds the admissible |imbalance| per balance row in p.u.
|
|
283
283
|
# ``None`` in the soft mode.
|
|
284
284
|
balance_tol: np.ndarray | None = None
|
|
285
|
+
# Rows the solver enforces as equalities and their admissible |c| (p.u.):
|
|
286
|
+
# every balance row in the hard mode, the transit balance rows with
|
|
287
|
+
# ``transit_balance_mode="hard"``; ``None`` — no equality rows.
|
|
288
|
+
eq_rows: np.ndarray | None = None
|
|
289
|
+
eq_tol: np.ndarray | None = None
|
|
285
290
|
# ``injection_roles``: rows of ``z`` that measure a box variable directly
|
|
286
291
|
# (a generation / load part of a node injection) and their measurement ids;
|
|
287
292
|
# ``role_dropped_ids`` — part rows folded back into a net injection row.
|
|
@@ -556,6 +561,9 @@ def build_ipm_setup(
|
|
|
556
561
|
hard_balance_rho_factor: float = 1e3,
|
|
557
562
|
balance_tol_p_mw: float = 1.0,
|
|
558
563
|
balance_tol_q_mvar: float = 10.0,
|
|
564
|
+
transit_balance_mode: str = "soft",
|
|
565
|
+
transit_balance_tol_p_mw: float = 0.01,
|
|
566
|
+
transit_balance_tol_q_mvar: float = 0.1,
|
|
559
567
|
fixed_injection_from_input: bool = False,
|
|
560
568
|
injection_roles: bool = False,
|
|
561
569
|
measurements: _ArrayCollection | None = None,
|
|
@@ -628,6 +636,14 @@ def build_ipm_setup(
|
|
|
628
636
|
hard_balance_rho_factor: see ``balance_mode``.
|
|
629
637
|
balance_tol_p_mw, balance_tol_q_mvar: admissible |imbalance| of a hard
|
|
630
638
|
P / Q balance row (MW / MVAr).
|
|
639
|
+
transit_balance_mode: ``"soft"`` (default) — the balance rows of transit
|
|
640
|
+
nodes (no ``exist_gen`` / ``exist_load``) are weighted like the
|
|
641
|
+
others (see ``transit_balance_weight_factor``); ``"hard"`` — they are
|
|
642
|
+
equality constraints with the penalty of ``balance_mode="hard"``,
|
|
643
|
+
the other balance rows stay soft. Ignored when ``balance_mode`` is
|
|
644
|
+
already ``"hard"``.
|
|
645
|
+
transit_balance_tol_p_mw, transit_balance_tol_q_mvar: admissible
|
|
646
|
+
|injection| of a transit node in the hard transit mode (MW / MVAr).
|
|
631
647
|
fixed_injection_from_input: the balance row of a node carries the
|
|
632
648
|
input injection of its parts without a box variable (no
|
|
633
649
|
``exist_gen`` / ``exist_load``) as a fixed term instead of zero.
|
|
@@ -732,6 +748,11 @@ def build_ipm_setup(
|
|
|
732
748
|
if balance_mode not in ("soft", "hard"):
|
|
733
749
|
raise ValueError(f"balance_mode={balance_mode!r} is not one of ['soft', 'hard']")
|
|
734
750
|
hard = balance_mode == "hard"
|
|
751
|
+
if transit_balance_mode not in ("soft", "hard"):
|
|
752
|
+
raise ValueError(
|
|
753
|
+
f"transit_balance_mode={transit_balance_mode!r} is not one of ['soft', 'hard']"
|
|
754
|
+
)
|
|
755
|
+
transit_hard = transit_balance_mode == "hard" and not hard and n_balance > 0
|
|
735
756
|
|
|
736
757
|
# ---- Prior measurements: z=0, individual σ². Only for box-vars with σ²>0 ----
|
|
737
758
|
pgen_prior_arr = np.asarray(pgen_sec.prior_s2, dtype=np.float64)
|
|
@@ -816,6 +837,12 @@ def build_ipm_setup(
|
|
|
816
837
|
for start in (n_old, n_old + n_balance):
|
|
817
838
|
rows = sigma2_new[start : start + n_balance]
|
|
818
839
|
rows[transit] = np.minimum(rows[transit], transit_s2)
|
|
840
|
+
transit_eq = np.zeros(0, dtype=np.int64)
|
|
841
|
+
if transit_hard:
|
|
842
|
+
transit = _transit_mask(nodes_arr, active_ids)
|
|
843
|
+
pos = np.flatnonzero(transit)
|
|
844
|
+
transit_eq = np.concatenate([n_old + pos, n_old + n_balance + pos]).astype(np.int64)
|
|
845
|
+
sigma2_new[transit_eq] = _adaptive_balance_sigma2(sigma2_old, hard_balance_rho_factor)
|
|
819
846
|
if n_prior > 0:
|
|
820
847
|
prior_sigmas = np.concatenate(
|
|
821
848
|
[
|
|
@@ -893,6 +920,31 @@ def build_ipm_setup(
|
|
|
893
920
|
meas_id=new_meas_id,
|
|
894
921
|
)
|
|
895
922
|
|
|
923
|
+
balance_tol = (
|
|
924
|
+
np.concatenate(
|
|
925
|
+
[
|
|
926
|
+
np.full(n_balance, balance_tol_p_mw / BASE_MVA),
|
|
927
|
+
np.full(n_balance, balance_tol_q_mvar / BASE_MVA),
|
|
928
|
+
]
|
|
929
|
+
)
|
|
930
|
+
if hard
|
|
931
|
+
else None
|
|
932
|
+
)
|
|
933
|
+
eq_rows: np.ndarray | None = None
|
|
934
|
+
eq_tol: np.ndarray | None = None
|
|
935
|
+
if hard:
|
|
936
|
+
eq_rows = np.arange(n_old, n_old + 2 * n_balance, dtype=np.int64)
|
|
937
|
+
eq_tol = balance_tol
|
|
938
|
+
elif transit_eq.size:
|
|
939
|
+
n_t = transit_eq.size // 2
|
|
940
|
+
eq_rows = transit_eq
|
|
941
|
+
eq_tol = np.concatenate(
|
|
942
|
+
[
|
|
943
|
+
np.full(n_t, transit_balance_tol_p_mw / BASE_MVA),
|
|
944
|
+
np.full(n_t, transit_balance_tol_q_mvar / BASE_MVA),
|
|
945
|
+
]
|
|
946
|
+
)
|
|
947
|
+
|
|
896
948
|
return IPMSetup(
|
|
897
949
|
layout=layout,
|
|
898
950
|
box_idx_in_state=box_idx_arr,
|
|
@@ -907,16 +959,9 @@ def build_ipm_setup(
|
|
|
907
959
|
meas_index=meas_index_aug,
|
|
908
960
|
balance_start=n_old,
|
|
909
961
|
n_balance=n_balance,
|
|
910
|
-
balance_tol=
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
np.full(n_balance, balance_tol_p_mw / BASE_MVA),
|
|
914
|
-
np.full(n_balance, balance_tol_q_mvar / BASE_MVA),
|
|
915
|
-
]
|
|
916
|
-
)
|
|
917
|
-
if hard
|
|
918
|
-
else None
|
|
919
|
-
),
|
|
962
|
+
balance_tol=balance_tol,
|
|
963
|
+
eq_rows=eq_rows,
|
|
964
|
+
eq_tol=eq_tol,
|
|
920
965
|
role_rows=np.arange(role_start, n_new, dtype=np.int64),
|
|
921
966
|
role_meas_ids=role_ids.astype(np.int64),
|
|
922
967
|
role_dropped_ids=np.asarray(
|
|
@@ -75,6 +75,52 @@ def test_unknown_balance_mode_is_rejected():
|
|
|
75
75
|
_setup(_build_net_with_transit(), balance_mode="strict")
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
def test_transit_hard_mode_makes_only_transit_rows_equalities():
|
|
79
|
+
# Node order 1..4: node 2 is the transit node.
|
|
80
|
+
setup = _setup(
|
|
81
|
+
_build_net_with_transit(),
|
|
82
|
+
transit_balance_mode="hard",
|
|
83
|
+
transit_balance_tol_p_mw=0.02,
|
|
84
|
+
transit_balance_tol_q_mvar=0.2,
|
|
85
|
+
hard_balance_rho_factor=1e3,
|
|
86
|
+
)
|
|
87
|
+
assert setup.balance_tol is None
|
|
88
|
+
assert setup.eq_rows is not None and setup.eq_tol is not None
|
|
89
|
+
np.testing.assert_array_equal(setup.eq_rows, [2 + 1, 2 + N_BALANCE + 1])
|
|
90
|
+
np.testing.assert_allclose(setup.eq_tol, [0.02 / BASE_MVA, 0.2 / BASE_MVA])
|
|
91
|
+
sigma2 = setup.r_matrix.diagonal()[2 : 2 + 2 * N_BALANCE]
|
|
92
|
+
soft = _setup(_build_net_with_transit()).r_matrix.diagonal()[2 : 2 + 2 * N_BALANCE]
|
|
93
|
+
is_eq = np.zeros(2 * N_BALANCE, dtype=bool)
|
|
94
|
+
is_eq[[1, N_BALANCE + 1]] = True
|
|
95
|
+
assert np.allclose(sigma2[is_eq], 2.5e-4 / 1e3)
|
|
96
|
+
np.testing.assert_array_equal(sigma2[~is_eq], soft[~is_eq])
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_soft_modes_have_no_equality_rows():
|
|
100
|
+
setup = _setup(_build_net_with_transit(), transit_balance_weight_factor=10.0)
|
|
101
|
+
assert setup.eq_rows is None and setup.eq_tol is None
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_global_hard_mode_overrides_the_transit_mode():
|
|
105
|
+
setup = _setup(
|
|
106
|
+
_build_net_with_transit(),
|
|
107
|
+
balance_mode="hard",
|
|
108
|
+
balance_tol_p_mw=2.0,
|
|
109
|
+
transit_balance_mode="hard",
|
|
110
|
+
transit_balance_tol_p_mw=0.02,
|
|
111
|
+
)
|
|
112
|
+
assert setup.eq_rows is not None and setup.eq_tol is not None
|
|
113
|
+
np.testing.assert_array_equal(setup.eq_rows, setup.balance_rows)
|
|
114
|
+
np.testing.assert_array_equal(setup.eq_tol, setup.balance_tol)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_unknown_transit_balance_mode_is_rejected():
|
|
118
|
+
with pytest.raises(ValueError, match="transit_balance_mode"):
|
|
119
|
+
_setup(_build_net_with_transit(), transit_balance_mode="strict")
|
|
120
|
+
with pytest.raises(ValueError, match="ipm_transit_balance_mode"):
|
|
121
|
+
PipelineConfig(algorithm="ipm_pd", ipm_transit_balance_mode="strict")
|
|
122
|
+
|
|
123
|
+
|
|
78
124
|
def test_fixed_injection_from_input():
|
|
79
125
|
m = _build_net_with_transit()
|
|
80
126
|
# Transit node 2 gets a non-estimated load and generation.
|
|
@@ -131,6 +177,26 @@ def test_hard_balance_closes_the_estimated_imbalance(algorithm: str):
|
|
|
131
177
|
assert imb_p <= 0.1 and imb_q <= 0.1
|
|
132
178
|
|
|
133
179
|
|
|
180
|
+
@pytest.mark.parametrize("algorithm", ["ipm", "ipm_pd"])
|
|
181
|
+
def test_transit_hard_balance_zeroes_the_transit_injection(algorithm: str):
|
|
182
|
+
# Soft, the transit node 2 absorbs the data conflict (≈ 4 MW / 14 MVAr).
|
|
183
|
+
soft = _build_net_with_transit()
|
|
184
|
+
estimate(soft, algorithm=algorithm, reconcile_balance=False, include_quality_summary=False)
|
|
185
|
+
n_soft = soft.nodes.to_numpy()
|
|
186
|
+
assert abs(n_soft["p_inj_calc"][1]) > 1.0 and abs(n_soft["q_inj_calc"][1]) > 1.0
|
|
187
|
+
m = _build_net_with_transit()
|
|
188
|
+
res = estimate(
|
|
189
|
+
m,
|
|
190
|
+
algorithm=algorithm,
|
|
191
|
+
reconcile_balance=False,
|
|
192
|
+
include_quality_summary=False,
|
|
193
|
+
transit_balance_mode="hard",
|
|
194
|
+
)
|
|
195
|
+
assert res.success
|
|
196
|
+
n = m.nodes.to_numpy()
|
|
197
|
+
assert abs(n["p_inj_calc"][1]) <= 0.01 and abs(n["q_inj_calc"][1]) <= 0.1
|
|
198
|
+
|
|
199
|
+
|
|
134
200
|
def test_ipm_pd_close_to_ipm():
|
|
135
201
|
results = {}
|
|
136
202
|
for algorithm in ("ipm", "ipm_pd"):
|
|
@@ -170,6 +236,7 @@ def test_measurement_bounds_require_ipm_pd():
|
|
|
170
236
|
def test_config_defaults_and_ranges():
|
|
171
237
|
cfg = PipelineConfig(algorithm="ipm_pd")
|
|
172
238
|
assert cfg.ipm_balance_mode == "soft"
|
|
239
|
+
assert cfg.ipm_transit_balance_mode == "soft"
|
|
173
240
|
assert not cfg.ipm_measurement_bounds and not cfg.ipm_fixed_injection_from_input
|
|
174
241
|
# The soft balance weight may now exceed the median measurement weight tenfold.
|
|
175
242
|
dataclasses.replace(cfg, ipm_balance_weight_factor=1e3)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|