Skip to content
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

Merged
merged 18 commits into from
Apr 10, 2019
Merged

Conversation

veronicaguo
Copy link
Collaborator

No description provided.

@coveralls
Copy link

coveralls commented Apr 4, 2019

Pull Request Test Coverage Report for Build 116

  • 48 of 48 (100.0%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+4.2%) to 87.5%

Totals Coverage Status
Change from base Build 79: 4.2%
Covered Lines: 126
Relevant Lines: 144

💛 - Coveralls

@veronicaguo veronicaguo force-pushed the support-concentric-neutral-cables branch from fca708f to 7ffb2b0 Compare April 4, 2019 21:16
@veronicaguo veronicaguo assigned veronicaguo and unassigned AnjoMan Apr 4, 2019
Copy link
Contributor

@etimberg etimberg left a 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:

  1. Single/two phase cables
  2. Cables with another neutral, ie phases of A, B, C, N, AN, BN, CN
  3. Input validation for cases that are not supported

It might also be good to add some documentation on what the inputs are

else:
return distance_ij

def compute_P(self, i, j, number_of_terms=1):
Copy link
Contributor

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?

Copy link
Collaborator Author

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dropped it here 53fa95f

@@ -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):
Copy link
Contributor

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

Copy link
Member

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.

Copy link
Collaborator Author

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

tests/test_concentric_neutral_cable.py Show resolved Hide resolved
carsons/carsons.py Outdated Show resolved Hide resolved
Copy link
Contributor

@etimberg etimberg left a 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):
Copy link
Contributor

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

Copy link
Collaborator Author

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 by 24 to include unit conversion from inch to ft. We handle everything using metric units, so dividing by 2 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.

Copy link
Contributor

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):
Copy link
Contributor

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.

@etimberg etimberg merged commit 0ba5498 into master Apr 10, 2019
@etimberg etimberg deleted the support-concentric-neutral-cables branch October 27, 2019 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants