Compare commits
1 Commits
main
...
test-wind-
Author | SHA1 | Date | |
---|---|---|---|
f30661c709 |
@ -26,8 +26,8 @@ class OpenMeteoClient:
|
||||
return responses
|
||||
|
||||
def process_response(self, response):
|
||||
# Process hourly data
|
||||
daily = response.Daily()
|
||||
|
||||
daily_wind_speed_10m = daily.Variables(0).ValuesAsNumpy()
|
||||
daily_wind_direction_10m = daily.Variables(1).ValuesAsNumpy()
|
||||
|
||||
@ -36,5 +36,4 @@ class OpenMeteoClient:
|
||||
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)
|
||||
response = responses[0]
|
||||
self.process_response(response)
|
||||
return self.process_response(response)
|
||||
|
Binary file not shown.
@ -4,12 +4,15 @@ from sqlalchemy.orm import sessionmaker, declarative_base, Session
|
||||
# TODO Maybe create env file
|
||||
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()
|
||||
|
||||
engine = create_engine(DATABASE_URL)
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
# Функция для получения сессии
|
||||
def get_db() -> Session:
|
||||
|
@ -44,7 +44,7 @@ class WindTurbineType(Base):
|
||||
BladeLength: Mapped[float] = mapped_column(Float)
|
||||
|
||||
# Связь с WindParkTurbine
|
||||
turbine_parks: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="turbine")
|
||||
turbine_parks: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="turbine", cascade='all, delete-orphan')
|
||||
|
||||
|
||||
class WindPark(Base):
|
||||
@ -55,14 +55,14 @@ class WindPark(Base):
|
||||
CenterLongitude: Mapped[float] = mapped_column(Float)
|
||||
|
||||
# Связь с WindParkTurbine
|
||||
wind_park_turbines: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="wind_park")
|
||||
wind_park_turbines: Mapped["list[WindParkTurbine]"] = relationship("WindParkTurbine", back_populates="wind_park", cascade='all, delete-orphan')
|
||||
|
||||
|
||||
class WindParkTurbine(Base):
|
||||
__tablename__ = 'wind_park_turbine'
|
||||
|
||||
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'), primary_key=True)
|
||||
wind_park_id: Mapped[int] = mapped_column(Integer, ForeignKey('wind_park.Id', ondelete='CASCADE'), primary_key=True)
|
||||
turbine_id: Mapped[int] = mapped_column(Integer, ForeignKey('wind_turbine_type.Id', ondelete='CASCADE'), primary_key=True)
|
||||
x_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)
|
||||
|
@ -33,7 +33,6 @@ class WeatherRepository:
|
||||
return SWeatherInfo.model_validate(weather_model, from_attributes=True)
|
||||
|
||||
|
||||
|
||||
class WindTurbineTypeRepository:
|
||||
@staticmethod
|
||||
def create(db: Session, turbine_type: WindTurbineTypeCreate):
|
||||
@ -144,11 +143,13 @@ class WindParkTurbineRepository:
|
||||
|
||||
@staticmethod
|
||||
def get(db: Session, park_id: int, turbine_id: int):
|
||||
return db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id, WindParkTurbine.turbine_id == turbine_id).first()
|
||||
return db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id,
|
||||
WindParkTurbine.turbine_id == turbine_id).first()
|
||||
|
||||
@staticmethod
|
||||
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, WindParkTurbine.turbine_id == turbine_id).first()
|
||||
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id,
|
||||
WindParkTurbine.turbine_id == turbine_id).first()
|
||||
if db_park_turbine:
|
||||
for key, value in park_turbine.dict().items():
|
||||
setattr(db_park_turbine, key, value)
|
||||
@ -159,7 +160,8 @@ class WindParkTurbineRepository:
|
||||
|
||||
@staticmethod
|
||||
def delete(db: Session, park_id: int, turbine_id: int):
|
||||
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id, WindParkTurbine.turbine_id == turbine_id).first()
|
||||
db_park_turbine = db.query(WindParkTurbine).filter(WindParkTurbine.wind_park_id == park_id,
|
||||
WindParkTurbine.turbine_id == turbine_id).first()
|
||||
if db_park_turbine:
|
||||
db.delete(db_park_turbine)
|
||||
db.commit()
|
||||
|
@ -20,6 +20,12 @@ class SFlorisInputParams(BaseModel):
|
||||
date_end: date
|
||||
|
||||
|
||||
class SFlorisInputParamsForWindPark(BaseModel):
|
||||
plots: set[str] = Field(Query(["horizontal_plane"]))
|
||||
date_start: date
|
||||
date_end: date
|
||||
|
||||
|
||||
class SFlorisOutputData(BaseModel):
|
||||
file_name: object
|
||||
data: object
|
||||
@ -99,6 +105,7 @@ class WindTurbineResponse(BaseModel):
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class WindTurbineWithParkDetailsResponse(BaseModel):
|
||||
Id: int
|
||||
Name: str
|
||||
|
@ -1,3 +1,4 @@
|
||||
import math
|
||||
import sys
|
||||
from http import HTTPStatus
|
||||
from pathlib import Path
|
||||
@ -6,16 +7,17 @@ from typing import Annotated
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from data.repository import WeatherRepository
|
||||
from data.schemas import SFlorisInputParams, SFlorisOutputData, SWeatherInfo
|
||||
import numpy as np
|
||||
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(
|
||||
@ -34,7 +36,6 @@ async def get_windmill_data(
|
||||
detail="Length of layout x and y and yaw_angle must be the same",
|
||||
)
|
||||
|
||||
|
||||
fmodel = FlorisULSTU()
|
||||
|
||||
client = OpenMeteoClient()
|
||||
@ -92,4 +93,123 @@ async def download_image(
|
||||
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