diff --git a/server/floris_module/.gitignore b/server/floris_module/.gitignore index 2d62eb2..6a6f633 100644 --- a/server/floris_module/.gitignore +++ b/server/floris_module/.gitignore @@ -232,6 +232,8 @@ dmypy.json # Cython debug symbols cython_debug/ +src/.cache.sqlite + # 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 diff --git a/server/floris_module/requirements.txt b/server/floris_module/requirements.txt index 6a93f4f..bb9c456 100644 --- a/server/floris_module/requirements.txt +++ b/server/floris_module/requirements.txt @@ -20,3 +20,6 @@ scipy==1.14.1 shapely==2.0.6 six==1.16.0 tzdata==2024.2 +openmeteo-requests==1.3.0 +requests-cache==1.2.1 +retry-requests==2.0.0 diff --git a/server/floris_module/src/OpenMeteoClient.py b/server/floris_module/src/OpenMeteoClient.py new file mode 100644 index 0000000..deddd5d --- /dev/null +++ b/server/floris_module/src/OpenMeteoClient.py @@ -0,0 +1,40 @@ +import openmeteo_requests +import requests_cache +from retry_requests import retry + + +class OpenMeteoClient: + def __init__(self, timezone="GMT+0"): + self.timezone = timezone + + # Set up the Open-Meteo API client with cache and retry on error + self.cache_session = requests_cache.CachedSession('.cache', expire_after=3600) + self.retry_session = retry(self.cache_session, retries=5, backoff_factor=0.2) + self.openmeteo = openmeteo_requests.Client(session=self.retry_session) + + def fetch_weather_data(self, latitude, longitude, start_date, end_date): + url = "https://api.open-meteo.com/v1/forecast" + params = { + "latitude": latitude, + "longitude": longitude, + "hourly": ["wind_speed_10m", "wind_direction_10m"], + "timezone": self.timezone, + "start_date": start_date, + "end_date": end_date + } + responses = self.openmeteo.weather_api(url, params=params) + return responses + + def process_response(self, response): + # Process hourly data + hourly = response.Hourly() + hourly_wind_speed_10m = hourly.Variables(0).ValuesAsNumpy() + hourly_wind_direction_10m = hourly.Variables(1).ValuesAsNumpy() + + return hourly_wind_speed_10m.tolist(), hourly_wind_direction_10m.tolist() + + def get_weather_info(self, latitude, longitude, start_date, end_date): + responses = self.fetch_weather_data(latitude, longitude, start_date, end_date) + response = responses[0] + self.process_response(response) + return self.process_response(response) diff --git a/server/floris_module/src/main_with_api.py b/server/floris_module/src/main_with_api.py new file mode 100644 index 0000000..dac65d6 --- /dev/null +++ b/server/floris_module/src/main_with_api.py @@ -0,0 +1,69 @@ +import datetime + +import numpy as np +import matplotlib.pyplot as plt + +from FlorisULSTU import FlorisULSTU +from OpenMeteoClient import OpenMeteoClient + + + +# 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, + ) + + client = OpenMeteoClient() + + wind_directions = list() + wind_speeds = list() + + for i in range(len(layout_x)): + i_result = client.get_weather_info(layout_x[i], layout_y[i], datetime.datetime.now().date(), datetime.datetime.now().date()) + wind_speeds.extend(i_result[0]) + wind_directions.extend(i_result[1]) + + print(wind_directions) + print('test') + print(wind_speeds) + + 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) + diff --git a/server/requirements.txt b/server/requirements.txt index 8986ff1..ad67f03 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -29,6 +29,6 @@ starlette==0.38.6 typer==0.12.5 typing_extensions==4.12.2 uvicorn==0.30.6 -uvloop==0.20.0 watchfiles==0.24.0 websockets==13.1 +PyMySQL=1.1.1 diff --git a/server/src/data/schemas.py b/server/src/data/schemas.py index 3388350..6558a9d 100644 --- a/server/src/data/schemas.py +++ b/server/src/data/schemas.py @@ -1,3 +1,5 @@ +from datetime import date + from pydantic import BaseModel, Field from fastapi import Query @@ -9,9 +11,10 @@ class SWeatherInfo(BaseModel): class SFlorisInputParams(BaseModel): - weather_id: int layout_x: list[float] = Field(Query([])) layout_y: list[float] = Field(Query([])) + date_start: date + date_end: date class SFlorisOutputData(BaseModel): diff --git a/server/src/routers/floris_router.py b/server/src/routers/floris_router.py index 7bbca30..ddccce5 100644 --- a/server/src/routers/floris_router.py +++ b/server/src/routers/floris_router.py @@ -12,6 +12,7 @@ from data.schemas import SFlorisInputParams, SFlorisOutputData, SWeatherInfo sys.path.append(str(Path(__file__).parent.parent.parent)) from floris_module.src import FlorisULSTU +from floris_module.src.OpenMeteoClient import OpenMeteoClient FLORIS_IMAGES_PATH = Path(__file__).parent.parent.parent / "public" / "floris" @@ -31,15 +32,26 @@ async def get_windmill_data( detail="Length of layout x and y must be the same", ) - atmosphere_param: SWeatherInfo = WeatherRepository().get_by_id(data.weather_id) - fmodel = FlorisULSTU() + + client = OpenMeteoClient() + + wind_directions = list() + wind_speeds = list() + + for i in range(len(data.layout_x)): + i_result = client.get_weather_info(data.layout_x[i], data.layout_y[i], data.date_start, data.date_end) + wind_speeds.extend(i_result[0]) + wind_directions.extend(i_result[1]) + fmodel.set( layout_x=data.layout_x, layout_y=data.layout_y, - wind_directions=[atmosphere_param.WindDir], - wind_speeds=[atmosphere_param.WindSpeed], + wind_directions=wind_directions, + wind_speeds=wind_speeds, + turbulence_intensities=[0.1] * len(wind_directions) ) + fmodel.run() data = fmodel.get_turbine_powers()[0].tolist()