feat: add loadability calculations and standard conductor parameters#272
feat: add loadability calculations and standard conductor parameters#272danielolsen merged 11 commits intodevelopfrom
Conversation
150042e to
203a2eb
Compare
|
Here's a usage example, showing how we can leverage the new functionality to automatically generate the parameters needed for Grid-building: Assume we have a CSV data table of representative line designs:
The first three represent single-pole designs (phases oriented primarily vertically, two on one side and one on the other), while the other represent H-frame or lattice designs (phases oriented horizontally). All distances are measured in meters, with the center of the tower at ground level representing the origin. We can build a function that interprets each row of this table into a def build_tower(series):
"""Build a Tower object using the transmission line design"""
conductor = Conductor(series["conductor"])
if series["bundle_count"] != 1:
bundle = ConductorBundle(
conductor=conductor,
n=series["bundle_count"],
spacing=series["spacing"],
)
else:
bundle = ConductorBundle(conductor=conductor) # n = 1 by default
locations = PhaseLocations(
a=tuple(series[["a_x", "a_y"]]),
b=tuple(series[["b_x", "b_y"]]),
c=tuple(series[["c_x", "c_y"]]),
)
tower = Tower(bundle=bundle, locations=locations)
return towerThen, we can define a function which calculates per-mile parameter values: def calculate_per_mile_parameters(series):
z_base = series["voltage"]**2 / 100 # 100 MVA base
tower = build_tower(series)
line = Line(tower=tower, voltage=series["voltage"], length=1.609) # km in 1 mile
output = pd.Series(
{
"reactance_per_mile": line.series_impedance.imag / z_base,
"thermal_rating": line.thermal_rating,
"surge_impedance_loading": line.surge_impedance_loading,
}
)
return outputand one that calculates the rating for a 100-mile long line: def calculate_100_mile_rating(series):
tower = build_tower(series)
line = Line(tower=tower, voltage=series["voltage"], length=160.9) # km in 100 mile
return line.power_ratingPutting these into action: >>> import pandas as pd
>>> from prereise.gather.griddata.transmission.geometry import (
... Conductor,
... ConductorBundle,
... Line,
... PhaseLocations,
... Tower,
... )
>>> data = pd.read_csv("path/to/line_designs.csv")
>>> one_mile_parameters = data.apply(calculate_per_mile_parameters, axis=1)
>>> # Add a meaningful index to the new calculations, using some of the original columns
>>> index_for_display = data.set_index(["voltage", "bundle_count"]).index
>>> one_mile_parameters.index = index_for_display
>>> one_mile_parameters
reactance_per_mile thermal_rating surge_impedance_loading
voltage bundle_count
69 1 0.015501 85.450727 13.113853
115 1 0.005447 183.250975 37.534569
138 1 0.003941 250.974162 52.098497
230 1 0.001448 551.744785 142.092507
2 0.001092 733.003902 187.338069
3 0.000947 1099.505853 215.762241
345 1 0.000649 960.274948 318.173207
2 0.000482 1655.234354 425.428999
500 3 0.000217 2585.085830 941.144695
765 4 0.000092 6651.594716 2225.035726
>>> rating_100_mile = data.apply(calculate_100_mile_rating, axis=1)
>>> rating_100_mile.index = index_for_display
>>> rating_100_mile
voltage bundle_count
69 1 26.199249
115 1 74.987693
138 1 104.083946
230 1 283.876688
2 374.269634
3 431.056300
345 1 635.656013
2 849.934862
500 3 1880.247205
765 4 4445.243361
dtype: float64As would be expected, the rating for a line of about 100 miles is less than the thermal rating, and about 2x the surge impedance loading, matching the St. Clair curve. To build a full Grid, we just need to associate each branch with a representative tower design, and then we can use that line's specific length to calculate the allowable power transfer, which will always be the lower of the thermal limit or the stability limit. Extending the tower-building function to multiple circuits on a tower is fairly trivial: we can follow the same procedure as with single vs. multiple conductors, where one column tells us which other columns we should be looking at to build the appropriate |
203a2eb to
167d47a
Compare
|
Reading the code, it looks like a conductor bundle is made of the same type of conductor. Is that what happens in reality or a this is use to simplify the model? |
I believe this is true to real life: I've never seen reference to a heterogeneous conductor bundle. There are a few other implicit assumptions that I think are more often violated in real life, to varying degrees:
I think these assumptions are all reasonable though, since to account for them would require more complicated logic and/or data that is unavailable or very difficult to collect, and would probably not make a large impact on the overall calculation results. |
|
In the |
We are indeed working with complex impedance. Properly calculating the impedance of long-distance transmission lines requires doing calculations which combine series resistance (real), series reactance (imaginary), and shunt admittance (imaginary).
|
Thanks for the explanation. The other option is to take the real part of the output: Not very important |
rouille
left a comment
There was a problem hiding this comment.
I did not go too far into the calculations of the conductor(s) parameters but the logic and the tests look good
35e91b1 to
1686bec
Compare
Pull Request doc
Purpose
What the code is doing
approximate_loadabilityadds an interface to use line length to estimate loadability. The design is flexible enough to be able to incorporate additional methods, in case we want to enable direct calculation of the St. Clair curve for a given set of per-length impedances.get_standard_conductorsis a small helper function which reads the standard conductor parameters from the new CSV and returns a dataframe.geometrymodule:get_standard_conductorsis used within the instantiation of aConductor: we can now specify conductors by code name, rather than physical parameter values, and all relevant physical parameters will be populated. This is also used to simplify the tests a bit.Line.power_ratingparameter for aLineis set based on the smaller of the thermal power rating and the loadability calculated using the approximated St. Clair curve.Testing
One new unit test is added, and existing unit tests are enhanced.
Usage Example/Visuals
Besides being able to instantiate a
Conductorby name (e.g.c = Conductor("Ostrich")), there are no other changes to how the objects are used. The only difference is that additional attributes are available.Time estimate
30 minutes? There calculations that the code is doing aren't very complex, but probably only make sense to the folks with a power systems background.