216 lines
6.5 KiB
Python
216 lines
6.5 KiB
Python
import math
|
||
import sys
|
||
from http import HTTPStatus
|
||
from pathlib import Path
|
||
from typing import Annotated
|
||
|
||
from fastapi import APIRouter, HTTPException, Depends
|
||
from fastapi.responses import FileResponse
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
from data.repository import WeatherRepository, WindParkRepository
|
||
from data.schemas import SFlorisInputParams, SFlorisOutputData, SFlorisInputParamsForWindPark
|
||
from data.database import get_db
|
||
import numpy as np
|
||
|
||
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||
from floris_module.src import FlorisULSTU
|
||
from floris_module.src.OpenMeteoClient import OpenMeteoClient
|
||
|
||
FLORIS_IMAGES_PATH = Path(__file__).parent.parent.parent / "public" / "floris"
|
||
|
||
router = APIRouter(
|
||
prefix="/api/floris",
|
||
tags=["Floris Api"],
|
||
)
|
||
|
||
|
||
@router.get("/get_windmill_data")
|
||
async def get_windmill_data(
|
||
data: Annotated[SFlorisInputParams, Depends()]
|
||
):
|
||
if len(data.layout_x) != len(data.layout_y) and len(data.layout_x) != len(data.yaw_angle):
|
||
raise HTTPException(
|
||
status_code=HTTPStatus.BAD_REQUEST,
|
||
detail="Length of layout x and y and yaw_angle must be the same",
|
||
)
|
||
|
||
fmodel = FlorisULSTU()
|
||
|
||
client = OpenMeteoClient()
|
||
|
||
climate_info = client.get_weather_info(data.date_start, data.date_end)
|
||
wind_speeds = climate_info[0]
|
||
wind_directions = climate_info[1]
|
||
|
||
fmodel.set(
|
||
layout_x=data.layout_x,
|
||
layout_y=data.layout_y,
|
||
wind_directions=wind_directions,
|
||
wind_speeds=wind_speeds,
|
||
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()
|
||
|
||
res = fmodel.get_turbine_powers().tolist()
|
||
file_names = fmodel.visualization(data.plots)
|
||
|
||
return SFlorisOutputData(
|
||
file_name=file_names,
|
||
data=res
|
||
)
|
||
|
||
|
||
@router.get("/get_images")
|
||
async def get_images():
|
||
try:
|
||
images = [file.name for file in FLORIS_IMAGES_PATH.iterdir() if file.is_file()]
|
||
return {"images": images}
|
||
except Exception as e:
|
||
raise HTTPException(
|
||
status_code=500,
|
||
detail=str(e)
|
||
)
|
||
|
||
|
||
@router.get("/download_image/{image_name}")
|
||
async def download_image(
|
||
image_name: str
|
||
):
|
||
image_path = FLORIS_IMAGES_PATH / image_name
|
||
if not image_path.exists() or not image_path.is_file():
|
||
raise HTTPException(status_code=404, detail="Image not found")
|
||
return FileResponse(image_path, media_type="image/jpeg", filename=image_name)
|
||
|
||
|
||
@router.get("/get_windmill_data/{park_id}", response_model=SFlorisOutputData)
|
||
async def get_windmill_data_by_wind_park(
|
||
park_id: int,
|
||
data: Annotated[SFlorisInputParamsForWindPark, Depends()],
|
||
db: Session = Depends(get_db)
|
||
):
|
||
fmodel = FlorisULSTU()
|
||
|
||
client = OpenMeteoClient()
|
||
|
||
park = WindParkRepository.get(db, park_id)
|
||
|
||
turbines = WindParkRepository.get_turbines_by_park_id(park_id, db)
|
||
|
||
turbine_coordinates = [
|
||
get_new_coordinates(park.CenterLatitude, park.CenterLongitude, turbine.x_offset, turbine.y_offset)
|
||
for turbine in turbines
|
||
]
|
||
|
||
turbine_yaw_angles = [turbine.angle for turbine in turbines]
|
||
|
||
weather_info_list = [
|
||
client.get_weather_info(start_date=data.date_start, end_date=data.date_end, latitude=lat, longitude=lon)
|
||
for lat, lon in turbine_coordinates
|
||
]
|
||
|
||
park_centerX, park_centerY = get_absolute_coordinates(park.CenterLatitude, park.CenterLongitude)
|
||
|
||
turbineX = [
|
||
park_centerX + turbine.x_offset
|
||
for turbine in turbines
|
||
]
|
||
|
||
turbineY = [
|
||
park_centerY + turbine.y_offset
|
||
for turbine in turbines
|
||
]
|
||
|
||
wind_speeds = np.array([item[0][0] for item in weather_info_list])
|
||
wind_directions = np.array([item[1][0] for item in weather_info_list])
|
||
|
||
print(wind_directions)
|
||
|
||
fmodel.set(
|
||
layout_x=turbineX,
|
||
layout_y=turbineY,
|
||
wind_directions=wind_directions,
|
||
wind_speeds=wind_speeds,
|
||
turbulence_intensities=[0.1] * len(wind_directions)
|
||
)
|
||
|
||
yaw_angles = np.zeros((len(wind_directions), len(turbineX)))
|
||
for i in range(len(turbineX)):
|
||
yaw_angles[:, i] = turbine_yaw_angles[i]
|
||
|
||
fmodel.set(
|
||
yaw_angles=yaw_angles,
|
||
)
|
||
|
||
fmodel.run()
|
||
|
||
res = fmodel.get_turbine_powers().tolist()
|
||
file_names = fmodel.visualization(data.plots)
|
||
|
||
return SFlorisOutputData(
|
||
file_name=file_names,
|
||
data=res
|
||
)
|
||
|
||
|
||
def get_new_coordinates(lat, lon, dx, dy):
|
||
"""
|
||
Вычисляет новую широту и долготу, исходя из заданной точки и смещения по осям x и y.
|
||
|
||
:param lat: Широта исходной точки (в градусах)
|
||
:param lon: Долгота исходной точки (в градусах)
|
||
:param dx: Смещение по оси X (в метрах)
|
||
:param dy: Смещение по оси Y (в метрах)
|
||
:return: (новая_широта, новая_долгота)
|
||
"""
|
||
# Радиус Земли (в метрах)
|
||
R = 6378137 # Средний радиус Земли
|
||
|
||
# Преобразуем широту из градусов в радианы
|
||
lat_rad = math.radians(lat)
|
||
|
||
# Вычисляем смещение в градусах
|
||
dlat = dy / R * (180 / math.pi) # Смещение широты
|
||
dlon = dx / (R * math.cos(lat_rad)) * (180 / math.pi) # Смещение долготы
|
||
|
||
# Вычисляем новые координаты
|
||
new_lat = lat + dlat
|
||
new_lon = lon + dlon
|
||
|
||
return new_lat, new_lon
|
||
|
||
|
||
def get_absolute_coordinates(lat, lon):
|
||
"""
|
||
Конвертирует широту и долготу в абсолютные координаты (x, y) в метрах.
|
||
|
||
:param lat: Широта точки (в градусах)
|
||
:param lon: Долгота точки (в градусах)
|
||
:return: Абсолютные координаты (x, y) в метрах
|
||
"""
|
||
# Радиус Земли в метрах
|
||
R = 6378137
|
||
|
||
# Преобразуем широту и долготу из градусов в радианы
|
||
lat_rad = math.radians(lat)
|
||
lon_rad = math.radians(lon)
|
||
|
||
# Вычисляем координату y (по широте)
|
||
y = R * lat_rad # в метрах
|
||
|
||
# Вычисляем координату x (по долготе)
|
||
x = R * math.cos(lat_rad) * lon_rad # в метрах
|
||
|
||
return x, y
|
||
|