Summary
Lines.R1(), Lines.X1(), and Lines.C1() (and the equivalent LineCodes.* properties) are symmetric-component fields. They only reflect reality when a line/LineCode was defined via r1=/x1=/c1=. Once a line/LineCode is switched to a matrix definition (rmatrix=/xmatrix=/cmatrix=), those fields are never cleared or recomputed -- they silently hold whatever they were constructed with (R1 := 0.0580, X1 := 0.1206, per TLineObj/TLineCodeObj construction in the original Pascal source), and the direct numeric getters return that stale value with no indication anything is wrong.
This is distinct from #36 (which is about a scaling inconsistency between the RMatrix/XMatrix getter and setter) -- this is about R1/X1/C1 becoming meaningless, not mis-scaled, once matrix mode is active.
Reproduction
New Circuit.test basekv=0.4 pu=1.0 phases=3
New LineCode.lc1 nphases=3 baseFreq=50 units=km
~ rmatrix=[0.868 | 0.1 0.868 | 0.1 0.1 0.868]
~ xmatrix=[0.077 | 0.01 0.077 | 0.01 0.01 0.077]
New Line.l1 bus1=a.1.2.3 bus2=b.1.2.3 phases=3 linecode=lc1 length=1 units=km
import opendssdirect as dss
dss.Command("...redirect the above...")
dss.Lines.Name("l1")
dss.Lines.R1(), dss.Lines.X1()
# -> (0.058, 0.1206) -- the hard-coded construction default, not 0.868/0.077 -- or
# anything derivable from the declared matrix.
Confirmed via the Pascal source that the text-based property interface handles this correctly -- querying the R1 property by name returns '----' once SymComponentsModel is False (see LineCode.pas: If SymComponentsModel Then Result := Format(...) else Result := '----'). The direct numeric getter used by the C-API/OpenDSSDirect.py doesn't perform the same check, so there is no way to tell, from the numeric API alone, that the returned value is meaningless.
Impact
Any matrix-defined line, any conductor count -- not neutral- or phase-count-specific. We hit this while validating a pandapower OpenDSS importer against a real utility network (an explicit-neutral 4-conductor European LineCode looked like a Kron-reduction issue at first; a plain 3-conductor matrix LineCode with no neutral at all reproduced the identical stale value), which is what led us to check the Pascal source. Any tool that reads Lines.R1()/X1()/C1() directly (rather than going through the text/property-name interface, or manually deriving from RMatrix()/XMatrix()/CMatrix()) on a matrix-defined line will silently get wrong data with no error, warning, or way to detect it via the numeric API.
Suggested fix
Given #36 was closed as not-planned (presumably to avoid changing numeric behavior that might already be relied upon / for EPRI-COM-compatibility reasons), I'd guess changing what R1()/X1()/C1() themselves return is a similarly hard sell. Two lower-risk options that don't touch existing behavior:
- Document it on the
Lines.R1/X1/C1 (and LineCode.R1/X1/C1) properties: "meaningless once the line/LineCode has been defined via a matrix; check SymComponentsModel" (or equivalent) -- currently there's no hint of this anywhere a numeric-API user would see it.
- Expose
SymComponentsModel as a new read-only boolean getter (e.g. Lines.SymComponentsModel() / LineCodes.SymComponentsModel()). Purely additive, no behavior change, and it would let API consumers detect the unsafe case themselves and fall back to RMatrix()/XMatrix()/CMatrix().
Happy to help test either, or just to have this documented if that's the preferred resolution.
Summary
Lines.R1(),Lines.X1(), andLines.C1()(and the equivalentLineCodes.*properties) are symmetric-component fields. They only reflect reality when a line/LineCode was defined viar1=/x1=/c1=. Once a line/LineCode is switched to a matrix definition (rmatrix=/xmatrix=/cmatrix=), those fields are never cleared or recomputed -- they silently hold whatever they were constructed with (R1 := 0.0580,X1 := 0.1206, perTLineObj/TLineCodeObjconstruction in the original Pascal source), and the direct numeric getters return that stale value with no indication anything is wrong.This is distinct from #36 (which is about a scaling inconsistency between the
RMatrix/XMatrixgetter and setter) -- this is aboutR1/X1/C1becoming meaningless, not mis-scaled, once matrix mode is active.Reproduction
Confirmed via the Pascal source that the text-based property interface handles this correctly -- querying the
R1property by name returns'----'onceSymComponentsModelisFalse(seeLineCode.pas:If SymComponentsModel Then Result := Format(...) else Result := '----'). The direct numeric getter used by the C-API/OpenDSSDirect.py doesn't perform the same check, so there is no way to tell, from the numeric API alone, that the returned value is meaningless.Impact
Any matrix-defined line, any conductor count -- not neutral- or phase-count-specific. We hit this while validating a pandapower OpenDSS importer against a real utility network (an explicit-neutral 4-conductor European LineCode looked like a Kron-reduction issue at first; a plain 3-conductor matrix LineCode with no neutral at all reproduced the identical stale value), which is what led us to check the Pascal source. Any tool that reads
Lines.R1()/X1()/C1()directly (rather than going through the text/property-name interface, or manually deriving fromRMatrix()/XMatrix()/CMatrix()) on a matrix-defined line will silently get wrong data with no error, warning, or way to detect it via the numeric API.Suggested fix
Given #36 was closed as not-planned (presumably to avoid changing numeric behavior that might already be relied upon / for EPRI-COM-compatibility reasons), I'd guess changing what
R1()/X1()/C1()themselves return is a similarly hard sell. Two lower-risk options that don't touch existing behavior:Lines.R1/X1/C1(andLineCode.R1/X1/C1) properties: "meaningless once the line/LineCode has been defined via a matrix; checkSymComponentsModel" (or equivalent) -- currently there's no hint of this anywhere a numeric-API user would see it.SymComponentsModelas a new read-only boolean getter (e.g.Lines.SymComponentsModel()/LineCodes.SymComponentsModel()). Purely additive, no behavior change, and it would let API consumers detect the unsafe case themselves and fall back toRMatrix()/XMatrix()/CMatrix().Happy to help test either, or just to have this documented if that's the preferred resolution.