Skip to content

Network Validation ​

This guide covers PyPtP's validation system for checking network data quality. The validators detect issues such as branches that refer to missing nodes, nominal voltages that do not match, branch corners that miss their nodes, and element data a network file cannot hold.

Full Example

View the complete code: 04_validation.py

Running Validation ​

Basic Usage ​

python
from pyptp import NetworkMV, configure_logging
from pyptp.validator import CheckRunner, Severity

configure_logging(level="INFO")

# Load network
network = NetworkMV.from_file("network.vnf")

# Run all validators
runner = CheckRunner(network)
report = runner.run()

The CheckRunner executes all registered validation checks and returns a Report containing any issues found.

Quick Summary ​

python
from pyptp.ptp_log import logger

logger.info("Validation: %s", report.summary())

The summary() method returns a concise string like "Found 15 issues: 3 error, 12 warning", or "No issues found".

Understanding Severity Levels ​

python
# Count issues by severity
error_count = sum(1 for issue in report.issues if issue.severity == Severity.ERROR)
warning_count = sum(1 for issue in report.issues if issue.severity == Severity.WARNING)

logger.info("  Errors: %s", error_count)
logger.info("  Warnings: %s", warning_count)

Severity Definitions ​

LevelMeaningAction Required
ERRORNetwork will not function correctlyMust fix before use
WARNINGPotential issue or suboptimal configurationReview and fix if relevant

Inspecting Issues ​

Iterate Critical Issues ​

python
for issue in report.issues:
    if issue.severity == Severity.ERROR:
        logger.error("ERROR: %s", issue.message)
        logger.error("  Element: %s %s", issue.object_type, issue.object_id)

Issue Properties ​

PropertyDescription
severitySeverity.ERROR or Severity.WARNING
codeMachine-readable issue identifier
messageHuman-readable description
object_typeElement type, for example "Node" or "Cable"
object_idGUID of the affected element
validatorName of the validator that raised the issue
detailsExtra data for the issue, or None

Filter by Issue Code ​

python
# Find all coordinate mismatch issues
coord_issues = [
    issue for issue in report.issues
    if issue.code == "corner_coordinate_mismatch"
]

Topology and Drawing Checks ​

These validators check how elements refer to each other and how branches are drawn:

ValidatorNetworkCodeSeverityReports
cable_node_referenceLV, MVmissing_node_referenceERRORA cable whose node1 or node2 is not a node in the network
link_node_referenceLV, MVmissing_node_referenceERRORA link whose node1 or node2 is not a node in the network
transformer_node_referenceLV, MVmissing_node_referenceERRORA transformer whose node1 or node2 is not a node in the network
node_unomLV, MVinvalid_node_unomERRORA node with a nominal voltage of 0 or less
branch_unom_validatorLV, MVunequal_unomERRORA link, cable, reactance coil or line between nodes of different nominal voltage, unless every phase and auxiliary conductor switch on both sides is open
special_transformer_sortLV, MVspecial_transformer_sort_noneERRORA special transformer whose sort is still SpecialTransformerSort.NONE
branch_corner_coordinatesLV, MVcorner_coordinate_mismatchWARNINGA branch drawing whose first_corners or second_corners does not start at the position of its node on that sheet
branch_corner_coordinatesLV, MVempty_corner_arrayWARNINGA branch drawing with an empty first_corners or second_corners

See Corner Validation for how corners are matched to line symbols.

Cable, Connection and Source Checks ​

These validators check the electrical data of single elements:

ValidatorNetworkCodeSeverityReports
connection_load_phasesLVload_ignored_for_phasesWARNINGA connection whose load or generation is set in fields its phase setting ignores: p1/q1 on a three-phase connection (phases 4), or the per-phase fields (pa, qa, ... qbc) on any other. The part detail says load or generation
gm_type_referenceLVunknown_gm_typeERRORA GM on a connection that refers to a GM type number the network does not contain
source_voltageLVsource_umin_out_of_range, source_umax_out_of_rangeERRORA source whose umin is outside 0.5 to 1.5 times the nominal voltage of its node, or whose umax is below umin or above 1.5 times that voltage
cable_partLV, MVcable_part_too_shortERRORA cable part shorter than 0.5 m (LV) or 1 m (MV)
cable_partLV, MVcable_part_without_typeERRORA cable part without cable type data. An MV cable is saved without that part, an LV cable without its impedance data
secondary_sideLV, MVsecondary_side_invalidERRORA fuse, switch, measure field or fault indicator on a side its object does not have: side 0 in a node, 1 in an element, 1 or 2 in a branch, 1 to 3 in a three-winding transformer. A measure field in a transformer load may also use side 2

A source outside the source_voltage limits, or a secondary on a side secondary_side reports, does not load.

The validators in both tables are in ValidatorCategory.CORE, so CheckRunner(network).run(categories=ValidatorCategory.CORE) runs all of them without the native loader. cable_part_without_type fires for every cable that has no cable type data, for example an LV cable built without set_cable_type(), or an MV cable read from a file that has no type data for it.

Exporting Reports ​

JSON Export ​

python
from pathlib import Path

Path("validation_report.json").write_text(
    str(report.to_json()),
    encoding="utf-8"
)

The JSON format is useful for:

  • Automated CI/CD pipelines
  • Integration with issue tracking systems
  • Historical comparison of validation results

Programmatic Access ​

python
# Check if network passes validation
is_valid = error_count == 0
logger.info("Network valid: %s", is_valid)

# Use in automation
if not is_valid:
    raise ValueError(f"Network has {error_count} validation errors")

Validation in Workflows ​

Pre-Save Validation ​

python
def save_if_valid(network, path):
    """Only save network if it passes validation."""
    report = CheckRunner(network).run()
    errors = sum(1 for i in report.issues if i.severity == Severity.ERROR)

    if errors > 0:
        logger.error("Cannot save: %s errors found", errors)
        return False

    network.save(path)
    logger.info("Network saved to %s", path)
    return True

CI/CD Integration ​

python
import sys

def validate_network(vnf_path):
    """Exit with error code if validation fails."""
    network = NetworkMV.from_file(vnf_path)
    report = CheckRunner(network).run()

    errors = sum(1 for i in report.issues if i.severity == Severity.ERROR)

    if errors > 0:
        for issue in report.issues:
            if issue.severity == Severity.ERROR:
                print(f"ERROR: {issue.message}")
        sys.exit(1)

    print("Validation passed")
    sys.exit(0)

Validation by the Gaia or Vision Loader ​

The native_loader validator, included in CheckRunner(network).run() by default, saves the network and loads it with the Gaia or Vision loader from the bundled migrator library. save() and from_file() run the same check. See Native Validation for the issue fields, return codes and the native_check opt-out.

Complete Example ​

python
"""Validate network data quality."""

from pathlib import Path
import sys

from pyptp import NetworkMV, configure_logging
from pyptp.ptp_log import logger
from pyptp.validator import CheckRunner, Severity

configure_logging(level="INFO")

# Load network
network = NetworkMV.from_file("network.vnf")

# Run validation
runner = CheckRunner(network)
report = runner.run()

# Check results
logger.info("Validation: %s", report.summary())

# Count issues by severity
error_count = sum(1 for issue in report.issues if issue.severity == Severity.ERROR)
warning_count = sum(1 for issue in report.issues if issue.severity == Severity.WARNING)
logger.info("  Errors: %s", error_count)
logger.info("  Warnings: %s", warning_count)

# Show critical issues
for issue in report.issues:
    if issue.severity == Severity.ERROR:
        logger.error("ERROR: %s", issue.message)
        logger.error("  Element: %s %s", issue.object_type, issue.object_id)

# Find all coordinate mismatch issues
coord_issues = [
    issue for issue in report.issues
    if issue.code == "corner_coordinate_mismatch"
]

# Export report
Path("validation_report.json").write_text(str(report.to_json()), encoding="utf-8")

# Check if network is valid
is_valid = error_count == 0
logger.info("Network valid: %s", is_valid)

# Use in automation
if not is_valid:
    raise ValueError(f"Network has {error_count} validation errors")

# Pre-Save Validation
def save_if_valid(network, path):
    """Only save network if it passes validation."""
    report = CheckRunner(network).run()
    errors = sum(1 for i in report.issues if i.severity == Severity.ERROR)

    if errors > 0:
        logger.error("Cannot save: %s errors found", errors)
        return False

    network.save(path)
    logger.info("Network saved to %s", path)
    return True    

# CI/CD Integration

def validate_network(vnf_path):
    """Exit with error code if validation fails."""
    network = NetworkMV.from_file(vnf_path)
    report = CheckRunner(network).run()

    errors = sum(1 for i in report.issues if i.severity == Severity.ERROR)

    if errors > 0:
        for issue in report.issues:
            if issue.severity == Severity.ERROR:
                print(f"ERROR: {issue.message}")
        sys.exit(1)

    print("Validation passed")
    sys.exit(0)

Next Steps ​