Skip to content

Commit 329ad00

Browse files
committed
Working on adding examples in the opendss folder
1 parent 655109b commit 329ad00

13 files changed

+357
-210
lines changed

1-buses_min_max_v.py

+38-12
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,69 @@
11
# -*- coding: utf-8 -*-
22
# @Author : Paulo Radatz
33
4-
# @File : 1-buses_min_max_v.py
5-
# @Software: PyCharm
64

75
import py_dss_interface
86
import os
97
import pathlib
108
import numpy as np
119

10+
# Get the script's current directory
1211
script_path = os.path.dirname(os.path.abspath(__file__))
1312

13+
# Construct the path to the DSS file within the 'feeders/123Bus' directory
1414
dss_file = pathlib.Path(script_path).joinpath("feeders", "123Bus", "IEEE123Master.dss")
1515

16-
dss = py_dss_interface.DSS() # using OpenDSS provided in the package
16+
# Initialize the OpenDSS interface using the py_dss_interface package
17+
dss = py_dss_interface.DSS()
18+
19+
# Compile the DSS file for the IEEE 123-bus test feeder to load the circuit into OpenDSS
1720
dss.text(f"compile [{dss_file}]")
21+
22+
# Load the bus coordinates from the 'buscoords.dat' file for visualization purposes. This file is in 'feeders/123Bus' directory
1823
dss.text("buscoords buscoords.dat")
24+
25+
# Run the snapshot power flow simulation
1926
dss.text("solve")
2027

28+
# Retrieve the names of all nodes in the circuit (in "Bus.Node" format)
2129
nodes = dss.circuit.nodes_names
30+
31+
# Retrieve the per-unit voltage magnitudes for all circuit nodes
2232
voltages = dss.circuit.buses_vmag_pu
23-
max_voltage = max(voltages)
24-
max_voltage_index = voltages.index(max_voltage)
25-
bus_max_voltages = nodes[max_voltage_index].split(".")[0]
2633

27-
min_voltage = min(voltages)
28-
min_voltage_index = voltages.index(min_voltage)
29-
bus_min_voltages = nodes[min_voltage_index].split(".")[0]
34+
# Find the maximum per-unit voltage and the node associated with it
35+
max_voltage = max(voltages) # Determine the maximum voltage
36+
max_voltage_index = voltages.index(max_voltage) # Get the index of this maximum voltage
37+
bus_max_voltages = nodes[max_voltage_index].split(".")[0] # Extract the bus name (exclude node)
38+
39+
# Find the minimum per-unit voltage and the node associated with it
40+
min_voltage = min(voltages) # Determine the minimum voltage
41+
min_voltage_index = voltages.index(min_voltage) # Get the index of this minimum voltage
42+
bus_min_voltages = nodes[min_voltage_index].split(".")[0] # Extract the bus name (exclude node)
43+
44+
# OPTIONAL (commented): Get the bus name directly using combined code
3045
# bus_min = dss.circuit.nodes_names[dss.circuit.buses_vmag_pu.index(min(dss.circuit.buses_vmag_pu))].split(".")[0]
3146

47+
# Mark capacitors in the circuit diagram
3248
dss.text("set markcapacitor=yes")
49+
50+
# Mark regulators in the circuit diagram
3351
dss.text("set markregulator=yes")
52+
53+
# Add a red marker for the bus with the minimum voltage
3454
dss.text(f"AddBusMarker Bus={bus_min_voltages} code=7 color=red size=10")
55+
56+
# Add a green marker for the bus with the maximum voltage
3557
dss.text(f"AddBusMarker Bus={bus_max_voltages} code=7 color=green size=10")
36-
dss.text("plot circuit Power max=2000 n n C1=$00FF0000")
3758

59+
# Plot the circuit diagram with power flow results
60+
dss.text("plot circuit Power max=2000 n n C1=$00FF0000")
3861

62+
# Convert the list of voltages and node names into NumPy arrays for efficient processing
3963
voltages_array = np.array(voltages)
4064
nodes_array = np.array(nodes)
4165

42-
# Bus.Node with voltage less than 0.98 pu
43-
buses = nodes_array[voltages_array < 0.98]
66+
# Apply a filter to identify all nodes where the per-unit voltage is less than 0.98
67+
# This results in a list of node names (in "Bus.Node" format) with low voltage
68+
buses = nodes_array[voltages_array < 0.98]
69+
print(buses)

1-creating_dss_obj.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
# @Author : Paulo Radatz
3+
4+
5+
"""
6+
This script demonstrates how to create the DSS object using either the OpenDSS version (powered by EPRI) included in
7+
the package or the version installed on your computer.
8+
9+
Watch this video for more information:
10+
https://www.youtube.com/watch?v=sUZJfwor8xs&list=PLhdRxvt3nJ8xURfBipVoAx8du1a-S5YsL&index=2
11+
"""
12+
13+
import py_dss_interface
14+
15+
# Instantiating an OpenDSS object using the py_dss_interface.DSS() class.
16+
# This creates an interface to use OpenDSS functionalities provided by the py_dss_interface package.
17+
dss = py_dss_interface.DSS()
18+
19+
# Print the status of the OpenDSS interface and its version:
20+
# - `dss.started` checks whether the OpenDSS interface has been successfully initiated.
21+
# - `dss.dssinterface.version` returns the version of the OpenDSS executable being used.
22+
print(f"OpenDSS from Package started: {dss.started} \n"
23+
f"Version {dss.dssinterface.version}")
24+
25+
26+
# You can use the OpenDSS version installed on your computer by providing the path to the dll_folder_param property.
27+
# Keep in mind that py-dss-interface has only been tested with the provided OpenDSS version,
28+
# so using a different version may not be fully compatible.
29+
dss = py_dss_interface.DSS(r"C:\Program Files\OpenDSS")
30+
print(f"\nOpenDSS from Computer started: {dss.started} \n"
31+
f"Version {dss.dssinterface.version}")

2-futher_3ph_bus.py

+29-6
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,57 @@
11
# -*- coding: utf-8 -*-
22
# @Author : Paulo Radatz
33
4-
# @File : 2-futher_3ph_bus.py
5-
# @Software: PyCharm
64

75
import py_dss_interface
86
import os
97
import pathlib
108

9+
# Get the path of the current script
1110
script_path = os.path.dirname(os.path.abspath(__file__))
1211

12+
# Path to the distribution feeder file to be used in OpenDSS
1313
dss_file = pathlib.Path(script_path).joinpath("feeders", "123Bus", "IEEE123Master.dss")
1414

15-
dss = py_dss_interface.DSS() # using OpenDSS provided in the package
15+
# Initialize an OpenDSS interface object
16+
dss = py_dss_interface.DSS()
17+
18+
# Compile the specified OpenDSS model file
1619
dss.text(f"compile [{dss_file}]")
20+
21+
# Add an energy meter to the feeder to define distances for the buses
1722
dss.text("New EnergyMeter.Feeder Line.L115 1")
23+
24+
# Load bus coordinate data from an external file
1825
dss.text("buscoord buscoords.dat")
26+
27+
# Solve the snapshot power flow:
28+
# the goal is to define the distances for the buses, we could do it using different commands that make the bus list
1929
dss.text("solve")
2030

21-
bus_dist_actual = 0
22-
bus_name = None
31+
# Initialize variables to find the farthest three-phase bus
32+
bus_dist_actual = 0 # Keeps track of the maximum bus distance
33+
bus_name = None # Holds the name of the farthest bus
34+
35+
# Iterate through all bus names in the circuit
2336
for bus in dss.circuit.buses_names:
37+
# Set the current bus as active in OpenDSS
2438
dss.circuit.set_active_bus(bus)
39+
40+
# Check if the current bus has at least 3 nodes (indicates a three-phase bus)
2541
if len(dss.bus.nodes) >= 3:
42+
# Get the distance of the current bus
2643
bus_dist = dss.bus.distance
44+
45+
# Update the farthest bus if the current bus is farther
2746
if bus_dist > bus_dist_actual:
2847
bus_dist_actual = bus_dist
29-
bus_name = bus
48+
bus_name = bus # Store the name of the farthest bus
3049

50+
# Print the name of the farthest three-phase bus and its distance
3151
print(f"Bus: {bus_name} with distance: {bus_dist_actual} is the furthest Three-phase bus")
3252

53+
# Add a bus marker to the farthest three-phase bus in the OpenDSS circuit plot
3354
dss.text(f"AddBusMarker Bus={bus_name} code=7 color=Red size=10")
55+
56+
# Plot the circuit with the farthest three-phase bus
3457
dss.text("plot circuit Power max=2000 n n C1=$00FF0000")

2-text_method.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# -*- coding: utf-8 -*-
2+
# @Author : Paulo Radatz
3+
4+
5+
"""
6+
This script demonstrates that anything you can do in OpenDSS standalone can also be done through Python.
7+
8+
Watch this video for more explanation:
9+
https://www.youtube.com/watch?v=BIMcjZWpJek&list=PLhdRxvt3nJ8w36keL4uGBNbWs5SRxEyW0
10+
"""
11+
12+
import os
13+
import pathlib
14+
import py_dss_interface
15+
16+
# Get the current script's directory path
17+
script_path = os.path.dirname(os.path.abspath(__file__))
18+
19+
# Construct the full path to the OpenDSS feeder file (IEEE 123-node test feeder)
20+
# This assumes the `feeders/123Bus/IEEE123Master.dss` file structure is within the same directory containing the script
21+
dss_file = pathlib.Path(script_path).joinpath("feeders", "123Bus", "IEEE123Master.dss")
22+
23+
# Initialize the DSS interface
24+
dss = py_dss_interface.DSS()
25+
26+
# Use the OpenDSS Text interface to compile the .dss file.
27+
# The file path to the DSS model is passed within square brackets.
28+
dss.text(f"compile [{dss_file}]")
29+
30+
# Set the simulation mode to "Snapshot"
31+
dss.text("set mode=SnapShot")
32+
33+
# Solve the snapshot power flow for the loaded circuit.
34+
dss.text("solve")
35+
36+
# Generate and display a report of the line-to-neutral (LN) voltages at each node.
37+
dss.text("Show Voltage LN Nodes")
38+
39+
# Open up an interactive form to edit a line object named "Line.L1" within the DSS model
40+
# This allows for visually inspecting or modifying the parameters for the specified line.
41+
dss.text('FormEdit "Line.L1"')
42+
43+
# Export voltage information to a file and also store the command's return value as a string in the `result` variable.
44+
result = dss.text("export voltages") # Text method returns strings. It could be useful.
45+

3-max_load_mult.py

+37-11
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,60 @@
11
# -*- coding: utf-8 -*-
22
# @Author : Paulo Radatz
33
4-
# @File : 3-max_load_mult.py
5-
# @Software: PyCharm
64

75
import py_dss_interface
86
import os
97
import pathlib
108

9+
# Getting the absolute directory path of the current script
1110
script_path = os.path.dirname(os.path.abspath(__file__))
1211

12+
# Building the full path to the IEEE 123-bus feeder model
1313
dss_file = pathlib.Path(script_path).joinpath("feeders", "123Bus", "IEEE123Master.dss")
1414

15-
dss = py_dss_interface.DSS() # using OpenDSS provided in the package
15+
# Instantiating the DSS interface object that allows interaction with OpenDSS
16+
dss = py_dss_interface.DSS()
1617

18+
# Initializing a flag to track when the voltage drops below the threshold (0.95 per unit)
1719
under_voltage = False
20+
21+
# Counter for iteration
1822
i = 0
19-
while not under_voltage and i < 100:
20-
i = i + 1
23+
load_mult = 1
24+
25+
# Start iterating to find the maximum permissible load multiplier without causing undervoltage
26+
while not under_voltage and i < 100: # Stop either when undervoltage is found or after 100 iterations
27+
i = i + 1 # Incrementing the iteration counter
28+
29+
# Calculating the load multiplier as 1 + i/100
2130
load_mult = 1 + i / 100
31+
32+
# OpenDSS commands:
33+
# Reloading the DSS model
2234
dss.text(f"compile [{dss_file}]")
35+
36+
# Creating a new energy meter at Line "L115" in case you want to check the voltage profile commented out below
2337
dss.text("New EnergyMeter.Feeder Line.L115 1")
38+
39+
# Setting the load multiplier (scale factor for system loads)
2440
dss.text(f"set loadmult={load_mult}")
41+
42+
# Solving the distribution power flow for the current conditions
2543
dss.text("solve")
2644

27-
if min(dss.circuit.buses_vmag_pu) < 0.95:
28-
under_voltage = True
45+
# Plot profile, I would run it in debug mode to plot each profile individually.
46+
# dss.text("Plot profile phases=all")
47+
48+
# Checking the minimum voltage magnitude (in per unit) in all buses of the circuit
49+
if min(dss.circuit.buses_vmag_pu) < 0.95: # If any bus voltage drops below 0.95 pu
50+
under_voltage = True # Set the flag for undervoltage
51+
52+
# Adjust the load multiplier to the value before the last increment (i.e., the last safe value)
2953
load_mult = 1 + (i - 1) / 100
3054

31-
if under_voltage:
32-
print(f"The max loadmult: {load_mult}")
33-
else:
34-
print(f"No problem with max loadmult of: {load_mult}")
55+
56+
# Once the loop finishes, print the results
57+
if under_voltage: # If the loop stopped due to undervoltage
58+
print(f"The max loadmult: {load_mult}") # Print the maximum safe load multiplier
59+
else: # If the loop reached 100 iterations without causing undervoltage
60+
print(f"No problem with max loadmult of: {load_mult}")

4-line_dataframe.py

+35-12
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,66 @@
11
# -*- coding: utf-8 -*-
22
# @Author : Paulo Radatz
33
4-
# @File : 4-line_dataframe.py
5-
# @Software: PyCharm
64

75
import py_dss_interface
86
import os
97
import pathlib
108
import pandas as pd
119

10+
# Get the directory path of the current script
1211
script_path = os.path.dirname(os.path.abspath(__file__))
1312

13+
# Define the path to the DSS model file
1414
dss_file = pathlib.Path(script_path).joinpath("feeders", "123Bus", "IEEE123Master.dss")
1515

16+
# Create an instance of the OpenDSS interface
1617
dss = py_dss_interface.DSS()
17-
dss.text(f"compile [{dss_file}]")
1818

19-
dss.lines.first()
20-
line_properties = dss.cktelement.property_names
19+
# Compile the DSS file to load the model into the OpenDSS engine
20+
dss.text(f"compile [{dss_file}]")
2121

22+
# Prepare an empty dictionary to store the data we will collect
2223
dict_to_df = dict()
2324

25+
# Initialize a list to store the names of all the lines in the model
2426
line_name_list = list()
27+
28+
# Get the first line in the loaded DSS file - it is the first line loaded into the OpenDSS memory
2529
dss.lines.first()
26-
for _ in range(dss.lines.count):
27-
line_name_list.append(dss.lines.name)
28-
dss.lines.next()
30+
31+
# Retrieve the list of property names for the current circuit element
32+
line_properties = dss.cktelement.property_names
33+
34+
# Loop through all lines in the model to collect their names
35+
for _ in range(dss.lines.count): # Loop over the total number of lines
36+
line_name_list.append(dss.lines.name) # Add the current line's name to the list
37+
dss.lines.next() # Move to the next line in the DSS model
38+
39+
# Add the collected line names to the dictionary with the key "name"
2940
dict_to_df["name"] = line_name_list
3041

42+
# Loop through all the circuit (line) element properties
3143
for line_property in line_properties:
44+
# Prepare a list to store the values of the current property for all lines
3245
property_list = list()
3346

47+
# Move to the first line in the DSS model
3448
dss.lines.first()
35-
for _ in range(dss.lines.count):
36-
property_list.append(dss.dssproperties.value_read(str(dss.cktelement.property_names.index(line_property) + 1)))
37-
dss.lines.next()
3849

39-
dict_to_df[line_property] = property_list
50+
# Loop through all lines in the model to collect the property values
51+
for _ in range(dss.lines.count): # Loop over the total number of lines
52+
# Retrieve the value of the current property and add it to the list
53+
property_index = str(dss.cktelement.property_names.index(line_property) + 1) # Property index is 1-based
54+
property_list.append(dss.dssproperties.value_read(property_index))
55+
dss.lines.next() # Move to the next line
4056

57+
# Add the collected property values to the dictionary with the property name as the key
58+
dict_to_df[line_property] = property_list
4159

60+
# Convert the dictionary into a Pandas DataFrame and set the line names as the index
4261
df = pd.DataFrame().from_dict(dict_to_df).set_index("name")
62+
print(df.head(3))
63+
64+
# Print a message to state that the process is complete.
65+
# If you liked it, you might find this project interesting "https://github.com/PauloRadatz/py_dss_tools"
4366
print("This is cool!")

0 commit comments

Comments
 (0)