OpenDSS CtrlQueue Interface
The CtrlQueue interface contains properties and methods for interactions between internal DSS control objects and control algorithms written in programs driving the OpenDSS program through its interface.
DSS Control Queue
The OpenDSS program has an internal control dispatching process implemented as a queue of action requests at specified times. This control queue is primarily for managing discrete controls such as tap-changing voltage regulators and switched capacitors that can have delayed actions. For example, when a voltage regulator goes out of band, it pushes a message on the Control Queue with a requested time for a tap-changing action. This
simulates starting the timer in the control. When the requested time is reached, the message is sent back to the regulator, which decides at that time whether or not it really needs to change taps. This simulates the timer expiring and putting a signal on an AND gate with the out-of-band signal, which is the way some regulator controls work.
Continuous controls generally act directly on variables within the program and any delay is accomplished through time integration. Continuous controls are primarily used in dynamics simulations while discrete controls are mainly for sequential power flow simulations. They are often implemented by user-written DLLs.
The Control Queue gets populated after a converged solution is achieved. All of the control objects currently enabled in the circuit are polled. If they determine that they need to make a delayed change, they will push a control action onto the Control Queue using codes that are unique to each class of control object. When it comes time to execute the action, the OpenDSS pops the appropriate control actions off the Control Queue and dispatches each of them to the appropriate control handler (a virtual function called DoPendingAction in the module that pushed the action code onto the control queue).
The CtrlQueue COM interface instantiates a proxy that provides a DoPendingAction function to the OpenDSS and, thus imitates an internal OpenDSS control object.
COM Interface Control Proxy
The COM interface contains a proxy for any control objects that might be implemented in some program external to the OpenDSS and driving the OpenDSS through the COM interface. The COM Control Proxy contains an Action List that contains a list of actions that have been popped off the OpenDSS Control Queue (see Figure 1) and sent back to the module that pushed the message onto the control queue. User-written control algorithms should check this List and execute the requested action, if it is still appropriate to do so.
The Action List dispatches control requests using a Device Handle and an Action Code. Controls implemented in external languages via the COM interface should be associated with a particular user-assigned Device Handle for dispatching by the COM Control Proxy object. At this time, Device Handles and Action Codes are defined in the external userwritten code, but a future version may also employ handles and codes managed by the simulator.
Typically, an external control would step through the solution one step at a time. After achieving a converged circuit solution, the control would execute the functions that sample the quantities of interest. It could then Push any control actions onto the OpenDSS Control Queue and rely on the OpenDSS to dispatch the control. The OpenDSS would know the action request came from the COM Control Proxy and dispatch any actions back to the Proxy where they are accumulated in the Action Queue. The user-written control would then be responsible for checking the Action List, setting the Active Action (directly or by popping off the list in sequence), and then dispatching the action code to the proper control algorithm.
All discrete external controls with time-delayed actions should employ the OpenDSS Control Queue for proper synchronization with other active controllers in the circuit. When the time comes to execute an action, external controls may act directly upon circuit objects through the COM interface. For example, if a user-written relay control determines that a device terminal needs to be opened, it can do so through either the text interface or the CktElement interface.
Note that accessing most of these properties and methods without an active circuit defined will result in no action.
COM Interface Control Proxy Operation
Figure 1. Illustration of how the COM Control Proxy interacts with the DSS Control Queue
Properties:
NumActions:Integer (Read only) |
Returns the number of actions in the COM Control Proxy Action List. Check this value to see if the OpenDSS control queue has popped any actions off the list that are destined for the user-written control. |
Action(Index: Integer): (Write Only) |
One way to set which of the Actions in the Action List is the Active Action. Sets it by index value after checking to make sure that Index is within the valid range (based on NumActions). This is a zero-based index - the first element in the Action List is index 0. |
ActionCode: Integer: (Read Only) |
Action Code for the Active Action in the COM Control Proxy Action List. Use this to determine what the user-defined controls are supposed to do. Can be any long (32-bit) integer of the programmer’s choosing and is the same value that the control pushed onto the control queue earlier. |
DeviceHandle:Integer (Read only) |
Returns a handle to the user’s control device from the Active Action. The user-written code driving the COM interface may support more than one control element as necessary to perform the simulation. This handle is an index returned to the user program that lets the program know which control is to perform the active action. |
PopAction: Integer (Read Only) |
Sets the Active Action by popping the next action off the COM Control Proxy action list. Returns zero when there are no more actions to pop. |
Methods:
Function ClearQueue: HResult; |
This clears all actions from the DSS Control Queue. The return value is always zero. |
Function Delete (ActionHandle: Integer): HResult |
Deletes an Action from the DSS Control Queue by the handle that is returned when the action is added. (The Push function returns the handle.) The return value is always zero. |
Function Push (Hour: Integer; Seconds: Double; ActionCode: Integer; DeviceHandle: Integer): HResult; |
Pushes an Action onto the DSS Control Queue. The return value is the handle to the action on the queue. Specify the time of the requested action in Hour, Seconds. Push an ActionCode that is meaningful to the user-written control object. DeviceHandle is a userdefined handle (a 32-bit Integer) to the control object that will be used to dispatch the control action to the proper control device managed by the user-written code when the Action is popped off the Control Queue. The DSS will automatically return this handle to the control proxy in the COM interface when the action is popped. The actual internal DSS Control Queue Push function passes one more argument not shown in this COM interface method. For internal control objects, it is the Self variable; for any control model coming through the COM interface, it is the address of the COMControlProxyObj variable. When an action destined for the user-written code is popped off the DSS Control Queue, the COMControlProxyObj.DoPendingAction function is called. This dispatches the action message to the Action List in the userwritten code. Then the user-written code must decipher it. |
Function Show: HResult; |
This function executes the procedure inside the OpenDSS to show a text file in CSV form containing the present contents of the DSS Control Queue. The return value is always zero. When this command is complete the Result property of the Text interface contains the name of the file, which you may use for further processing. (The Result property must be accessed before doing anything else or it will disappear.) |
Function ClearActions: HResult; |
This clears all actions from the Control Proxy’s Action List (they are popped off the list). The return value is always zero. |
Example 1
[DSSStartOK, DSSObj, DSSText] = DSSStartup;
if DSSStartOK
DSSText.command='Compile (C:\opendss\IEEETestCases\123Bus\IEEE123Master.dss)';
% Set up the interface variables
DSSCircuit=DSSObj.ActiveCircuit;
DSSSolution=DSSCircuit.Solution;
DSSText.Command='Set MaxControlIter=30';
DSSSolution.SolveNoControl; % solve power flow with no control sampling or actions
disp(['Result=' DSSText.Result])
if DSSSolution.Converged
a = 'Solution Converged';
disp(a)
else
a = 'Solution did not Converge';
disp(a)
end
DSSText.Command='Export Voltages';
disp(DSSText.Result)
DSSSolution.SampleControlDevices;
DSSCircuit.CtrlQueue.Show; % show the contents of the control queue
disp(DSSText.Result)
DSSSolution.DoControlActions; % execute the control actions
DSSCircuit.CtrlQueue.Show; % see what is left on the queue
else
a = 'DSS Did Not Start'
disp(a)
end
Example 2 (requires 7.6.4.42 or later)
Here is a VBA macro for Excel that implements a capacitor control based on voltage. It uses the DSS script below that describes a simple circuit with a 4-step capacitor. It starts off turning all steps off and then increments load (using the Loadmult option) until all steps turn on because the voltage goes low. Then it reverses direction on the load multiplier until all steps turn off because the voltage goes too high. In Excel VBA, the output will show up in the “Immediate Window” (ctrl-G will open it).
Option Explicit
' VBA code for Excel
Public DSSobj As OpenDSSengine.DSS
Public DSSText As OpenDSSengine.Text
Public DSSCircuit As OpenDSSengine.Circuit
Public DSSSolution As OpenDSSengine.Solution
Public DSSControlQueue As OpenDSSengine.CtrlQueue
Public DSSCktElement As OpenDSSengine.CktElement
Public DSSPDElement As OpenDSSengine.PDElements
Public DSSMeters As OpenDSSengine.Meters
Public DSSBus As OpenDSSengine.Bus
Public DSSCmath As OpenDSSengine.CmathLib
Public DSSParser As OpenDSSengine.Parser
Public DSSIsources As OpenDSSengine.ISources
Public DSSMonitors As OpenDSSengine.Monitors
' Execute this first
Public Sub StartDSS()
' Create a new instance of the DSS
Set DSSobj = New OpenDSSengine.DSS
' Start the DSS
If Not DSSobj.Start(0) Then
MsgBox "DSS Failed to Start"
Else
' MsgBox "DSS Started successfully"
' Assign a variable to each of the interfaces for easier access
Set DSSText = DSSobj.Text
Set DSSCircuit = DSSobj.ActiveCircuit
Set DSSSolution = DSSCircuit.Solution
Set DSSControlQueue = DSSCircuit.CtrlQueue
Set DSSCktElement = DSSCircuit.ActiveCktElement
Set DSSPDElement = DSSCircuit.PDElements
Set DSSMeters = DSSCircuit.Meters
Set DSSBus = DSSCircuit.ActiveBus
Set DSSCmath = DSSobj.CmathLib
Set DSSParser = DSSobj.Parser
Set DSSIsources = DSSCircuit.ISources
Set DSSMonitors = DSSCircuit.Monitors
Range("DSSVersion").Value = "Version: " + DSSobj.Version
Beep
End If
End Sub
Public Sub TestCtrlQueue()
' Example of implementing a simple voltage control for Capacitors via the COM interface
Dim hour As Long, secDelay As Double
' Run simple capacitor interface test and execute local cap control that emulates
CapControl
' with these settings:
' PT=125.09 Type=voltage onsetting=118.8 offsetting=121.2
Dim DSSCapacitors As OpenDSSengine.Capacitors
Set DSSCapacitors = DSSCircuit.Capacitors ' set a variable to the Capacitors
interface
' this test case has a four-step capacitor bank named "cap" and can be found in the
Test Folder
DSSText.Command = "Compile
(C:\Users\prdu001\OpenDSS\Test\Master_TestCapInterface.DSS)"
' Set all capacitor steps open for first capacitor
Dim i As Long, iCap As Long, iStates(1 To 10) As Variant
Dim strValue As String
iCap = DSSCapacitors.First ' should check iCap for >0
For i = 1 To DSSCapacitors.NumSteps
iStates(i) = 0
Next i
DSSCapacitors.States = iStates ' push over the interface to OpenDSS
' check to make sure it worked
DSSText.Command = "? Capacitor.Cap.States"
strValue = "Starting Capacitor Step States=" + DSSText.Result ' should be [0 0 0 0]
Debug.Print strValue
' Base solution
DSSSolution.Solve
' Each message we push onto the queue will get a 5 s delay
hour = 0
secDelay = 5 ' delay
Dim V As Variant ' for getting bus voltages
Dim PTratio As Double, Vreg As Double
Dim ONsetting As Double, OFFsetting As Double
Dim ActionCodeAdd As Long, DeviceHandle As Long, ActionCodeSub As Long
PTratio = 125.09 ' for 26 kV system
ONsetting = 118.8
OFFsetting = 121.2
ActionCodeAdd = 201 ' just an arbitrary action code
ActionCodeSub = 202 ' just another arbitrary action code
DeviceHandle = 123 ' arbitrary handle that signifies this control
' now, we'll crank the load up in 10% steps, checking the voltage at each step
' until all cap steps are on (no more available)
i = 0
Do While DSSCapacitors.AvailableSteps > 0
i = i + 1
DSSSolution.LoadMult = 1# + i * 0.1 ' 10% more each time
DSSSolution.InitSnap
DSSSolution.SolveNoControl
DSSSolution.SampleControlDevices ' sample all other controls
' Emulate the cap control Sample Routine and get the bus voltage
DSSCircuit.SetActiveBus "feedbus"
V = DSSBus.VMagAngle
' check the first phase magnitude
Vreg = V(0) / PTratio
Debug.Print "Step "; i; " Voltage="; Vreg; " LoadMult="; DSSSolution.LoadMult
If Vreg < ONsetting Then ' push a message to bump up the number of steps
DSSControlQueue.Push hour, secDelay, ActionCodeAdd, DeviceHandle
End If
DSSSolution.DoControlActions ' this sends actions to the local action list
If DSSControlQueue.NumActions > 0 Then
Do While DSSControlQueue.PopAction > 0
Select Case DSSControlQueue.DeviceHandle
Case 123
iCap = DSSCapacitors.First ' Sets designated capacitor active
End Select
Select Case DSSControlQueue.ActionCode
Case 201
DSSCapacitors.AddStep
Case 202
DSSCapacitors.SubtractStep
End Select
' Print result
DSSText.Command = "? Capacitor." + DSSCapacitors.Name + ".States"
Debug.Print "Capacitor " + DSSCapacitors.Name + " States=" +
DSSText.Result
Loop
End If
Loop
' Now let's reverse Direction and start removing steps
Do While DSSCapacitors.AvailableSteps < DSSCapacitors.NumSteps
i = i - 1
DSSSolution.LoadMult = 1# + i * 0.1 ' 10% more each time
DSSSolution.InitSnap
DSSSolution.SolveNoControl
DSSSolution.SampleControlDevices ' sample all other controls
' Emulate the cap control Sample Routine and get the bus voltage
DSSCircuit.SetActiveBus "feedbus"
V = DSSBus.VMagAngle
' check the first phase magnitude
Vreg = V(0) / PTratio
Debug.Print "Step "; i; " Voltage="; Vreg; " LoadMult="; DSSSolution.LoadMult
If Vreg > OFFsetting Then ' push a message to bump down the number of steps
DSSControlQueue.Push hour, secDelay, ActionCodeSub, DeviceHandle
End If
DSSSolution.DoControlActions ' this send actions to the local action list
If DSSControlQueue.NumActions > 0 Then
Do While DSSControlQueue.PopAction > 0
Select Case DSSControlQueue.DeviceHandle
Case 123
iCap = DSSCapacitors.First ' Sets designated capacitor active
End Select
Select Case DSSControlQueue.ActionCode
Case 201
DSSCapacitors.AddStep
Case 202
DSSCapacitors.SubtractStep
End Select
' Print result
DSSText.Command = "? Capacitor." + DSSCapacitors.Name + ".States"
Debug.Print "Capacitor " + DSSCapacitors.Name + " States=" +
DSSText.Result
Loop
End If
Loop
End Sub
The DSS script for the circuit (from the Test folder).
New Circuit.CapInterface
~ basekv=161 pu=1.05 angle=0.0 freq=60.0 phases=3
!************** Begin Transformers **************!
new transformer.TR1 phases=3 windings=2 buses=(genbus,feedbus) conns=(delta,wye)
~ kvs=(161,26) kvas=(42000,42000) XHL=9.049 %r=0.2961
new transformer.TR2 phases=3 windings=2 buses=(genbus,feedbus) conns=(delta,wye)
~ kvs=(161,26) kvas=(42000,42000) XHL=8.26 %r=0.2877
!************** End Transformers **************!
!************** Begin Lines **************!
! R and X are real values Base V=161kV
! Assumed zero sequence R and X is 3X positive sequence
NEW line.line1 bus1=sourcebus bus2=genbus R1=0.9357 X1=6.9987 R0=2.8071 X0=20.9961
!************** End Lines **************!
!************** Begin Capacitors **************!
New Capacitor.Cap Bus1=feedbus phases=3 kv=26 numsteps=4 kvar=[5400 5400 5400 5400]
// New CapControl.CapCtrl element=transformer.TR2 terminal=2 capacitor=Cap
// ~ PT=125.09 Type=voltage onsetting=118.8 offsetting=121.2
!************** End Capacitors **************!
!************** Begin Loads **************!
!2006 Med. Summer Extreme Pk Forecast, updated PF to match PSS/E data
New Load.Load_TH_Summer bus1=feedbus phases=3 kv=26 kw=57000 pf=0.95 model=1
!************** End Loads **************!
Set voltagebases=[161 26]
Calcv