Skip to content

Commit

Permalink
Enhancement: Add more inline commentary (#148)
Browse files Browse the repository at this point in the history
* add inline comments for turbine, substation, and cable creation

* add logging comments

* update changelog
  • Loading branch information
RHammond2 authored Jun 27, 2024
1 parent ec8c3ef commit 9fa829e
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Adds a `replacement` flag for interruption methods, so that a failure or replacement comment can be added as a cause for `simpy.process.interrupt`. This update allows the failure and maintenance processes to check if an interruption should cause the process to exit completely. Additionally, the forced exit ensures that processes can't persist after a replacement event when a process is recreated, which was happening in isolated cases.
- Fixes a bug in `RepairManager.purge_subassemble_requests()` where the pending tows are cleared regardless of whether or not the focal subassembly is the cause of the tow, leading to a simulation failure.
- Fixes a bug in `utilities/utilities.py:create_variable_from_string()` to operate in a way that is expected. The original method was removing all numerics, but only leading punctuation and numerics should be replaced, with any punctuation being replaced with an underscore.
- Adds additional inline comments for clarification on internal methods.

## v0.9.3 (15 February 2024)

Expand Down
30 changes: 28 additions & 2 deletions wombat/windfarm/windfarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def _create_graph_layout(self, windfarm_layout: str) -> None:
windfarm_layout : str
Filename to use for reading in the windfarm layout; must be a csv file.
"""
# Read in the layout CSV file, then sort it by string, then order to ensure
# it can be traversed in sequential order later
layout_path = str(self.env.data_dir / "project/plant" / windfarm_layout)
layout = (
pd.read_csv(layout_path)
Expand All @@ -83,6 +85,7 @@ def _create_graph_layout(self, windfarm_layout: str) -> None:

# Determine which nodes are substations and which are turbines
if "type" in layout.columns:
# Extract the type directly from the layout file
if layout.loc[~layout.type.isin(("substation", "turbine"))].size > 0:
raise ValueError(
"At least one value in the 'type' column are not one of:"
Expand All @@ -93,24 +96,30 @@ def _create_graph_layout(self, windfarm_layout: str) -> None:
windfarm, dict(layout[["id", "type"]].values), name="type"
)
else:
# Deduce substations by their self-connected setting for interconnection
substation_filter = layout.id == layout.substation_id
_type = {True: "substation", False: "turbine"}
d = {i: _type[val] for i, val in zip(layout.id, substation_filter.values)}
nx.set_node_attributes(windfarm, d, name="type")

self.turbine_id: np.ndarray = layout.loc[~substation_filter, "id"].values

self.substation_id = layout.loc[substation_filter, "id"].values
for substation in self.substation_id:
windfarm.nodes[substation]["connection"] = layout.loc[
layout.id == substation, "substation_id"
].values[0]

self.turbine_id: np.ndarray = layout.loc[~substation_filter, "id"].values
# Create a mapping for each substation to all connected turbines (subgraphs)
substations = layout[substation_filter].copy()
turbines = layout[~substation_filter].copy()
substation_sections = [
turbines[turbines.substation_id == substation]
for substation in substations.id
]

# For each subgraph, create the edge connections between substations and
# turbines. Note: these are pre-sorted in the layout creation step.
for section in substation_sections:
for _, row in section.iterrows():
if row.order == 0:
Expand All @@ -121,6 +130,7 @@ def _create_graph_layout(self, windfarm_layout: str) -> None:
windfarm.add_edge(
start, current, length=row.distance, cable=row.upstream_cable
)
# Create the substation to substation and substation to self connections
for substation in self.substation_id:
row = layout.loc[layout.id == substation]
windfarm.add_edge(
Expand All @@ -143,6 +153,7 @@ def _create_turbines_and_substations(self) -> None:
ValueError
Raised if the subassembly data is not provided in the layout file.
"""
# Loop through all nodes in the graph, and create the actual simulation objects
for system_id, data in self.graph.nodes(data=True):
name = data["subassembly"]
node_type = data["type"]
Expand All @@ -152,10 +163,13 @@ def _create_turbines_and_substations(self) -> None:
" windfarm layout!"
)

# Read in unique system configuration files only once, and reference
# the existing dictionary when possible to reduce I/O
if (subassembly_dict := self.configs[node_type].get(name)) is None:
subassembly_dict = load_yaml(self.env.data_dir / f"{node_type}s", name)
self.configs[node_type][name] = subassembly_dict

# Create the turbine or substation simulation object
self.graph.nodes[system_id]["system"] = System(
self.env,
self.repair_manager,
Expand All @@ -177,6 +191,8 @@ def _create_cables(self) -> None:
"""
get_name = "upstream_cable_name" in self.layout_df
bad_data_location_messages = []

# Loop over all the edges in the graph and create cable objects
for start_node, end_node, data in self.graph.edges(data=True):
name = data["cable"]

Expand All @@ -186,6 +202,8 @@ def _create_cables(self) -> None:
"An 'upstream_cable' file must be specified for all nodes in the"
" windfarm layout!"
)

# Read in unique cable configuration files once to reduce I/O
if (cable_dict := self.configs["cable"].get(name)) is None:
try:
cable_dict = load_yaml(self.env.data_dir / "cables", data["cable"])
Expand All @@ -199,6 +217,7 @@ def _create_cables(self) -> None:
)
self.configs["cable"][name] = cable_dict

# Get the lat, lon pairs for the start and end points
start_coordinates = (
self.graph.nodes[start_node]["latitude"],
self.graph.nodes[start_node]["longitude"],
Expand All @@ -208,6 +227,7 @@ def _create_cables(self) -> None:
self.graph.nodes[end_node]["longitude"],
)

# Get the unique naming of the cable connection if it's configured
name = None
if get_name:
name, *_ = self.layout_df.loc[
Expand All @@ -221,16 +241,19 @@ def _create_cables(self) -> None:
start_coordinates, end_coordinates, ellipsoid="WGS-84"
).km

# Encode whether it is an array cable or an export cable
if self.graph.nodes[end_node]["type"] == "substation":
data["type"] = "export"
else:
data["type"] = "array"

# Create the Cable simulation object
data["cable"] = Cable(
self, self.env, data["type"], start_node, end_node, cable_dict, name
)

# Calaculate the geometric center point
# Calaculate the geometric center point of the cable for later
# determining travel distances to cables
end_points = np.array((start_coordinates, end_coordinates))
data["latitude"], data["longitude"] = end_points.mean(axis=0)

Expand Down Expand Up @@ -381,6 +404,9 @@ def _log_operations(self):

HOURS = 1
while True:
# Loop 10K times, to limit the number of times we write to the operations
# log file. 10K was a crude optimization decision, so performance can vary
# dependending on the simulation
for _ in range(10000):
yield self.env.timeout(HOURS)
message = {
Expand Down

0 comments on commit 9fa829e

Please sign in to comment.