floris_design #3
@ -1,8 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<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="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>
|
||||
</module>
|
12
.idea/dataSources.xml
Normal file
12
.idea/dataSources.xml
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
Normal file
6
.idea/jsLibraryMappings.xml
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>
|
@ -1,4 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
6
.idea/other.xml
Normal file
6
.idea/other.xml
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
Normal file
14
.idea/webResources.xml
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 config = {
|
||||
entry: './src/index.tsx',
|
||||
entry: './routers/index.tsx',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'main.js',
|
||||
|
@ -20,8 +20,13 @@
|
||||
```zsh
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
4. Start FastAPI process
|
||||
|
||||
4. Enter src directory
|
||||
```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
|
||||
shellingham==1.5.4
|
||||
sniffio==1.3.1
|
||||
SQLAlchemy==2.0.35
|
||||
starlette==0.38.6
|
||||
typer==0.12.5
|
||||
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…
Reference in New Issue
Block a user