floris_design #3
18
.idea/EvaluationEfficiencyOptimizationWind.iml
generated
18
.idea/EvaluationEfficiencyOptimizationWind.iml
generated
@ -1,8 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$">
|
||||||
<orderEntry type="inheritedJdk" />
|
<sourceFolder url="file://$MODULE_DIR$/server/floris_module/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/server/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/server/floris_module/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.12 (EvaluationEfficiencyOptimizationWind)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="bootstrap" level="application" />
|
||||||
|
<orderEntry type="library" name="@popperjs/core" level="application" />
|
||||||
|
</component>
|
||||||
|
<component name="TemplatesService">
|
||||||
|
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||||
|
<option name="TEMPLATE_FOLDERS">
|
||||||
|
<list>
|
||||||
|
<option value="$MODULE_DIR$/server/src/templates" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="Wind" uuid="401b0fa3-45f0-4ed2-9524-fd6acc19f73e">
|
||||||
|
<driver-ref>mariadb</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:mariadb://193.124.203.110:3306/wind_towers</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptLibraryMappings">
|
||||||
|
<file url="file://$PROJECT_DIR$" libraries="{@popperjs/core, bootstrap}" />
|
||||||
|
</component>
|
||||||
|
</project>
|
5
.idea/misc.xml
generated
5
.idea/misc.xml
generated
@ -1,4 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.12 (EvaluationEfficiencyOptimizationWind) (2)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (EvaluationEfficiencyOptimizationWind)" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
6
.idea/other.xml
generated
Normal file
6
.idea/other.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PySciProjectComponent">
|
||||||
|
<option name="PY_INTERACTIVE_PLOTS_SUGGESTED" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
14
.idea/webResources.xml
generated
Normal file
14
.idea/webResources.xml
generated
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="WebResourcesPaths">
|
||||||
|
<contentEntries>
|
||||||
|
<entry url="file://$PROJECT_DIR$">
|
||||||
|
<entryData>
|
||||||
|
<resourceRoots>
|
||||||
|
<path value="file://$PROJECT_DIR$/server/src/static" />
|
||||||
|
</resourceRoots>
|
||||||
|
</entryData>
|
||||||
|
</entry>
|
||||||
|
</contentEntries>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -5,7 +5,7 @@ import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
|||||||
const __dirname = path.resolve();
|
const __dirname = path.resolve();
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
entry: './src/index.tsx',
|
entry: './routers/index.tsx',
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
filename: 'main.js',
|
filename: 'main.js',
|
||||||
|
@ -21,7 +21,12 @@
|
|||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Start FastAPI process
|
4. Enter src directory
|
||||||
```zsh
|
```zsh
|
||||||
fastapi dev main.py
|
cd src
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Start FastAPI process
|
||||||
|
```zsh
|
||||||
|
uvicorn main:app --reload
|
||||||
```
|
```
|
@ -1,21 +0,0 @@
|
|||||||
from sqlalchemy import create_engine, Column, Integer, String
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
class User(Base):
|
|
||||||
__tablename__ = "user" # Убедитесь, что эта таблица существует
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
|
||||||
name = Column(String(50), index=True)
|
|
241
server/floris_module/.gitignore
vendored
Normal file
241
server/floris_module/.gitignore
vendored
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### Python template
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
46
server/floris_module/NOTES.md
Normal file
46
server/floris_module/NOTES.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Заметки по Floris
|
||||||
|
|
||||||
|
## Описание
|
||||||
|
Здесь я буду описывать общую информацию для всех,
|
||||||
|
чтобы остальным не копать глубоко как пользоваться библиотекой
|
||||||
|
|
||||||
|
|
||||||
|
## Инициализация и концепты библиотеки
|
||||||
|
|
||||||
|
1) Для работы Floris **требуется** специальный конфигурационный yaml файл.
|
||||||
|
В нем хранятся базовые данные о ветряках - их расположения, сила ветра и тому подобное.
|
||||||
|
Базовое его содержимое не так важно, так как впоследствии их можно будет изменить напрямую в процессе работы программы
|
||||||
|
(Буду постепенно искать интересующие нас параметры)
|
||||||
|
|
||||||
|
2) Для инициализации используется класс `FlorisModel(yaml_path)` в библиотеке Floris. Объявляем переменную этим классом,
|
||||||
|
и передаем в него путь к нашему Yaml файлу
|
||||||
|
[Пример](https://nrel.github.io/floris/intro_concepts.html#build-the-model)
|
||||||
|
|
||||||
|
3) Для изменения параметров заданных в yaml файле, используется метод set
|
||||||
|
[Пример](https://nrel.github.io/floris/intro_concepts.html#run-the-floris-wake-calculation)
|
||||||
|
|
||||||
|
4) Запуск симуляции происходит с помощью ```fmodel.run()```
|
||||||
|
[Пример](https://nrel.github.io/floris/intro_concepts.html#run-the-floris-wake-calculation)
|
||||||
|
|
||||||
|
5) Получаем мощность турбин с помощью ```fmodel.get_turbine_powers()``` в ваттах в виде массива Numpy.
|
||||||
|
[Пример](https://nrel.github.io/floris/intro_concepts.html#get-turbine-power)
|
||||||
|
|
||||||
|
6) Для каждого ветряка можно задать Yaw угол (Что такое Yaw угол показано на картинке)
|
||||||
|
[Пример](https://nrel.github.io/floris/intro_concepts.html#applying-yaw-angles)
|
||||||
|
|
||||||
|
![Yaw_example.png](docs/Yaw_example.png)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Параметры
|
||||||
|
1) farm - Общие настройки расположения турбин (Кол-во элементов, задает кол-во ветряков. Можно поставить 1 элемент,
|
||||||
|
в таком случае данные у всех ветряков будут одинаковые)
|
||||||
|
1) layout_x - Список float координат турбин по оси X
|
||||||
|
2) layout_y - Список float координат турбин по оси Y
|
||||||
|
3) turbine_type - Список типов турбин
|
||||||
|
2) flow_field - Общие настройки атмосферных параметров (Кол-во элементов, задает кол-во экспериментов)
|
||||||
|
1) wind_directions - Список float Направление ветра в градусах (при этом север принимается за 0° или 360°,
|
||||||
|
восток – за 90°, юг – за 180°, а запад – за 270°)
|
||||||
|
2) turbulence_intensities - Список float силы? ветряков
|
||||||
|
3) wind_speeds - Cписок скорости ветра (Наверное в м/с)
|
BIN
server/floris_module/docs/Yaw_example.png
Normal file
BIN
server/floris_module/docs/Yaw_example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
22
server/floris_module/requirements.txt
Normal file
22
server/floris_module/requirements.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
attrs==24.2.0
|
||||||
|
coloredlogs==15.0.1
|
||||||
|
contourpy==1.3.0
|
||||||
|
cycler==0.12.1
|
||||||
|
FLORIS==4.1.1
|
||||||
|
fonttools==4.54.1
|
||||||
|
humanfriendly==10.0
|
||||||
|
kiwisolver==1.4.7
|
||||||
|
matplotlib==3.9.2
|
||||||
|
numexpr==2.10.1
|
||||||
|
numpy==1.26.4
|
||||||
|
packaging==24.1
|
||||||
|
pandas==2.2.3
|
||||||
|
pillow==10.4.0
|
||||||
|
pyparsing==3.2.0
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
pytz==2024.2
|
||||||
|
PyYAML==6.0.2
|
||||||
|
scipy==1.14.1
|
||||||
|
shapely==2.0.6
|
||||||
|
six==1.16.0
|
||||||
|
tzdata==2024.2
|
74
server/floris_module/src/FlorisULSTU.py
Normal file
74
server/floris_module/src/FlorisULSTU.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
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] = {
|
||||||
|
"N": 0.0,
|
||||||
|
"E": 90.0,
|
||||||
|
"S": 180.0,
|
||||||
|
"W": 270.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _convert_winds_list_direction_definitions(
|
||||||
|
wind_directions: list[str],
|
||||||
|
) -> list[float]:
|
||||||
|
return list(map(lambda x: _convert_wind_direction_definition(x.upper()), wind_directions))
|
||||||
|
|
||||||
|
|
||||||
|
def _convert_wind_direction_definition(
|
||||||
|
wind_direction: str
|
||||||
|
) -> float:
|
||||||
|
if not _check_wind_direction_definition(wind_direction):
|
||||||
|
raise ValueError(f"Wind direction {wind_direction} is not valid")
|
||||||
|
|
||||||
|
return sum(map(lambda x: _wind_direction_to_val[x], wind_direction)) / len(wind_direction)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_wind_direction_definition(wind_direction: str) -> bool:
|
||||||
|
if len(set(wind_direction) | set(_wind_direction_to_val)) > len(_wind_direction_to_val):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class FlorisULSTU(FlorisModel):
|
||||||
|
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 (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
|
||||||
|
|
||||||
|
|
||||||
|
|
3
server/floris_module/src/__init__.py
Normal file
3
server/floris_module/src/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from .FlorisULSTU import FlorisULSTU
|
||||||
|
|
||||||
|
__all__ = ['FlorisULSTU']
|
244
server/floris_module/src/gch.yaml
Normal file
244
server/floris_module/src/gch.yaml
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
|
||||||
|
###
|
||||||
|
# A name for this input file.
|
||||||
|
# This is not currently only for the user's reference.
|
||||||
|
name: GCH
|
||||||
|
|
||||||
|
###
|
||||||
|
# A description of the contents of this input file.
|
||||||
|
# This is not currently only for the user's reference.
|
||||||
|
description: Three turbines using Gauss Curl Hybrid model
|
||||||
|
|
||||||
|
###
|
||||||
|
# The earliest verion of FLORIS this input file supports.
|
||||||
|
# This is not currently only for the user's reference.
|
||||||
|
floris_version: v4
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the logging level and where to show the logs.
|
||||||
|
logging:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Settings for logging to the console (i.e. terminal).
|
||||||
|
console:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Can be "true" or "false".
|
||||||
|
enable: true
|
||||||
|
|
||||||
|
###
|
||||||
|
# Set the severity to show output. Messages at this level or higher will be shown.
|
||||||
|
# Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG".
|
||||||
|
level: WARNING
|
||||||
|
|
||||||
|
###
|
||||||
|
# Settings for logging to a file.
|
||||||
|
file:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Can be "true" or "false".
|
||||||
|
enable: false
|
||||||
|
|
||||||
|
###
|
||||||
|
# Set the severity to show output. Messages at this level or higher will be shown.
|
||||||
|
# Can be one of "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG".
|
||||||
|
level: WARNING
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the solver for the type of simulation.
|
||||||
|
solver:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Select the solver type.
|
||||||
|
# Can be one of: "turbine_grid", "flow_field_grid", "flow_field_planar_grid".
|
||||||
|
type: turbine_grid
|
||||||
|
|
||||||
|
###
|
||||||
|
# Options for the turbine type selected above. See the solver documentation for available parameters.
|
||||||
|
turbine_grid_points: 3
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the turbine types and their placement within the wind farm.
|
||||||
|
farm:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Coordinates for the turbine locations in the x-direction which is typically considered
|
||||||
|
# to be the streamwise direction (left, right) when the wind is out of the west.
|
||||||
|
# The order of the coordinates here corresponds to the index of the turbine in the primary
|
||||||
|
# data structures.
|
||||||
|
layout_x:
|
||||||
|
- 0.0
|
||||||
|
- 630.0
|
||||||
|
- 1260.0
|
||||||
|
|
||||||
|
###
|
||||||
|
# Coordinates for the turbine locations in the y-direction which is typically considered
|
||||||
|
# to be the spanwise direction (up, down) when the wind is out of the west.
|
||||||
|
# The order of the coordinates here corresponds to the index of the turbine in the primary
|
||||||
|
# data structures.
|
||||||
|
layout_y:
|
||||||
|
- 0.0
|
||||||
|
- 0.0
|
||||||
|
- 0.0
|
||||||
|
|
||||||
|
###
|
||||||
|
# Listing of turbine types for placement at the x and y coordinates given above.
|
||||||
|
# The list length must be 1 or the same as ``layout_x`` and ``layout_y``. If it is a
|
||||||
|
# single value, all turbines are of the same type. Otherwise, the turbine type
|
||||||
|
# is mapped to the location at the same index in ``layout_x`` and ``layout_y``.
|
||||||
|
# The types can be either a name included in the turbine_library or
|
||||||
|
# a full definition of a wind turbine directly.
|
||||||
|
turbine_type:
|
||||||
|
- nrel_5MW
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the atmospheric conditions.
|
||||||
|
flow_field:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Air density.
|
||||||
|
air_density: 1.225
|
||||||
|
|
||||||
|
###
|
||||||
|
# The height to consider the "center" of the vertical wind speed profile
|
||||||
|
# due to shear. With a shear exponent not 1, the wind speed at this height
|
||||||
|
# will be the value given in ``wind_speeds``. Above and below this height,
|
||||||
|
# the wind speed will change according to the shear profile; see
|
||||||
|
# :py:meth:`.FlowField.initialize_velocity_field`.
|
||||||
|
# For farms consisting of one wind turbine type, use ``reference_wind_height: -1``
|
||||||
|
# to use the hub height of the wind turbine definition. For multiple wind turbine
|
||||||
|
# types, the reference wind height must be given explicitly.
|
||||||
|
reference_wind_height: -1
|
||||||
|
|
||||||
|
###
|
||||||
|
# The turbulence intensities to include in the simulation, specified as a decimal.
|
||||||
|
turbulence_intensities:
|
||||||
|
- 0.06
|
||||||
|
|
||||||
|
###
|
||||||
|
# The wind directions to include in the simulation.
|
||||||
|
# 0 is north and 270 is west.
|
||||||
|
wind_directions:
|
||||||
|
- 270.0
|
||||||
|
|
||||||
|
###
|
||||||
|
# The exponent used to model the wind shear profile; see
|
||||||
|
# :py:meth:`.FlowField.initialize_velocity_field`.
|
||||||
|
wind_shear: 0.12
|
||||||
|
|
||||||
|
###
|
||||||
|
# The wind speeds to include in the simulation.
|
||||||
|
wind_speeds:
|
||||||
|
- 8.0
|
||||||
|
|
||||||
|
###
|
||||||
|
# The wind veer as a constant value for all points in the grid.
|
||||||
|
wind_veer: 0.0
|
||||||
|
|
||||||
|
###
|
||||||
|
# The conditions that are specified for use with the multi-dimensional Cp/Ct capbility.
|
||||||
|
# These conditions are external to FLORIS and specified by the user. They are used internally
|
||||||
|
# through a nearest-neighbor selection process to choose the correct Cp/Ct interpolants
|
||||||
|
# to use.
|
||||||
|
multidim_conditions:
|
||||||
|
Tp: 2.5
|
||||||
|
Hs: 3.01
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the wake model.
|
||||||
|
wake:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Select the data to use for the simulation.
|
||||||
|
# See :py:mod:`~.wake` for a list
|
||||||
|
# of available data and their descriptions.
|
||||||
|
model_strings:
|
||||||
|
|
||||||
|
###
|
||||||
|
# Select the wake combination model.
|
||||||
|
combination_model: sosfs
|
||||||
|
|
||||||
|
###
|
||||||
|
# Select the wake deflection model.
|
||||||
|
deflection_model: gauss
|
||||||
|
|
||||||
|
###
|
||||||
|
# Select the wake turbulence model.
|
||||||
|
turbulence_model: crespo_hernandez
|
||||||
|
|
||||||
|
###
|
||||||
|
# Select the wake velocity deficit model.
|
||||||
|
velocity_model: gauss
|
||||||
|
|
||||||
|
###
|
||||||
|
# Can be "true" or "false".
|
||||||
|
enable_secondary_steering: true
|
||||||
|
|
||||||
|
###
|
||||||
|
# Can be "true" or "false".
|
||||||
|
enable_yaw_added_recovery: true
|
||||||
|
|
||||||
|
###
|
||||||
|
# Can be "true" or "false".
|
||||||
|
enable_active_wake_mixing: false
|
||||||
|
|
||||||
|
###
|
||||||
|
# Can be "true" or "false".
|
||||||
|
enable_transverse_velocities: true
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the parameters for the wake deflection model
|
||||||
|
# selected above.
|
||||||
|
# Additional blocks can be provided for
|
||||||
|
# data that are not enabled, but the enabled model
|
||||||
|
# must have a corresponding parameter block.
|
||||||
|
wake_deflection_parameters:
|
||||||
|
gauss:
|
||||||
|
ad: 0.0
|
||||||
|
alpha: 0.58
|
||||||
|
bd: 0.0
|
||||||
|
beta: 0.077
|
||||||
|
dm: 1.0
|
||||||
|
ka: 0.38
|
||||||
|
kb: 0.004
|
||||||
|
jimenez:
|
||||||
|
ad: 0.0
|
||||||
|
bd: 0.0
|
||||||
|
kd: 0.05
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the parameters for the wake velocity deficit model
|
||||||
|
# selected above.
|
||||||
|
# Additional blocks can be provided for
|
||||||
|
# data that are not enabled, but the enabled model
|
||||||
|
# must have a corresponding parameter block.
|
||||||
|
wake_velocity_parameters:
|
||||||
|
cc:
|
||||||
|
a_s: 0.179367259
|
||||||
|
b_s: 0.0118889215
|
||||||
|
c_s1: 0.0563691592
|
||||||
|
c_s2: 0.13290157
|
||||||
|
a_f: 3.11
|
||||||
|
b_f: -0.68
|
||||||
|
c_f: 2.41
|
||||||
|
alpha_mod: 1.0
|
||||||
|
gauss:
|
||||||
|
alpha: 0.58
|
||||||
|
beta: 0.077
|
||||||
|
ka: 0.38
|
||||||
|
kb: 0.004
|
||||||
|
jensen:
|
||||||
|
we: 0.05
|
||||||
|
|
||||||
|
###
|
||||||
|
# Configure the parameters for the wake turbulence model
|
||||||
|
# selected above.
|
||||||
|
# Additional blocks can be provided for
|
||||||
|
# data that are not enabled, but the enabled model
|
||||||
|
# must have a corresponding parameter block.
|
||||||
|
wake_turbulence_parameters:
|
||||||
|
crespo_hernandez:
|
||||||
|
initial: 0.1
|
||||||
|
constant: 0.5
|
||||||
|
ai: 0.8
|
||||||
|
downstream: -0.32
|
55
server/floris_module/src/main.py
Normal file
55
server/floris_module/src/main.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from FlorisULSTU import FlorisULSTU
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# yaw_angles = np.zeros((4, 4))
|
||||||
|
# print("Yaw angle array initialized with 0's")
|
||||||
|
# print(yaw_angles)
|
||||||
|
#
|
||||||
|
# print("First turbine yawed 25 degrees for every atmospheric condition")
|
||||||
|
# yaw_angles[:, 0] = 25
|
||||||
|
# print(yaw_angles)
|
||||||
|
#
|
||||||
|
# fmodel.set(yaw_angles=yaw_angles)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
fmodel = FlorisULSTU("gch.yaml")
|
||||||
|
|
||||||
|
layout_x = list(map(float, input(f"Please enter x coordinates for turbines\n").split()))
|
||||||
|
layout_y = list(map(float, input(f"Please enter y coordinates for turbines\n").split()))
|
||||||
|
|
||||||
|
fmodel.set(
|
||||||
|
layout_x=layout_x, layout_y=layout_y,
|
||||||
|
)
|
||||||
|
|
||||||
|
wind_directions = list(map(float, input(f"Please enter wind_directions for experiments\n").split()))
|
||||||
|
wind_speeds = list(map(float, input(f"Please enter wind_speed for experiments\n").split()))
|
||||||
|
|
||||||
|
fmodel.set(
|
||||||
|
wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=[0.1] * len(wind_directions),
|
||||||
|
)
|
||||||
|
|
||||||
|
fmodel.run()
|
||||||
|
|
||||||
|
powers = fmodel.get_turbine_powers() / 1000.0
|
||||||
|
|
||||||
|
print("Dimensions of `powers`")
|
||||||
|
print(np.shape(powers))
|
||||||
|
|
||||||
|
N_TURBINES = fmodel.core.farm.n_turbines
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("Turbine powers for 8 m/s")
|
||||||
|
for i in range(2):
|
||||||
|
print(f"Wind condition {i}")
|
||||||
|
for j in range(N_TURBINES):
|
||||||
|
print(f" Turbine {j} - {powers[i, j]:7,.2f} kW")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("Turbine powers for all turbines at all wind conditions")
|
||||||
|
print(powers)
|
||||||
|
|
@ -1,38 +0,0 @@
|
|||||||
from fastapi import FastAPI, Depends
|
|
||||||
from fastapi.responses import RedirectResponse
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
from sqlalchemy.future import select
|
|
||||||
from database import SessionLocal, User, Base, engine
|
|
||||||
|
|
||||||
|
|
||||||
Base.metadata.create_all(bind=engine)
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import uvicorn
|
|
||||||
uvicorn.run(app, host="localhost", port=8080) # Изменено на localhost
|
|
@ -24,6 +24,7 @@ PyYAML==6.0.2
|
|||||||
rich==13.8.1
|
rich==13.8.1
|
||||||
shellingham==1.5.4
|
shellingham==1.5.4
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
|
SQLAlchemy==2.0.35
|
||||||
starlette==0.38.6
|
starlette==0.38.6
|
||||||
typer==0.12.5
|
typer==0.12.5
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
|
11
server/src/data/database.py
Normal file
11
server/src/data/database.py
Normal file
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
36
server/src/data/models.py
Normal file
36
server/src/data/models.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
|
||||||
|
from sqlalchemy.dialects.mysql import TIMESTAMP, TIME, VARCHAR
|
||||||
|
|
||||||
|
|
||||||
|
class Base(DeclarativeBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Weather(Base):
|
||||||
|
__tablename__ = "weather_data"
|
||||||
|
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True)
|
||||||
|
BarTrend: Mapped[str] = mapped_column(VARCHAR(255))
|
||||||
|
CRC: Mapped[str] = mapped_column(VARCHAR(255))
|
||||||
|
DateStamp: Mapped[datetime] = mapped_column(TIMESTAMP)
|
||||||
|
DewPoint: Mapped[float]
|
||||||
|
HeatIndex: Mapped[float]
|
||||||
|
ETDay: Mapped[float]
|
||||||
|
HumIn: Mapped[float]
|
||||||
|
HumOut: Mapped[float]
|
||||||
|
Pressure: Mapped[float]
|
||||||
|
RainDay: Mapped[float]
|
||||||
|
RainMonth: Mapped[float]
|
||||||
|
RainRate: Mapped[float]
|
||||||
|
RainStorm: Mapped[float]
|
||||||
|
RainYear: Mapped[float]
|
||||||
|
SunRise: Mapped[datetime] = mapped_column(TIME)
|
||||||
|
SunSet:Mapped[datetime] = mapped_column(TIME)
|
||||||
|
TempIn: Mapped[float]
|
||||||
|
TempOut: Mapped[float]
|
||||||
|
WindDir: Mapped[str] = mapped_column(VARCHAR(50))
|
||||||
|
WindSpeed: Mapped[float]
|
||||||
|
WindSpeed10Min: Mapped[float]
|
||||||
|
|
27
server/src/data/repository.py
Normal file
27
server/src/data/repository.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
|
from data.database import session_maker
|
||||||
|
from data.models import Weather
|
||||||
|
from data.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)
|
19
server/src/data/schemas.py
Normal file
19
server/src/data/schemas.py
Normal file
@ -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]
|
21
server/src/main.py
Normal file
21
server/src/main.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
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
|
||||||
|
from routers.weather_router import router as weather_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)
|
||||||
|
app.include_router(weather_router)
|
76
server/src/routers/floris_router.py
Normal file
76
server/src/routers/floris_router.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
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 data.repository import WeatherRepository
|
||||||
|
from data.schemas import SFlorisInputParams, SFlorisOutputData, SWeatherInfo
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||||
|
from floris_module.src import FlorisULSTU
|
||||||
|
|
||||||
|
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):
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
65
server/src/routers/floris_template_router.py
Normal file
65
server/src/routers/floris_template_router.py
Normal file
@ -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)
|
13
server/src/routers/weather_router.py
Normal file
13
server/src/routers/weather_router.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
from data.repository import WeatherRepository
|
||||||
|
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/api/weather",
|
||||||
|
tags=["Weather Api"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/")
|
||||||
|
async def get_weather():
|
||||||
|
return WeatherRepository().get_all()
|
||||||
|
|
67
server/static/style.css
Normal file
67
server/static/style.css
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-viewer {
|
||||||
|
background-color: #fff5e6;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-viewer img {
|
||||||
|
max-width: 400px;
|
||||||
|
max-height: 400px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-list {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-list ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-list li {
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-list li:hover {
|
||||||
|
background-color: #cceeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #82b1ff;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #6699ff;
|
||||||
|
}
|
85
server/templates/floris_templates/main.html
Normal file
85
server/templates/floris_templates/main.html
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Image Viewer</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.1/css/bootstrap.min.css" integrity="sha512-T584yQ/tdRR5QwOpfvDfVQUidzfgc2339Lc8uBDtcp/wYu80d7jwBgAxbyMh0a9YM9F8N3tdErpFI8iaGx6x5g==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.1/js/bootstrap.min.js" integrity="sha512-UR25UO94eTnCVwjbXozyeVd6ZqpaAE9naiEUBK/A+QDbfSTQFhPGj5lOR6d8tsgbBk84Ggb5A3EkjsOgPRPcKA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="row m-5">
|
||||||
|
<form class="col" method="post">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="btn-group" data-toggle="buttons">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">#</th>
|
||||||
|
<th scope="col">Wind Direction</th>
|
||||||
|
<th scope="col">Wind Speed</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for row in weather %}
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<label class="btn btn-outline-primary">{{ row.id }}
|
||||||
|
<input type="radio" class="btn-check"
|
||||||
|
name="options" id="option_{{ row.id }}" value="{{ row.id }}" autocomplete="off">
|
||||||
|
</label>
|
||||||
|
</th>
|
||||||
|
<td>{{ row.WindDir }}</td>
|
||||||
|
<td>{{ row.WindSpeed }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="input-group flex-nowrap">
|
||||||
|
<span class="input-group-text" id="addon-wrapping">X</span>
|
||||||
|
<input name="x" type="text" class="form-control" placeholder="X axis" aria-label="X axis" aria-describedby="addon-wrapping">
|
||||||
|
</div>
|
||||||
|
<div class="input-group flex-nowrap">
|
||||||
|
<span class="input-group-text" id="addon-wrapping">Y</span>
|
||||||
|
<input name="y" type="text" class="form-control" placeholder="Y axis" aria-label="Y axis" aria-describedby="addon-wrapping">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Submit</button>
|
||||||
|
</form>
|
||||||
|
<div class="col">
|
||||||
|
{{ data }}
|
||||||
|
{% if data is defined %}
|
||||||
|
<img id="display-image" src="/public/floris/{{ file_name }}" alt="Selected Image">
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function selectImage(imageName) {
|
||||||
|
const imgElement = document.getElementById('display-image');
|
||||||
|
imgElement.src = `/public/floris/${imageName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateImage() {
|
||||||
|
alert("Image generation functionality not implemented yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadImage() {
|
||||||
|
const imgElement = document.getElementById('display-image');
|
||||||
|
const imageName = imgElement.src.split('/').pop();
|
||||||
|
window.location.href = `/floris/download_image/${imageName}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user