-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support concentric neutral cables #16
Conversation
Pull Request Test Coverage Report for Build 116
💛 - Coveralls |
fca708f
to
7ffb2b0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the tests should include:
- Single/two phase cables
- Cables with another neutral, ie phases of
A, B, C, N, AN, BN, CN
- Input validation for cases that are not supported
It might also be good to add some documentation on what the inputs are
carsons/carsons.py
Outdated
else: | ||
return distance_ij | ||
|
||
def compute_P(self, i, j, number_of_terms=1): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should number_of_terms
be passed through here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like it's exactly the same as in CarsonsEquations
class... thinking of dropping it here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dropped it here 53fa95f
carsons/carsons.py
Outdated
@@ -183,3 +181,75 @@ def calculate_distance(positionᵢ, positionⱼ): | |||
def get_h(self, i): | |||
_, yᵢ = self.phase_positions[i] | |||
return yᵢ | |||
|
|||
@property | |||
def impedance(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this new property should be tested. perhaps also deprecate the convert_geometric_model
function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is called on the concentric neutral. its just added here because it doesn't depend on anything that Carsons
doesn't have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exposed impedance
as a stand-alone function in 723ff87
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some analysis of this code versus the underground cable impedance implementation in gridlab-d. There appear to be significant differences in the computation of the distances between phases.
It was hard to audit further since gridlab-d computes the Z matrix directly and not through carson's equations.
}) | ||
return | ||
|
||
def compute_d(self, i, j): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comparison to Gridlab-d
To try and find the issues with the impedance calculations, I audited the gridlab-d source. I think this method differs from the Gridlab-d distance calculations in the following cases.
Background
Gridlab-d appears to use the following indexes for different phases. The snippets below use these phase indexes
Index | Description |
---|---|
1 | Phase A |
2 | Phase B |
3 | Phase C |
4 | Concentric Neutral A |
5 | Concentric Neutral B |
6 | Concentric Neutral C |
7 | Neutral Conductor |
Conductor to Neutral Cable
Python Implementation
In this case, the inputs are I = {'A'}, J = {'N'}
This leads to the following:
I ^ J = {'A', 'N'}
I & J = {}
one_neutral_same_phase == False
different_phase == True
one_neutral == True
Thus the code passed the check on line 233 and returns (distance_ij**2 + r**2) ** 0.5
.
Gridlab-D Implementation
However, gridlab-d does the following in the case of A -> N:
#define DIST(ph1, ph2) (has_phase(PHASE_##ph1) && has_phase(PHASE_##ph2) && config->line_spacing ? OBJECTDATA(config->line_spacing, line_spacing)->distance_##ph1##to##ph2 : 0.0)
D(1, 7) = DIST(A, N);
Solution
I think the correct solution in this case is to return distance_ij
Conductor to Own Concentric Neutral
Python Implementation
In this case, the inputs are I = {'A'}, J = {'A', 'N'}
This leads to the following:
I ^ J = {'N'}
I & J = {'A}
one_neutral_same_phase == True
different_phase == False
one_neutral == True
Thus the check on line 227 is true, and so r
is returned. This value was calculated earlier on lines 200 and 201 as
(diameter_over_neutral - model.neutral_strand_diameter[phase]) / 2
Gridlab-d Distance
Gridlab-d does the following:
dia_od1 = UG_GET(A, outer_diameter);
DIA(4) = UG_GET(A, neutral_diameter);
rad_14 = (dia_od1 - DIA(4)) / 24.0;
D(1, 4) = rad_14;
Solution
Divide by 24 on line 201 instead of 2. Additionally, check that the gridlab-d outer diameter is the diameter over the neutral.
Conductor to Different Phase Concentric Neutral
Python Implementation
In this case, the inputs are I = {'A'}, J = {'B', 'N'}
This leads to the following:
I ^ J = {'A', 'B', 'N'}
I & J = {}
one_neutral_same_phase == False
different_phase == True
one_neutral == True
Thus the check on line 233 is True, and so the python code returns (distance_ij**2 + r**2) ** 0.5
Gridlab-d Implementation
D(1, 5) = D(1, 2);
In otherwords, the distance from A -> BN is the same as the distance from A -> B.
Concentric Neutral to Another Concentric Neutral
Python Implementation
In this case, the inputs are I = {'A', 'N'}, J = {'B', 'N'}
This leads to the following:
I ^ J = {'A', 'B'}
I & J = {'N'}
one_neutral_same_phase == False
different_phase == False
one_neutral == False
Thus the check on line 227 and the check on line 233 are False and so distance_ij
is returned
Gridlab-d Implementation
D(4, 5) = D(1, 2);
In other words, the distance from AN -> BN is the same as the distance from A -> B. The code appears to be correct here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the thorough audit @etimberg ! A couple points here:
-
For case
Conductor to Own Concentric Neutral
, I think gridlab-d divides by24
to include unit conversion frominch
toft
. We handle everything using metric units, so dividing by2
to get radius from diameter. -
For case
Conductor to Different Phase Concentric Neutral
, our implementation follows the assumption and example given in the Kersting's book, approximating the concentric neutrals as one single wire directly above their respective phase conductors. So there comes the Pythagorean theorem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense @veronicaguo re 2 instead of 24. For the Conductor to Different Phase Concentric Neutral
case, I'm not sure that the distance calculation is correct. It assumes all the cables are in one horizontal plane because that's the only way the triangle becomes right angle. If the cables were arranged in a geometry similar to this image, the distance from the top conductor to one of the bottom concentric neutrals is formed by a non right triangle and so the extra term from the cosine law would need to be added.
Maybe a way to simplify the code here is to calculate the position (x,y) of the concentric neutral in a prior step. Then, all you'd do here is lookup distance_ij
. It might be worthwhile testing this function independently too for the following cases:
- Horizontal geometry
- Vertical geometry
- Bundled like the image above
|
||
return (X_o + ΔX) * self.ω * self.μ / (2 * π) | ||
|
||
def GMR_cn(self, phase): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I audited this against gridlab-d. The gridlab-d implementation is shown below
GMRCN(4) = !(has_phase(PHASE_A) && strands_4 > 0) ? 0.0 : pow(GMR(4) * strands_4 * pow(rad_14, (strands_4 - 1)), (1.0 / strands_4));
Differences
- Does not bail out early in the 0 case. Perhaps check
GMR_s
? R
is incorrect per the comment on the distance function.
No description provided.