diff --git a/.idea/EvaluationEfficiencyOptimizationWind.iml b/.idea/EvaluationEfficiencyOptimizationWind.iml
index d0876a7..32cf6f3 100644
--- a/.idea/EvaluationEfficiencyOptimizationWind.iml
+++ b/.idea/EvaluationEfficiencyOptimizationWind.iml
@@ -1,8 +1,22 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index d56657a..9af92d1 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,7 @@
-
+
+
+
+
\ No newline at end of file
diff --git a/.idea/other.xml b/.idea/other.xml
new file mode 100644
index 0000000..2e75c2e
--- /dev/null
+++ b/.idea/other.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/floris_module/src/__init__.py b/floris_module/src/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/front/webpack.config.js b/front/webpack.config.js
index d840890..a40ac79 100644
--- a/front/webpack.config.js
+++ b/front/webpack.config.js
@@ -5,7 +5,7 @@ import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
const __dirname = path.resolve();
const config = {
- entry: './src/index.tsx',
+ entry: './routers/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
diff --git a/floris_module/.gitignore b/server/floris_module/.gitignore
similarity index 100%
rename from floris_module/.gitignore
rename to server/floris_module/.gitignore
diff --git a/floris_module/NOTES.md b/server/floris_module/NOTES.md
similarity index 100%
rename from floris_module/NOTES.md
rename to server/floris_module/NOTES.md
diff --git a/floris_module/docs/Yaw_example.png b/server/floris_module/docs/Yaw_example.png
similarity index 100%
rename from floris_module/docs/Yaw_example.png
rename to server/floris_module/docs/Yaw_example.png
diff --git a/floris_module/requirements.txt b/server/floris_module/requirements.txt
similarity index 100%
rename from floris_module/requirements.txt
rename to server/floris_module/requirements.txt
diff --git a/floris_module/src/FlorisULSTU.py b/server/floris_module/src/FlorisULSTU.py
similarity index 54%
rename from floris_module/src/FlorisULSTU.py
rename to server/floris_module/src/FlorisULSTU.py
index 405a632..d0971ac 100644
--- a/floris_module/src/FlorisULSTU.py
+++ b/server/floris_module/src/FlorisULSTU.py
@@ -1,4 +1,14 @@
+import os.path
+import sys
+import uuid
+from pathlib import Path
+
from floris import FlorisModel
+from floris.flow_visualization import visualize_cut_plane
+from floris.layout_visualization import plot_turbine_labels
+import floris.layout_visualization as layoutviz
+
+import matplotlib.pyplot as plt
_wind_direction_to_val: dict[str, float] = {
@@ -31,13 +41,34 @@ def _check_wind_direction_definition(wind_direction: str) -> bool:
class FlorisULSTU(FlorisModel):
- def __init__(self, url_yaml):
+ def __init__(
+ self,
+ url_yaml: Path = Path(__file__).parent / "gch.yaml"
+ ):
super().__init__(url_yaml)
def set(self, **kwargs):
- if ("wind_directions" in kwargs) and (kwargs["wind_directions"] is list[str]):
+ if ("wind_directions" in kwargs) and (type(kwargs["wind_directions"][0]) is str):
kwargs["wind_directions"] = _convert_winds_list_direction_definitions(kwargs["wind_directions"])
super().set(**kwargs)
+ def visualisation(self):
+
+
+ self.reset_operation()
+
+ horizontal_plane = self.calculate_horizontal_plane(height=90.0)
+ visualize_cut_plane(
+ horizontal_plane,
+ )
+
+ # plot_turbine_labels(self, axarr[0, 0])
+ filename = str(uuid.uuid4()) + ".png"
+ plt.savefig(Path(__file__).parent.parent.parent / f"public/floris/{filename}")
+ plt.close()
+
+ return filename
+
+
diff --git a/server/floris_module/src/__init__.py b/server/floris_module/src/__init__.py
new file mode 100644
index 0000000..4cc26c6
--- /dev/null
+++ b/server/floris_module/src/__init__.py
@@ -0,0 +1,3 @@
+from .FlorisULSTU import FlorisULSTU
+
+__all__ = ['FlorisULSTU']
\ No newline at end of file
diff --git a/floris_module/src/gch.yaml b/server/floris_module/src/gch.yaml
similarity index 95%
rename from floris_module/src/gch.yaml
rename to server/floris_module/src/gch.yaml
index 5c0ea8e..01d727d 100644
--- a/floris_module/src/gch.yaml
+++ b/server/floris_module/src/gch.yaml
@@ -149,9 +149,9 @@ flow_field:
wake:
###
- # Select the models to use for the simulation.
+ # Select the data to use for the simulation.
# See :py:mod:`~.wake` for a list
- # of available models and their descriptions.
+ # of available data and their descriptions.
model_strings:
###
@@ -190,7 +190,7 @@ wake:
# Configure the parameters for the wake deflection model
# selected above.
# Additional blocks can be provided for
- # models that are not enabled, but the enabled model
+ # data that are not enabled, but the enabled model
# must have a corresponding parameter block.
wake_deflection_parameters:
gauss:
@@ -210,7 +210,7 @@ wake:
# Configure the parameters for the wake velocity deficit model
# selected above.
# Additional blocks can be provided for
- # models that are not enabled, but the enabled model
+ # data that are not enabled, but the enabled model
# must have a corresponding parameter block.
wake_velocity_parameters:
cc:
@@ -234,7 +234,7 @@ wake:
# Configure the parameters for the wake turbulence model
# selected above.
# Additional blocks can be provided for
- # models that are not enabled, but the enabled model
+ # data that are not enabled, but the enabled model
# must have a corresponding parameter block.
wake_turbulence_parameters:
crespo_hernandez:
diff --git a/floris_module/src/main.py b/server/floris_module/src/main.py
similarity index 100%
rename from floris_module/src/main.py
rename to server/floris_module/src/main.py
diff --git a/server/main.py b/server/main.py
deleted file mode 100644
index d523ca6..0000000
--- a/server/main.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from fastapi import FastAPI, Depends, Request
-from fastapi.responses import RedirectResponse, HTMLResponse
-from sqlalchemy.orm import Session
-from sqlalchemy.future import select
-from database import SessionLocal, User, Base, engine
-from fastapi.templating import Jinja2Templates
-from fastapi.staticfiles import StaticFiles
-
-from src.floris_router import router as floris_router
-from src.floris_router import get_images
-
-Base.metadata.create_all(bind=engine)
-
-app = FastAPI()
-templates = Jinja2Templates(directory="templates")
-
-app.mount("/static", StaticFiles(directory="static"), name="static")
-app.mount("/public", StaticFiles(directory="public"), name="public")
-
-app.include_router(floris_router)
-
-
-def get_db():
- db = SessionLocal()
- try:
- yield db
- finally:
- db.close()
-
-
-@app.get("/hello")
-async def hello():
- return {"message": "Hello, World!"}
-
-
-@app.get("/")
-async def redirect_to_docs():
- return RedirectResponse(url="/docs")
-
-
-@app.get("/users")
-def read_users(db: Session = Depends(get_db)):
- result = db.execute(select(User))
- users = result.scalars().all() # Получаем всех пользователей
- return users
-
-
-@app.get("/main", response_class=HTMLResponse)
-async def main(request: Request):
- params = {"request": request}
- params.update(await get_images())
-
- return templates.TemplateResponse("main.html", params)
-
-
-if __name__ == "__main__":
- import uvicorn
- uvicorn.run(app, host="localhost", port=8080) # Изменено на localhost
diff --git a/server/public/floris/Yaw_example.png b/server/public/floris/Yaw_example.png
deleted file mode 100644
index 7b4d7b5..0000000
Binary files a/server/public/floris/Yaw_example.png and /dev/null differ
diff --git a/server/src/data/database.py b/server/src/data/database.py
new file mode 100644
index 0000000..01711ae
--- /dev/null
+++ b/server/src/data/database.py
@@ -0,0 +1,11 @@
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+
+# TODO Maybe create env file
+DATABASE_URL = "mysql+pymysql://wind:wind@193.124.203.110:3306/wind_towers"
+
+engine = create_engine(DATABASE_URL)
+session_maker = sessionmaker(autocommit=False, autoflush=False, bind=engine)
+
+
+
diff --git a/server/database.py b/server/src/data/models.py
similarity index 58%
rename from server/database.py
rename to server/src/data/models.py
index c6f5c78..3d1aa26 100644
--- a/server/database.py
+++ b/server/src/data/models.py
@@ -1,28 +1,15 @@
-from sqlalchemy import create_engine, Column, Integer, String
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker, Mapped, mapped_column
-from sqlalchemy.dialects.mysql import TIMESTAMP, TIME, VARCHAR
-
from datetime import datetime
-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)
+from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
+from sqlalchemy.dialects.mysql import TIMESTAMP, TIME, VARCHAR
-Base = declarative_base()
-
-
-class User(Base):
- __tablename__ = "user" # Убедитесь, что эта таблица существует
-
- id = Column(Integer, primary_key=True, index=True)
- name = Column(String(50), index=True)
+class Base(DeclarativeBase):
+ pass
class Weather(Base):
- __tablename__ = "weather"
+ __tablename__ = "weather_data"
id: Mapped[int] = mapped_column(primary_key=True)
BarTrend: Mapped[str] = mapped_column(VARCHAR(255))
@@ -46,3 +33,4 @@ class Weather(Base):
WindDir: Mapped[str] = mapped_column(VARCHAR(50))
WindSpeed: Mapped[float]
WindSpeed10Min: Mapped[float]
+
diff --git a/server/src/data/repository.py b/server/src/data/repository.py
new file mode 100644
index 0000000..2411229
--- /dev/null
+++ b/server/src/data/repository.py
@@ -0,0 +1,27 @@
+from sqlalchemy import select
+
+from .database import session_maker
+from .models import Weather
+from .schemas import SWeatherInfo
+
+
+class WeatherRepository:
+ @classmethod
+ def get_all(cls):
+ with session_maker() as session:
+ query = (
+ select(Weather)
+ )
+ res = session.execute(query)
+ weather_models = res.scalars().all()
+ return [SWeatherInfo.model_validate(weather, from_attributes=True) for weather in weather_models]
+
+ @classmethod
+ def get_by_id(cls, id):
+ with session_maker() as session:
+ query = (
+ select(Weather).where(Weather.id == id)
+ )
+ res = session.execute(query)
+ weather_model = res.scalars().first()
+ return SWeatherInfo.model_validate(weather_model, from_attributes=True)
\ No newline at end of file
diff --git a/server/src/data/schemas.py b/server/src/data/schemas.py
new file mode 100644
index 0000000..3388350
--- /dev/null
+++ b/server/src/data/schemas.py
@@ -0,0 +1,19 @@
+from pydantic import BaseModel, Field
+from fastapi import Query
+
+
+class SWeatherInfo(BaseModel):
+ id: int
+ WindDir: str
+ WindSpeed: float
+
+
+class SFlorisInputParams(BaseModel):
+ weather_id: int
+ layout_x: list[float] = Field(Query([]))
+ layout_y: list[float] = Field(Query([]))
+
+
+class SFlorisOutputData(BaseModel):
+ file_name: str
+ data: list[float]
\ No newline at end of file
diff --git a/server/src/floris_router.py b/server/src/floris_router.py
deleted file mode 100644
index 906d32d..0000000
--- a/server/src/floris_router.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from pathlib import Path
-from fastapi import APIRouter, Depends, HTTPException
-from fastapi.responses import FileResponse
-
-
-STATIC_DIR = Path("public/floris")
-
-router = APIRouter(
- prefix="/floris",
- tags=["Static Files from Floris"],
-)
-
-
-@router.get("/get_images")
-async def get_images():
- try:
- images = [file.name for file in STATIC_DIR.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 = STATIC_DIR / 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)
-
-
-
diff --git a/server/src/main.py b/server/src/main.py
new file mode 100644
index 0000000..692fdde
--- /dev/null
+++ b/server/src/main.py
@@ -0,0 +1,22 @@
+from pathlib import Path
+
+from fastapi import FastAPI
+
+from fastapi.staticfiles import StaticFiles
+
+from routers.floris_router import router as floris_router
+from routers.floris_template_router import router as floris_template_router
+
+
+app = FastAPI()
+
+app.mount("/static", StaticFiles(directory=Path("../static")), name="static")
+app.mount("/public", StaticFiles(directory=Path("../public")), name="public")
+
+app.include_router(floris_router)
+app.include_router(floris_template_router)
+
+
+# if __name__ == "__main__":
+# import uvicorn
+# uvicorn.run(app, host="localhost", port=8080) # Изменено на localhost
diff --git a/server/src/router.py b/server/src/router.py
deleted file mode 100644
index 8013b0a..0000000
--- a/server/src/router.py
+++ /dev/null
@@ -1 +0,0 @@
-st
\ No newline at end of file
diff --git a/server/src/routers/floris_router.py b/server/src/routers/floris_router.py
new file mode 100644
index 0000000..4e6a260
--- /dev/null
+++ b/server/src/routers/floris_router.py
@@ -0,0 +1,73 @@
+from http import HTTPStatus
+from pathlib import Path
+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
+
+from FlorisULSTU import FlorisULSTU
+
+FLORIS_IMAGES_PATH = Path("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):
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST,
+ detail="Length of layout x and y must be the same",
+ )
+
+ atmosphere_param: SWeatherInfo = WeatherRepository().get_by_id(data.weather_id)
+
+ fmodel = FlorisULSTU()
+ fmodel.set(
+ layout_x=data.layout_x,
+ layout_y=data.layout_y,
+ wind_directions=[atmosphere_param.WindDir],
+ wind_speeds=[atmosphere_param.WindSpeed],
+ )
+ fmodel.run()
+
+ data = fmodel.get_turbine_powers()[0].tolist()
+ file_name = fmodel.visualisation()
+
+ return SFlorisOutputData(
+ file_name=file_name,
+ data=data
+ )
+
+
+@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)
+
+
+
diff --git a/server/src/routers/floris_template_router.py b/server/src/routers/floris_template_router.py
new file mode 100644
index 0000000..1519ef8
--- /dev/null
+++ b/server/src/routers/floris_template_router.py
@@ -0,0 +1,65 @@
+from pathlib import Path
+from typing import Annotated
+
+from fastapi import APIRouter, Request, Form, Depends
+from fastapi.responses import HTMLResponse
+from fastapi.templating import Jinja2Templates
+
+from data.repository import WeatherRepository
+from data.schemas import SFlorisInputParams
+
+from routers.floris_router import get_windmill_data
+
+templates = Jinja2Templates(directory=Path("../templates/floris_templates"))
+
+router = APIRouter(
+ prefix="",
+ tags=["floris_template"],
+ include_in_schema=False,
+)
+
+
+@router.get("/", response_class=HTMLResponse)
+async def get_main(request: Request, context: dict | None = None):
+ if context is None:
+ context = {}
+
+ params = {"request": request, "weather": WeatherRepository.get_all()}
+
+ if context is not None:
+ params.update(context)
+
+ return templates.TemplateResponse("main.html", params)
+
+
+def transform_windmill_data(
+ options: Annotated[str, Form()],
+ x: Annotated[str, Form()],
+ y: Annotated[str, Form()]
+) -> SFlorisInputParams | None:
+
+ if not all([options, x, y]):
+ return None
+
+ return SFlorisInputParams(
+ weather_id=int(options),
+ layout_x=list(map(float, x.split())),
+ layout_y=list(map(float, y.split())),
+ )
+
+
+@router.post("/", response_class=HTMLResponse)
+async def post_main(
+ request: Request,
+ windmill_params: Annotated[SFlorisInputParams | None, Depends(transform_windmill_data)] = None,
+):
+ if windmill_params is None:
+ return await get_main(request)
+
+ data = await get_windmill_data(windmill_params)
+ context = {
+ 'data': data.data,
+ 'file_name': data.file_name,
+ }
+
+ return await get_main(request, context)
diff --git a/server/templates/floris_templates/main.html b/server/templates/floris_templates/main.html
new file mode 100644
index 0000000..9bc3c4b
--- /dev/null
+++ b/server/templates/floris_templates/main.html
@@ -0,0 +1,85 @@
+
+
+
+
+
+ Image Viewer
+
+
+
+
+
+
+
+
+
+ {{ data }}
+ {% if data is defined %}
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
diff --git a/server/templates/main.html b/server/templates/main.html
deleted file mode 100644
index 01e50b9..0000000
--- a/server/templates/main.html
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
- Image Viewer
-
-
-
-
-
-
-
-
-
-
-
Available Images
-
- {% for image in images %}
- - {{ image }}
- {% endfor %}
-
-
-
-
-
-
-