dss-extensions / OpenDSSDirect.jl

OpenDSSDirect.jl is a cross-platform Julia package implements a "direct" library interface to OpenDSS, currently using the alternative OpenDSS implementation from DSS-Extensions
https://dss-extensions.org/OpenDSSDirect.jl/latest/
Other
24 stars 6 forks source link

`DSSObject.Field() = Value::Float64` should throw an error if the left hand side is only a getter function call #105

Closed Realife-Brahmin closed 1 hour ago

Realife-Brahmin commented 1 week ago

Earlier I was trying to set my PV real and reactive power values by using the first two sets of locs: (assume I am iterating over PVs correctly, locs for the same not shown in this example)

# Incorrect methods which DO NOT throw an error:
PVsystems.kW() = 5 # Like saying 3 = 5, this should instantly throw an error, especially since kW is purely a getter function for PVsystems
PVsystems.kvar() = 2.4 # Also like saying 3 = 5, although PVsystems.kvar has setter function capabilities.

# Correct calls?
PVsystems.Pmpp(5) # Why Pmpp? Why not kW? From docs, calling kW as nominal value unfortunately doesn't clarify it for me.
PVsystems.kvar(2.4) # Okay.

ODD.jl should throw an error for the first kind of getter call = value locs.

This way, a user:

  1. Will immediately realize that the call is incorrect a
  2. Will subsequently realize that a getter functions like kW() is indeed a getter function.
  3. See which API will serve their intended purpose.

It is true that the doc lists PVsystems.kW() as a getter function, but a user like me usually works in this manner:

  1. Check an object's possible API calls (not necessarily read descriptions, kinda like writing PVsytems. and pressing the Tab key)
  2. Try to use object's API call in a manner intuitive to them
  3. Realize that the API call isn't working in a manner that they intended (This is my concern, ODD.jl doesn't seem to throw a loud and clear error message for this case)
  4. Check the documentation for the APIs and realize their fault.

PS: I apologize for the long message which kinda looks like a rant.

kdheepak commented 1 week ago

I don't believe it is possible to throw an error for the getter call = value syntax. This is a Julia language feature.

Writing:

x() = 5

is equivalent to writing

function x()
    return 5
end

See screenshot below for what happens in a fresh session:

image

So PVsystems.kW() = 5 defines a new function kW() in the PVsystems module that will always return 5.

Realife-Brahmin commented 1 week ago

@kdheepak Thank you for your excellent example to clear my misconceptions. I had no idea about this thing in julia (or maybe this is normal in other popular languages too?)

Is it an acceptable thing to be able to attach more functions/fields for a module object from outside the module? (Sorry for my poorly phrased question)

My apologies for blaming ODD.jl for this issue.

PMeira commented 1 week ago

@Realife-Brahmin, you can add --warn-overwrite=yes when calling Julia to make it generate warnings like (this one for Loads.kvar() = 10):

WARNING: Method definition kvar() in module Loads at /home/meira/.julia/packages/OpenDSSDirect/vXmoP/src/loads.jl:422 overwritten in module Main at /home/meira/tmp/test_overwrite.jl:7.

I did see some issues still open in the Julia repo related to that, but no recent updates. For example: https://github.com/JuliaLang/julia/issues/15602

Like @kdheepak wrote, that's more a Julia feature (maybe missing some control) than an issue with ODD.jl. In Python/ODD.py, we had a similar issue in the past, and now it does generate errors: we added classes, keeping the modules for backwards compat -- we now have far fewer surprised users there. Maybe something similar could be done here -- keep the current modules, and add structures to allow more control. I'm not sure if it would fine well with the rest.

For #82, maybe we could test some alternatives, like using structs and defining custom setproperty! and getproperty, even if it seems to be generally discouraged in Julia. Otherwise, it will be kinda hard to distinguish between a DSS property and the actual functions, but the docstrings should help.

Regarding this:

# Why Pmpp? Why not kW? From docs, calling kW as nominal value unfortunately doesn't clarify it for me.

We mirror what's done in the official OpenDSS API, so we made no decision there: https://opendss.epri.com/Pmpp.html Pmpp is also a DSS property: https://dss-extensions.org/dss-format/PVSystem.html

Why not kW? If you want to manipulate the power directly, you probably shouldn't be using PVSystem, since it's main functionally is to couple some quantities (irradiance, efficiency, temperature) and related curves with some form of control to compute the powers (kW and kvar). Using a generator or even a negative load is a simpler alternative to model PV generation in some contexts; it all depends on the study and what input data you have available.

If you want to change the size of the PVSystem, you should use kVA (DSS property) or kVARated in Julia, combined with Pmpp. These two are parameters to the inverter model; typically the kVA, rated power, is a bit above Pmpp -- check some examples and the official OpenDSS docs.