Skip to content

Conversation

@sambuddhac
Copy link
Owner

This commit adds comprehensive CSV reading functionality to convert various CSV-based power system data formats into PowerSystems.jl compatible objects for use with PowerLASCOPF.

Features:

  • Automatic format detection (RTS-GMLC and csv_118 formats)
  • Configurable reader with sensible defaults for missing fields
  • Support for buses, branches, generators, and loads
  • Comprehensive documentation and examples

Files added:

  • src/io/readers/csv_reader.jl: Core CSV reading functionality
  • src/io/readers/PowerLASCOPFReaders.jl: Module definition
  • src/io/readers/README.md: Comprehensive documentation
  • examples/read_csv_example.jl: Usage examples

Supported formats:

  1. RTS-GMLC format (PowerSystems standard)

    • bus.csv, gen.csv, branch.csv
    • Example: example_cases/RTS_GMLC/
  2. csv_118 custom format

    • Buses.csv, Generators.csv, Lines.csv
    • Example: example_cases/csv_118/

The readers handle missing fields intelligently by:

  • Using configurable defaults
  • Computing values from other fields
  • Inferring values from naming conventions
  • Warning users of missing critical data

Future work:

  • MATPOWER format reader
  • PSSE format reader
  • JSON format reader
  • Time series data integration

Summary

This PR adds comprehensive CSV reading functionality to convert various CSV-based power system data formats into PowerSystems.jl compatible objects for use with PowerLASCOPF.

This is Part 1 of a multi-part implementation to address the full request.

Features

Supported CSV Formats

  1. RTS-GMLC format (PowerSystems standard)

    • Files: bus.csv, gen.csv, branch.csv
    • Example: example_cases/RTS_GMLC/
  2. csv_118 custom format

    • Files: Buses.csv, Generators.csv, Lines.csv
    • Example: example_cases/csv_118/

Key Capabilities

  • ✅ Automatic format detection
  • ✅ Configurable reader with sensible defaults
  • ✅ Intelligent handling of missing fields
  • ✅ Support for buses, branches, generators, and loads
  • ✅ Comprehensive documentation and examples

Files Added

  • src/io/readers/csv_reader.jl: Core CSV reading (~800 lines)
  • src/io/readers/PowerLASCOPFReaders.jl: Module definition
  • src/io/readers/README.md: Documentation with usage examples
  • examples/read_csv_example.jl: Practical examples

Usage Example

using PowerSystems
using PowerLASCOPFReaders

# Auto-detect format and read system
system = read_csv_system("example_cases/RTS_GMLC")

CompatHelper Julia and others added 30 commits February 17, 2025 00:08
…2-17-00-08-49-128-01411173499

CompatHelper: add new compat entry for PowerModels at version 0.21, (keep existing compat)
…02-17-00-08-50-572-00206183664

CompatHelper: add new compat entry for Statistics at version 1, (keep existing compat)
…02-17-00-08-52-103-03075536309

CompatHelper: add new compat entry for BenchmarkTools at version 1, (keep existing compat)
…02-17-00-08-53-518-00796215751

CompatHelper: add new compat entry for StorageSystemsSimulations at version 0.12, (keep existing compat)
sambuddhac and others added 22 commits June 2, 2025 15:27
…02-17-00-09-07-537-00951528936

CompatHelper: add new compat entry for Ipopt at version 1, (keep existing compat)
…02-17-00-09-09-031-04086465610

CompatHelper: add new compat entry for PowerGraphics at version 0.19, (keep existing compat)
…02-17-00-09-10-577-01515313495

CompatHelper: add new compat entry for DataFrames at version 1, (keep existing compat)
…02-17-00-09-12-269-02969035087

CompatHelper: add new compat entry for HydroPowerSimulations at version 0.11, (keep existing compat)
…02-17-00-09-13-829-02394655932

CompatHelper: add new compat entry for GLPK at version 1, (keep existing compat)
…02-17-00-09-15-412-02292681967

CompatHelper: add new compat entry for DataStructures at version 0.18, (keep existing compat)
…02-17-00-09-16-912-00310535970

CompatHelper: add new compat entry for PowerSystems at version 4, (keep existing compat)
…02-17-00-09-18-526-03575892309

CompatHelper: add new compat entry for PowerSimulations at version 0.30, (keep existing compat)
…02-17-00-09-20-189-00079707099

CompatHelper: add new compat entry for JuMP at version 1, (keep existing compat)
…03-27-00-08-37-804-00475756877

CompatHelper: add new compat entry for PowerGraphics at version 0.20, (keep existing compat)
Update ExtendedThermalGenerationCost.jl
This commit adds comprehensive CSV reading functionality to convert
various CSV-based power system data formats into PowerSystems.jl
compatible objects for use with PowerLASCOPF.

Features:
- Automatic format detection (RTS-GMLC and csv_118 formats)
- Configurable reader with sensible defaults for missing fields
- Support for buses, branches, generators, and loads
- Comprehensive documentation and examples

Files added:
- src/io/readers/csv_reader.jl: Core CSV reading functionality
- src/io/readers/PowerLASCOPFReaders.jl: Module definition
- src/io/readers/README.md: Comprehensive documentation
- examples/read_csv_example.jl: Usage examples

Supported formats:
1. RTS-GMLC format (PowerSystems standard)
   - bus.csv, gen.csv, branch.csv
   - Example: example_cases/RTS_GMLC/

2. csv_118 custom format
   - Buses.csv, Generators.csv, Lines.csv
   - Example: example_cases/csv_118/

The readers handle missing fields intelligently by:
- Using configurable defaults
- Computing values from other fields
- Inferring values from naming conventions
- Warning users of missing critical data

Future work:
- MATPOWER format reader
- PSSE format reader
- JSON format reader
- Time series data integration
Copilot AI review requested due to automatic review settings November 4, 2025 19:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a comprehensive CSV reader module for PowerLASCOPF that enables loading power system data from various CSV formats into PowerSystems.jl-compatible objects.

Key Changes:

  • Implements CSV reader with support for RTS-GMLC and csv_118 formats with automatic format detection
  • Provides configurable defaults for missing data fields through CSVReaderConfig struct
  • Includes comprehensive documentation and examples for the new CSV reader functionality

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 10 comments.

File Description
src/io/readers/csv_reader.jl Core CSV reader implementation with format-specific parsers for buses, branches, generators, and loads
src/io/readers/PowerLASCOPFReaders.jl Module wrapper that exports the CSV reader functionality
src/io/readers/README.md Comprehensive documentation for CSV readers including usage examples and format specifications
examples/read_csv_example.jl Example script demonstrating usage of CSV readers with both supported formats

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +682 to +683
system = PSY.System(config.base_power)
system.name = "System_$(basename(data_dir))"
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct assignment to system.name may not be supported by PowerSystems.jl API. PowerSystems typically requires setting the name during construction via PSY.System(base_power; name=...) or using a setter method if available. This could fail at runtime.

Suggested change
system = PSY.System(config.base_power)
system.name = "System_$(basename(data_dir))"
system = PSY.System(config.base_power; name="System_$(basename(data_dir))")

Copilot uses AI. Check for mistakes.

for row in eachrow(df)
line_name = string(row[Symbol("Line Name")])
from_bus_name = string(row[Symbol("Bus from ")]) # Note the space
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment 'Note the space' highlights a fragile dependency on CSV column naming with trailing whitespace. Consider using strip() on column names during CSV reading or implementing a more robust column name matching that ignores leading/trailing whitespace to prevent silent failures.

Copilot uses AI. Check for mistakes.
Comment on lines +682 to +683
system = PSY.System(config.base_power)
system.name = "System_$(basename(data_dir))"
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The system name should be set during system construction rather than after. Modify line 682 to: system = PSY.System(config.base_power; name=\"System_$(basename(data_dir))\") and remove line 683.

Suggested change
system = PSY.System(config.base_power)
system.name = "System_$(basename(data_dir))"
system = PSY.System(config.base_power; name="System_$(basename(data_dir))")

Copilot uses AI. Check for mistakes.
Comment on lines 13 to 15
# Add the PowerLASCOPF source to the load path
push!(LOAD_PATH, joinpath(@__DIR__, "..", "src"))

Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modifying LOAD_PATH at runtime is not recommended. For package development, users should activate the package environment instead. Consider removing this line and documenting proper package activation in comments or README.

Suggested change
# Add the PowerLASCOPF source to the load path
push!(LOAD_PATH, joinpath(@__DIR__, "..", "src"))
# To run this example, activate the package environment by starting Julia with:
# julia --project=..
# This ensures all dependencies are available without modifying LOAD_PATH.

Copilot uses AI. Check for mistakes.

# Calculate load based on participation factor
p = total_system_load_pu * load_participation
q = p * 0.33 # Assume power factor of 0.95 (tan(acos(0.95)) ≈ 0.33)
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment explains the power factor calculation but the magic number 0.33 reduces maintainability. Consider defining this as a named constant (e.g., const DEFAULT_POWER_FACTOR_TAN = 0.33) with a more detailed comment explaining the power factor assumption.

Copilot uses AI. Check for mistakes.
bus_dict = Dict(b.name => b for b in buses)

# Assume total system load of 1000 MW for scaling
total_system_load_mw = 1000.0
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded total system load of 1000.0 MW is an arbitrary assumption that may not be appropriate for all systems. This should either be configurable through CSVReaderConfig or calculated from generator capacities with a warning about the assumption.

Copilot uses AI. Check for mistakes.

# Create operation cost (simplified)
# In a real implementation, you'd parse the heat rate curve
variable_cost = 30.0 # Default $/MWh
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default variable cost of 30.0 $/MWh is a magic number that should be made configurable through CSVReaderConfig. Different fuel types and regions have significantly different costs, making this hardcoded value potentially misleading.

Copilot uses AI. Check for mistakes.
### Basic Usage

```julia
using PowerSystems
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README shows importing PowerLASCOPFReaders as a separate module, but this module is defined in src/io/readers/ which is not the standard location for a Julia module. Users would need to manually add src/io/readers to LOAD_PATH or the module needs to be included in the main PowerLASCOPF module. This creates a discrepancy between documentation and actual usage.

Suggested change
using PowerSystems
using PowerSystems
# NOTE: PowerLASCOPFReaders is located in src/io/readers/, which is not a standard Julia module location.
# To use it directly, add the directory to LOAD_PATH before importing:
push!(LOAD_PATH, joinpath(@__DIR__, "src/io/readers"))

Copilot uses AI. Check for mistakes.
angle_limits = if hasproperty(df, Symbol("Min Angle Diff")) && hasproperty(df, Symbol("Max Angle Diff"))
(min = row[Symbol("Min Angle Diff")], max = row[Symbol("Max Angle Diff")])
else
(min = -π/6, max = π/6)
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default angle limits of ±π/6 radians (±30 degrees) are magic numbers that should be defined as named constants (e.g., const DEFAULT_ANGLE_LIMIT = π/6) or made configurable through CSVReaderConfig for better maintainability and clarity.

Copilot uses AI. Check for mistakes.
claude and others added 4 commits November 4, 2025 20:28
Changes:
- Include csv_reader.jl in main PowerLASCOPF module
- Export CSV reader functions from PowerLASCOPF module
- Remove standalone PowerLASCOPFReaders module wrapper
- Update README to show usage as part of PowerLASCOPF
- Update examples to use PowerLASCOPF instead of separate module

This resolves the module structure issue where PowerLASCOPFReaders
was presented as a standalone module but was located in src/io/readers/.
Now all CSV reader functions are properly integrated into and exported
from the main PowerLASCOPF module.

Usage is now:
  using PowerLASCOPF
  system = read_csv_system("path/to/data")

Instead of the previous (incorrect):
  using PowerLASCOPFReaders
  system = read_csv_system("path/to/data")
@sambuddhac sambuddhac changed the base branch from main to sams_branch November 5, 2025 01:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants