Skip to content
Merged
82 changes: 42 additions & 40 deletions ORBIT/phases/design/array_system_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
import numpy as np
import pandas as pd

try:
import matplotlib.pyplot as plt
except ModuleNotFoundError:
pass

from ORBIT.core.library import export_library_specs, extract_library_specs
from ORBIT.core.exceptions import LibraryItemNotFoundError
from ORBIT.phases.design._cables import Plant, CableSystem
Expand Down Expand Up @@ -438,7 +433,13 @@ def save_layout(self, save_name, return_df=False, folder="cables"):

coords = np.array(
[[0, 0]]
+ list(zip(self.turbines_x.flatten(), self.turbines_y.flatten()))
+ list(
zip(
self.turbines_x.flatten(),
self.turbines_y.flatten(),
strict=False,
)
)
)
coords = coords[: self.system.num_turbines + 1]
layout_df[["longitude", "latitude"]] = coords
Expand Down Expand Up @@ -544,6 +545,14 @@ def plot_array_system(
settings, and can be manipulated to add annotations, or other
elements.
"""
try:
import matplotlib.pyplot as plt
except ModuleNotFoundError as err:
msg = (
"Please install ORBIT with the `plot` option or install"
" `matplotlib` manually."
)
raise ModuleNotFoundError(msg) from err

fig, ax = plt.subplots(figsize=(10, 10))
plt.axis("off")
Expand All @@ -560,35 +569,20 @@ def plot_array_system(
zorder=2,
label="Turbine",
)
# Plot the turbine names
# for i in range(self.coordinates.shape[0]):
# for j in range(self.coordinates.shape[1] - 1):
# if not np.any(np.isnan(self.coordinates[i, j + 1])):
# x, y = self.coordinates[i, j + 1]
# name = self.location_data.loc[
# (self.location_data.string == i)
# & (self.location_data.order == j),
# "turbine_name"
# ].to_numpy()[0]
# ax.text(x, y, name)

# Determine the cable section widths
string_sets = np.unique(
[
list(
OrderedDict.fromkeys(el for el in cables if el is not None)
)
for cables in self.sections_cables
]
)
string_sets = {
tuple(OrderedDict.fromkeys(el for el in cables if el is not None))
for cables in self.sections_cables
}
string_sets = [list(el) for el in string_sets]
if isinstance(string_sets[0], list):
max_string = max(string_sets, key=len)
else:
max_string = string_sets[::-1]
string_widths = np.arange(len(max_string), 0, -1, dtype=float).tolist()

for i, row in enumerate(self.sections_cables):
for cable, width in zip(max_string, string_widths):
for cable, width in zip(max_string, string_widths, strict=False):

ix_to_plot = np.where(row == cable)[0]
if ix_to_plot.size == 0:
Expand Down Expand Up @@ -662,7 +656,7 @@ class CustomArraySystemDesign(ArraySystemDesign):

expected_config = {
"site": {"depth": "str"},
"plant": {"layout": "str", "num_turbines": "int"},
"plant": {"layout": "str | pd.DataFrame", "num_turbines": "int"},
"turbine": {"turbine_rating": "int | float"},
"array_system_design": {
"cables": "list | str",
Expand Down Expand Up @@ -869,26 +863,33 @@ def _format_windfarm_data(self):
def _initialize_custom_data(self):
windfarm = self.config["array_system_design"]["location_data"]

try:
self.location_data = extract_library_specs(
"cables",
windfarm,
file_type="csv",
)
except LibraryItemNotFoundError:
self.location_data = extract_library_specs(
"plant",
windfarm,
file_type="csv",
if isinstance(windfarm, str):
try:
self.location_data = extract_library_specs(
"cables",
windfarm,
file_type="csv",
)
except LibraryItemNotFoundError:
self.location_data = extract_library_specs(
"plant",
windfarm,
file_type="csv",
)
elif isinstance(windfarm, pd.DataFrame):
self.location_data = windfarm
else:
raise ValueError(
"`location_data` must be a filename or DataFrame."
)

# Make sure no data is missing
missing = set(self.COLUMNS).difference(self.location_data.columns)
if missing:
raise ValueError(
"The following columns must be included in the location "
f"data: {missing}",
)

self._format_windfarm_data()

# Ensure there is no missing data in required columns
Expand Down Expand Up @@ -1110,6 +1111,7 @@ def cable_lengths_by_type_speed(self):
zip(
self.sections_cable_lengths[self.sections_cables == name],
self.sections_bury_speeds[self.sections_cables == name],
strict=False,
)
)
for name in self.cables
Expand Down
8 changes: 7 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ Instructions

# OR if you are you going to be contributing to the code or building documentation
pip install -e '.[dev]'

# OR if you wish to use native plotting tools
pip install -e '.[plot]'

6. (Development only) Install the pre-commit hooks to autoformat and lint code.

.. code-block:: console
Expand All @@ -83,7 +87,6 @@ Dependencies
- NumPy
- Pandas
- SciPy
- Matplotlib
- OpenMDAO (>=3.2)
- python-benedict
- statsmodels
Expand All @@ -101,6 +104,9 @@ Development Specific
- sphinx
- sphinx-rtd-theme

Optional Plotting
~~~~~~~~~~~~~~~~~
- matplotlib

Recommended packages for easy iteration and running of code:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
8 changes: 8 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

ORBIT Changelog
===============

Unreleased
----------
- Allow for a Pandas DataFrame to be passed directly to the ``CustomArraySystemDesign.layout_data``
configuration input.
- Move the matplotlib import from the import section of ``/ORBIT/phases/design/array_system_design.py``
to the ``CustomArraySystemDesign.plot_array_system`` for missing module error handling.

1.2.4
-----
- Support Python 3.14
Expand Down
3 changes: 3 additions & 0 deletions docs/source/phases/design/api_ArraySystemDesign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ For detailed methodology, please see

.. autoclass:: ORBIT.phases.design.ArraySystemDesign
:members:

.. autoclass:: ORBIT.phases.design.CustomArraySystemDesign
:members:
Loading