Merge pull request 'floris_design' (#7) from floris_design into main
Reviewed-on: #7
This commit is contained in:
commit
0aafeda77d
@ -1,7 +1,9 @@
|
|||||||
|
import math
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
from floris import FlorisModel
|
from floris import FlorisModel
|
||||||
from floris.flow_visualization import visualize_cut_plane
|
from floris.flow_visualization import visualize_cut_plane
|
||||||
@ -10,7 +12,6 @@ import floris.layout_visualization as layoutviz
|
|||||||
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
_wind_direction_to_val: dict[str, float] = {
|
_wind_direction_to_val: dict[str, float] = {
|
||||||
"N": 0.0,
|
"N": 0.0,
|
||||||
"E": 90.0,
|
"E": 90.0,
|
||||||
@ -53,22 +54,90 @@ class FlorisULSTU(FlorisModel):
|
|||||||
|
|
||||||
super().set(**kwargs)
|
super().set(**kwargs)
|
||||||
|
|
||||||
def visualisation(self):
|
def visualization(
|
||||||
|
self,
|
||||||
|
interest_plots: Set[str]
|
||||||
|
):
|
||||||
|
dct_functions = {
|
||||||
|
"horizontal_plane": self._horizontal_plane_visualization,
|
||||||
|
"vertical_plane": self._vertical_plane_visualization,
|
||||||
|
}
|
||||||
|
|
||||||
|
res = {}
|
||||||
|
|
||||||
self.reset_operation()
|
for param in interest_plots:
|
||||||
|
if param not in dct_functions:
|
||||||
|
res[param] = "Not found"
|
||||||
|
continue
|
||||||
|
res[param] = dct_functions[param]()
|
||||||
|
|
||||||
horizontal_plane = self.calculate_horizontal_plane(height=90.0)
|
return res
|
||||||
visualize_cut_plane(
|
|
||||||
horizontal_plane,
|
def _horizontal_plane_visualization(self):
|
||||||
)
|
return self._plane_visualization(self.calculate_horizontal_plane, {"height": 90.0})
|
||||||
|
|
||||||
|
def _vertical_plane_visualization(self):
|
||||||
|
return self._plane_visualization(self.calculate_y_plane,
|
||||||
|
{"crossstream_dist": 0.0,
|
||||||
|
"x_resolution": 200,
|
||||||
|
"z_resolution": 100})
|
||||||
|
|
||||||
|
def _plane_visualization(self, plane_func, func_params: dict[str, float] = {}):
|
||||||
|
wind_dirs = self.core.flow_field.wind_directions
|
||||||
|
wind_speeds = self.core.flow_field.wind_speeds
|
||||||
|
|
||||||
|
experiment_counts = len(wind_dirs)
|
||||||
|
near_square = math.ceil(math.sqrt(experiment_counts))
|
||||||
|
fig, ax_arr = plt.subplots(near_square, near_square, figsize=(15, 8))
|
||||||
|
ax_arr = ax_arr.flatten()
|
||||||
|
|
||||||
|
for experiment_count in range(experiment_counts):
|
||||||
|
self.reset_operation()
|
||||||
|
self.set(wind_speeds=[wind_speeds[experiment_count]], wind_directions=[wind_dirs[experiment_count]],
|
||||||
|
turbulence_intensities=[0.05])
|
||||||
|
ax = ax_arr[experiment_count]
|
||||||
|
plane = plane_func(**func_params)
|
||||||
|
visualize_cut_plane(
|
||||||
|
plane,
|
||||||
|
ax=ax,
|
||||||
|
title=f"Wind flow day {experiment_count + 1}",
|
||||||
|
)
|
||||||
|
|
||||||
# plot_turbine_labels(self, axarr[0, 0])
|
|
||||||
filename = str(uuid.uuid4()) + ".png"
|
filename = str(uuid.uuid4()) + ".png"
|
||||||
plt.savefig(Path(__file__).parent.parent.parent / f"public/floris/{filename}")
|
plt.savefig(Path(__file__).parent.parent.parent / f"public/floris/{filename}")
|
||||||
plt.close()
|
plt.close()
|
||||||
|
|
||||||
|
self.set(wind_speeds=wind_speeds, wind_directions=wind_dirs, turbulence_intensities=[0.05] * len(wind_dirs))
|
||||||
|
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def _cross_visualization(self, plane_func, func_params: dict[str, float] = {}):
|
||||||
|
wind_dirs = self.core.flow_field.wind_directions
|
||||||
|
wind_speeds = self.core.flow_field.wind_speeds
|
||||||
|
|
||||||
|
|
||||||
|
experiment_counts = len(wind_dirs)
|
||||||
|
near_square = math.ceil(math.sqrt(experiment_counts))
|
||||||
|
fig, ax_arr = plt.subplots(near_square, near_square, figsize=(15, 8))
|
||||||
|
ax_arr = ax_arr.flatten()
|
||||||
|
|
||||||
|
for experiment_count in range(experiment_counts):
|
||||||
|
self.reset_operation()
|
||||||
|
self.set(wind_speeds=[wind_speeds[experiment_count]], wind_directions=[wind_dirs[experiment_count]],
|
||||||
|
turbulence_intensities=[0.05])
|
||||||
|
ax = ax_arr[experiment_count]
|
||||||
|
plane = plane_func(**func_params)
|
||||||
|
visualize_cut_plane(
|
||||||
|
plane,
|
||||||
|
ax=ax,
|
||||||
|
title=f"Wind flow day {experiment_count + 1}",
|
||||||
|
)
|
||||||
|
|
||||||
|
filename = str(uuid.uuid4()) + ".png"
|
||||||
|
plt.savefig(Path(__file__).parent.parent.parent / f"public/floris/{filename}")
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
self.set(wind_speeds=wind_speeds, wind_directions=wind_dirs, turbulence_intensities=[0.05] * len(wind_dirs))
|
||||||
|
|
||||||
|
return filename
|
@ -17,7 +17,7 @@ class OpenMeteoClient:
|
|||||||
params = {
|
params = {
|
||||||
"latitude": latitude,
|
"latitude": latitude,
|
||||||
"longitude": longitude,
|
"longitude": longitude,
|
||||||
"hourly": ["wind_speed_10m", "wind_direction_10m"],
|
"daily": ["wind_speed_10m_max", "wind_direction_10m_dominant"],
|
||||||
"timezone": self.timezone,
|
"timezone": self.timezone,
|
||||||
"start_date": start_date,
|
"start_date": start_date,
|
||||||
"end_date": end_date
|
"end_date": end_date
|
||||||
@ -27,13 +27,13 @@ class OpenMeteoClient:
|
|||||||
|
|
||||||
def process_response(self, response):
|
def process_response(self, response):
|
||||||
# Process hourly data
|
# Process hourly data
|
||||||
hourly = response.Hourly()
|
daily = response.Daily()
|
||||||
hourly_wind_speed_10m = hourly.Variables(0).ValuesAsNumpy()
|
daily_wind_speed_10m = daily.Variables(0).ValuesAsNumpy()
|
||||||
hourly_wind_direction_10m = hourly.Variables(1).ValuesAsNumpy()
|
daily_wind_direction_10m = daily.Variables(1).ValuesAsNumpy()
|
||||||
|
|
||||||
return hourly_wind_speed_10m.tolist(), hourly_wind_direction_10m.tolist()
|
return daily_wind_speed_10m.tolist(), daily_wind_direction_10m.tolist()
|
||||||
|
|
||||||
def get_weather_info(self, latitude, longitude, start_date, end_date):
|
def get_weather_info(self, start_date, end_date, latitude=54.35119762746125, longitude=48.389356992149345):
|
||||||
responses = self.fetch_weather_data(latitude, longitude, start_date, end_date)
|
responses = self.fetch_weather_data(latitude, longitude, start_date, end_date)
|
||||||
response = responses[0]
|
response = responses[0]
|
||||||
self.process_response(response)
|
self.process_response(response)
|
||||||
|
@ -14,13 +14,15 @@ class SWeatherInfo(BaseModel):
|
|||||||
class SFlorisInputParams(BaseModel):
|
class SFlorisInputParams(BaseModel):
|
||||||
layout_x: list[float] = Field(Query([]))
|
layout_x: list[float] = Field(Query([]))
|
||||||
layout_y: list[float] = Field(Query([]))
|
layout_y: list[float] = Field(Query([]))
|
||||||
|
yaw_angle: list[float] = Field(Query([]))
|
||||||
|
plots: set[str] = Field(Query(["horizontal_plane"]))
|
||||||
date_start: date
|
date_start: date
|
||||||
date_end: date
|
date_end: date
|
||||||
|
|
||||||
|
|
||||||
class SFlorisOutputData(BaseModel):
|
class SFlorisOutputData(BaseModel):
|
||||||
file_name: str
|
file_name: object
|
||||||
data: list[float]
|
data: object
|
||||||
|
|
||||||
|
|
||||||
class WindTurbineTypeBase(BaseModel):
|
class WindTurbineTypeBase(BaseModel):
|
||||||
@ -108,4 +110,4 @@ class WindTurbineWithParkDetailsResponse(BaseModel):
|
|||||||
comment: Optional[str]
|
comment: Optional[str]
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
@ -8,12 +8,14 @@ from fastapi.responses import FileResponse
|
|||||||
|
|
||||||
from data.repository import WeatherRepository
|
from data.repository import WeatherRepository
|
||||||
from data.schemas import SFlorisInputParams, SFlorisOutputData, SWeatherInfo
|
from data.schemas import SFlorisInputParams, SFlorisOutputData, SWeatherInfo
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
sys.path.append(str(Path(__file__).parent.parent.parent))
|
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||||
from floris_module.src import FlorisULSTU
|
from floris_module.src import FlorisULSTU
|
||||||
from floris_module.src.OpenMeteoClient import OpenMeteoClient
|
from floris_module.src.OpenMeteoClient import OpenMeteoClient
|
||||||
|
|
||||||
|
|
||||||
FLORIS_IMAGES_PATH = Path(__file__).parent.parent.parent / "public" / "floris"
|
FLORIS_IMAGES_PATH = Path(__file__).parent.parent.parent / "public" / "floris"
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
@ -26,23 +28,20 @@ router = APIRouter(
|
|||||||
async def get_windmill_data(
|
async def get_windmill_data(
|
||||||
data: Annotated[SFlorisInputParams, Depends()]
|
data: Annotated[SFlorisInputParams, Depends()]
|
||||||
):
|
):
|
||||||
if len(data.layout_x) != len(data.layout_y):
|
if len(data.layout_x) != len(data.layout_y) and len(data.layout_x) != len(data.yaw_angle):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
detail="Length of layout x and y must be the same",
|
detail="Length of layout x and y and yaw_angle must be the same",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
fmodel = FlorisULSTU()
|
fmodel = FlorisULSTU()
|
||||||
|
|
||||||
client = OpenMeteoClient()
|
client = OpenMeteoClient()
|
||||||
|
|
||||||
wind_directions = list()
|
climate_info = client.get_weather_info(data.date_start, data.date_end)
|
||||||
wind_speeds = list()
|
wind_speeds = climate_info[0]
|
||||||
|
wind_directions = climate_info[1]
|
||||||
for i in range(len(data.layout_x)):
|
|
||||||
i_result = client.get_weather_info(data.layout_x[i], data.layout_y[i], data.date_start, data.date_end)
|
|
||||||
wind_speeds.extend(i_result[0])
|
|
||||||
wind_directions.extend(i_result[1])
|
|
||||||
|
|
||||||
fmodel.set(
|
fmodel.set(
|
||||||
layout_x=data.layout_x,
|
layout_x=data.layout_x,
|
||||||
@ -52,14 +51,22 @@ async def get_windmill_data(
|
|||||||
turbulence_intensities=[0.1] * len(wind_directions)
|
turbulence_intensities=[0.1] * len(wind_directions)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
yaw_angles = np.zeros((len(wind_directions), len(data.layout_x)))
|
||||||
|
for i in range(len(data.layout_x)):
|
||||||
|
yaw_angles[:, i] = data.yaw_angle[i]
|
||||||
|
|
||||||
|
fmodel.set(
|
||||||
|
yaw_angles=yaw_angles,
|
||||||
|
)
|
||||||
|
|
||||||
fmodel.run()
|
fmodel.run()
|
||||||
|
|
||||||
data = fmodel.get_turbine_powers()[0].tolist()
|
res = fmodel.get_turbine_powers().tolist()
|
||||||
file_name = fmodel.visualisation()
|
file_names = fmodel.visualization(data.plots)
|
||||||
|
|
||||||
return SFlorisOutputData(
|
return SFlorisOutputData(
|
||||||
file_name=file_name,
|
file_name=file_names,
|
||||||
data=data
|
data=res
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user