dss-extensions / OpenDSSDirect.py

OpenDSSDirect.py: a cross-platform Python package that implements a native/direct library interface to the alternative OpenDSS engine from DSS-Extensions.org

Home Page:https://dss-extensions.org/OpenDSSDirect.py/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

SwtControls.Action() has no effect after SwtControl is unlocked

wfvining opened this issue · comments

Versions

  • Python version: 3.8.5
  • Python architecture: 64 bit (AMD64)
  • Operating system and version: Windows 10
  • OpenDSSDirect.py version number: 0.6.0

Bug

  • What is the current behavior?

Switch controls don't work after being unlocked. For example after calling SwtControls.IsLocked(False) I should be able to use SwtControls.Action() to open or close the switch, but it has no effect. If I reset the control with SwtControls.Reset() then I can open/close the switch again.

  • What is the expected behavior?

After unlocking a switch control object it should be possible to use it to open/close the switch it controls by calling SwtControls.Action() without resetting the switch controller. Using reset may change the state of the switch which prevents me from simulating a situation where the switch can operate again, but I want it to stay its current state (which may or may not be its "normal" state).

  • What are the steps to reproduce this bug?
import opendssdirect as dssdirect
dssdirect.run_command("redirect IEEE13Nodeckt.dss")
dssdirect.run_command("new swtcontrol.sw1 delay=0.0 normal=closed switchedobj=line.671684 switchedterm=1")
dssdirect.Solution.SolveSnap()
dssdirect.SwtControls.Name("sw1")
# open the switch
dssdirect.SwtControls.Action(1)
dssdirect.Solution.SolvePlusControl()
# lock the switch control
dssdirect.SwtControls.IsLocked(True)
# try to close the switch ...
dssdirect.SwtControls.Action(2)
dssdirect.Solution.SolvePlusControl()
# ... but the line is still open because the control is locked
dssdirect.Circuit.SetActiveElement("line.671684")
assert dssdirect.CktElement.IsOpen(1, 1)
# unlock the control and close the switch
dssdirect.SwtControls.IsLocked(False)
dssdirect.SwtControls.Action(2)
dssdirect.Solution.SolvePlusControl()
# the line should now be closed
assert not dssdirect.CktElement.IsOpen(1, 1)

If you check Solution.EventLog(), you'll see no action is being done at all. Armed is true here, so it ignores the command:

https://github.com/dss-extensions/dss_capi/blob/0.10.7-1/Version7/Source/Controls/SwtControl.pas#L465

It seems that the only way to set Armed to false after an operation is indeed resetting.
This behavior is the same in the official/upstream OpenDSS (ported below), it would be unwise to change it just here -- and there is probably a reason for the current behavior.

COM version of the sample code
import os, win32com.client
orgcd = os.getcwd()
dss = win32com.client.Dispatch('OpenDSSengine.DSS')
os.chdir(orgcd)

dss.Text.Command = "redirect IEEE13Nodeckt.dss"
dss.Text.Command = "new swtcontrol.sw1 delay=0.0 normal=closed switchedobj=line.671684 switchedterm=1"
dss.ActiveCircuit.Solution.SolveSnap()
dss.ActiveCircuit.SwtControls.Name = "sw1"
# open the switch
dss.ActiveCircuit.SwtControls.Action = 1
dss.ActiveCircuit.Solution.SolvePlusControl()
# lock the switch control
dss.ActiveCircuit.SwtControls.IsLocked = True
# try to close the switch ...
dss.ActiveCircuit.SwtControls.Action = 2
dss.ActiveCircuit.Solution.SolvePlusControl()
# ... but the line is still open because the control is locked
dss.ActiveCircuit.SetActiveElement("line.671684")
assert dss.ActiveCircuit.ActiveCktElement.IsOpen(1, 1)
# unlock the control and close the switch
dss.ActiveCircuit.SwtControls.IsLocked = False
dss.ActiveCircuit.SwtControls.Action = 2
dss.ActiveCircuit.Solution.SolvePlusControl()
# the line should now be closed
assert not dss.ActiveCircuit.ActiveCktElement.IsOpen(1, 1)

Using reset may change the state of the switch which prevents me from simulating a situation where the switch can operate again

It does the following instantly, no control queue is involved (unlike Action()):

  • Sets the current state to the normal state.
  • Resets the armed state to false.
  • Opens or closes the controlled element terminal according to the normal state.

Looks like just saving its state, resetting, then updating the state to the saved state would be enough, no?
Manipulating State() directly bypasses most of the logic in the component.

By the way, be sure you know what SolvePlusControl does in comparison to a normal solution loop.

Looks like just saving its state, resetting, then updating the state to the saved state would be enough, no?
Manipulating State() directly bypasses most of the logic in the component.

# [...]
# unlock the control and close the switch
state = dssdirect.SwtControls.State()
dssdirect.SwtControls.Reset()
dssdirect.SwtControls.State(state)
dssdirect.Solution.Solve()
assert dssdirect.CktElement.IsOpen(1, 1), "closed after reset (should be open)"

Unfortunately that work-around doesn't seem to work. Checking the event log I see an extra control action has been taken to close the switch, and the call to SwtControls.State() has no effect.

This behavior is the same in the official/upstream OpenDSS (ported below), it would be unwise to change it just here -- and there is probably a reason for the current behavior.

I'll hit up the official OpenDSS forums. If there is a reason, I'd like to understand it. The current behavior seem pretty strange to me; makes me question whether I'm using switch controls correctly in the first place.

By the way, be sure you know what SolvePlusControl does in comparison to a normal solution loop.

My understanding is that it just executes a powerflow then clears the control queue by executing all pending actions, is that right?

Unfortunately that work-around doesn't seem to work.

Just checked the code again, and yeah, it wouldn't work. State() only updates the local component (SwtControl) without any side effects. You'd have to open/close the terminal of the controlled element yourself. I see no purpose in that. At this point, I'd question the usage of the component in the first place.

A state diagram would certainly help clear things up.

My understanding is that it just executes a powerflow then clears the control queue by executing all pending actions, is that right?

The main Solve function calls a separate solution loop according to the current solution mode. I believe many of the extra Solve... functions exist to facilitate the creation of custom solution loops.
SolveSnap is used somewhere in the loop (e.g. time loop) for most modes -- it solves the basic circuit and then iterates until controls are done.
SolvePlusControl just solves the circuit once and checks the controls once -- no outer iteration. It doesn't check ControlActionsDone, in fact it doesn't reset the ControlIteration counter or set ControlActionsDone to false like SolveSnap/InitSnap do.
For most uses, setting solution mode, control mode, and the solution number, and then calling the normal Solve is enough.

I'll hit up the official OpenDSS forums. If there is a reason, I'd like to understand it. The current behavior seem pretty strange to me; makes me question whether I'm using switch controls correctly in the first place.

@wfvining, sorry to bother, did you ever look further into this?