Compare commits
No commits in common. "test-wind-park-retrieve" and "main" have entirely different histories.
test-wind-
...
main
@ -26,8 +26,8 @@ class OpenMeteoClient:
|
|||||||
return responses
|
return responses
|
||||||
|
|
||||||
def process_response(self, response):
|
def process_response(self, response):
|
||||||
|
# Process hourly data
|
||||||
daily = response.Daily()
|
daily = response.Daily()
|
||||||
|
|
||||||
daily_wind_speed_10m = daily.Variables(0).ValuesAsNumpy()
|
daily_wind_speed_10m = daily.Variables(0).ValuesAsNumpy()
|
||||||
daily_wind_direction_10m = daily.Variables(1).ValuesAsNumpy()
|
daily_wind_direction_10m = daily.Variables(1).ValuesAsNumpy()
|
||||||
|
|
||||||
@ -36,4 +36,5 @@ class OpenMeteoClient:
|
|||||||
def get_weather_info(self, start_date, end_date, latitude=54.35119762746125, longitude=48.389356992149345):
|
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)
|
||||||
return self.process_response(response)
|
return self.process_response(response)
|
||||||
|
Binary file not shown.
@ -4,15 +4,12 @@ from sqlalchemy.orm import sessionmaker, declarative_base, Session
|
|||||||
# TODO Maybe create env file
|
# TODO Maybe create env file
|
||||||
DATABASE_URL = "mysql+pymysql://wind:wind@193.124.203.110:3306/wind_towers"
|
DATABASE_URL = "mysql+pymysql://wind:wind@193.124.203.110:3306/wind_towers"
|
||||||
|
|
||||||
|
engine = create_engine(DATABASE_URL)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
# Базовый класс для декларативных моделей
|
# Базовый класс для декларативных моделей
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
engine = create_engine(DATABASE_URL)
|
|
||||||
|
|
||||||
Base.metadata.create_all(engine)
|
|
||||||
|
|
||||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
||||||
|
|
||||||
|
|
||||||
# Функция для получения сессии
|
# Функция для получения сессии
|
||||||
def get_db() -> Session:
|
def get_db() -> Session:
|
||||||
|
@ -44,7 +44,7 @@ class WindTurbineType(Base):
|
|||||||
BladeLength: Mapped[float] = mapped_column(Float)
|
BladeLength: Mapped[float] = mapped_column(Float)
|
||||||
|
|
||||||
# Связь с WindParkTurbine
|
# Связь с WindParkTurbine
|
||||||
turbine_parks: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="turbine", cascade='all, delete-orphan')
|
turbine_parks: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="turbine")
|
||||||
|
|
||||||
|
|
||||||
class WindPark(Base):
|
class WindPark(Base):
|
||||||
@ -55,14 +55,14 @@ class WindPark(Base):
|
|||||||
CenterLongitude: Mapped[float] = mapped_column(Float)
|
CenterLongitude: Mapped[float] = mapped_column(Float)
|
||||||
|
|
||||||
# Связь с WindParkTurbine
|
# Связь с WindParkTurbine
|
||||||
wind_park_turbines: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="wind_park", cascade='all, delete-orphan')
|
wind_park_turbines: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="wind_park")
|
||||||
|
|
||||||
|
|
||||||
class WindParkTurbine(Base):
|
class WindParkTurbine(Base):
|
||||||
__tablename__ = 'wind_park_turbine'
|
__tablename__ = 'wind_park_turbine'
|
||||||
|
|
||||||
wind_park_id: Mapped[int] = mapped_column(Integer, ForeignKey('wind_park.Id', ondelete='CASCADE'), primary_key=True)
|
wind_park_id: Mapped[int] = mapped_column(Integer, ForeignKey('wind_park.Id'), primary_key=True)
|
||||||
turbine_id: Mapped[int] = mapped_column(Integer, ForeignKey('wind_turbine_type.Id', ondelete='CASCADE'), primary_key=True)
|
turbine_id: Mapped[int] = mapped_column(Integer, ForeignKey('wind_turbine_type.Id'), primary_key=True)
|
||||||
x_offset: Mapped[int] = mapped_column(Integer, nullable=False)
|
x_offset: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
y_offset: Mapped[int] = mapped_column(Integer, nullable=False)
|
y_offset: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
angle: Mapped[int] = mapped_column(Integer, nullable=True)
|
angle: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||||
|
@ -33,6 +33,7 @@ class WeatherRepository:
|
|||||||
return SWeatherInfo.model_validate(weather_model, from_attributes=True)
|
return SWeatherInfo.model_validate(weather_model, from_attributes=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WindTurbineTypeRepository:
|
class WindTurbineTypeRepository:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(db: Session, turbine_type: WindTurbineTypeCreate):
|
def create(db: Session, turbine_type: WindTurbineTypeCreate):
|
||||||
@ -143,13 +144,11 @@ class WindParkTurbineRepository:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(db: Session, park_id: int, turbine_id: int):
|
def get(db: Session, park_id: int, turbine_id: int):
|
||||||
return db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id,
|
return db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id, WindParkTurbine.turbine_id == turbine_id).first()
|
||||||
WindParkTurbine.turbine_id == turbine_id).first()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update(db: Session, park_id: int, turbine_id: int, park_turbine: WindParkTurbineCreate):
|
def update(db: Session, park_id: int, turbine_id: int, park_turbine: WindParkTurbineCreate):
|
||||||
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id,
|
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id, WindParkTurbine.turbine_id == turbine_id).first()
|
||||||
WindParkTurbine.turbine_id == turbine_id).first()
|
|
||||||
if db_park_turbine:
|
if db_park_turbine:
|
||||||
for key, value in park_turbine.dict().items():
|
for key, value in park_turbine.dict().items():
|
||||||
setattr(db_park_turbine, key, value)
|
setattr(db_park_turbine, key, value)
|
||||||
@ -160,8 +159,7 @@ class WindParkTurbineRepository:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete(db: Session, park_id: int, turbine_id: int):
|
def delete(db: Session, park_id: int, turbine_id: int):
|
||||||
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id,
|
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id, WindParkTurbine.turbine_id == turbine_id).first()
|
||||||
WindParkTurbine.turbine_id == turbine_id).first()
|
|
||||||
if db_park_turbine:
|
if db_park_turbine:
|
||||||
db.delete(db_park_turbine)
|
db.delete(db_park_turbine)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
@ -20,12 +20,6 @@ class SFlorisInputParams(BaseModel):
|
|||||||
date_end: date
|
date_end: date
|
||||||
|
|
||||||
|
|
||||||
class SFlorisInputParamsForWindPark(BaseModel):
|
|
||||||
plots: set[str] = Field(Query(["horizontal_plane"]))
|
|
||||||
date_start: date
|
|
||||||
date_end: date
|
|
||||||
|
|
||||||
|
|
||||||
class SFlorisOutputData(BaseModel):
|
class SFlorisOutputData(BaseModel):
|
||||||
file_name: object
|
file_name: object
|
||||||
data: object
|
data: object
|
||||||
@ -105,7 +99,6 @@ class WindTurbineResponse(BaseModel):
|
|||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|
||||||
|
|
||||||
class WindTurbineWithParkDetailsResponse(BaseModel):
|
class WindTurbineWithParkDetailsResponse(BaseModel):
|
||||||
Id: int
|
Id: int
|
||||||
Name: str
|
Name: str
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import math
|
|
||||||
import sys
|
import sys
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -7,17 +6,16 @@ from typing import Annotated
|
|||||||
from fastapi import APIRouter, HTTPException, Depends
|
from fastapi import APIRouter, HTTPException, Depends
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
|
|
||||||
from sqlalchemy.orm import Session
|
from data.repository import WeatherRepository
|
||||||
|
from data.schemas import SFlorisInputParams, SFlorisOutputData, SWeatherInfo
|
||||||
from data.repository import WeatherRepository, WindParkRepository
|
|
||||||
from data.schemas import SFlorisInputParams, SFlorisOutputData, SFlorisInputParamsForWindPark
|
|
||||||
from data.database import get_db
|
|
||||||
import numpy as np
|
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(
|
||||||
@ -28,7 +26,7 @@ router = APIRouter(
|
|||||||
|
|
||||||
@router.get("/get_windmill_data")
|
@router.get("/get_windmill_data")
|
||||||
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) and len(data.layout_x) != len(data.yaw_angle):
|
if len(data.layout_x) != len(data.layout_y) and len(data.layout_x) != len(data.yaw_angle):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@ -36,6 +34,7 @@ async def get_windmill_data(
|
|||||||
detail="Length of layout x and y and yaw_angle 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()
|
||||||
@ -93,123 +92,4 @@ async def download_image(
|
|||||||
return FileResponse(image_path, media_type="image/jpeg", filename=image_name)
|
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
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user