Merge pull request 'floris_design' (#7) from floris_design into main

Reviewed-on: #7
This commit is contained in:
maxim 2024-11-24 14:13:10 +04:00
commit 0aafeda77d
4 changed files with 108 additions and 30 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
) )