A differentiable fluid simulation pipeline for Wind Turbine Shape Optimization
- Simulate 2D smooth wind flow
- Ensure differentiability of the above with parametrised 2D airfoils
- Use Blade Element Momentum (BEM) methodology for connecting slice geometry to results for 3D wind turbines, incorporating BEM correctors
- Leverage differentiability to optimize parameterised shape of each slice to maximise turbine efficiency while maintaining some design & physical constraints
- Enhance wind flow simulation with low order RANS
- Make a virtual environment for project using
python -m venv /path/to/venv - Install turbodiff in editable mode using
pip install -e '.[dev]'from project root directory - (Linux with NVIDIA GPU only) For GPU acceleration, install CUDA-enabled JAX:
Or use:
pip install --upgrade "jax[cuda12_pip]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.htmlpip install -e '.[dev,cuda]' - (Windows) JAX CPU-only is installed by default. GPU support is experimental.
- Run
pre-commit install, this will ensure that your code is reformatted according to the Black formatter on commit - Run
pre-commit run --all-filesto run black, Ruff, and prettier before committing
- Add tests in test directory
- Run tests using command
pytest
TurboDiff now supports saving sessions and airfoils in Postgres (Neon compatible).
- Create a
.envfile at the repo root with:TURBODIFF_DATABASE_URL=postgresql://USER:PASSWORD@HOST/DB?sslmode=require - Install dependencies (includes
psycopgandpython-dotenv).
Schema changes are applied via patch files. On startup, TurboDiff applies any new patches that are not yet recorded in the database.
- Patch directory:
src/turbodiff/db/patches - Applied patch IDs are stored in
schema_migrations - Add new patches as
0002_*.sql,0003_*.sql, ...
Sessions and airfoils are saved automatically when creating sessions. Metrics can be saved later.
curl -X POST http://localhost:8000/sessions \
-H "Content-Type: application/json" \
-d '{
"user_id": "00000000-0000-0000-0000-000000000001",
"fidelity": "medium",
"sim_time": 2.0,
"dt": 0.01,
"cell_size": 0.01,
"diffusion": 0.001,
"viscosity": 0.0,
"boundary_type": 1,
"inflow_velocity": 2.0,
"stream_fps": 30.0,
"stream_every": 1,
"angle_of_attack": 5.0,
"cst_upper": [0.1, 0.2, 0.3],
"cst_lower": [-0.1, -0.2, -0.3],
"airfoil_offset_x": 0.2,
"airfoil_offset_y": 0.32,
"chord_length": 0.25,
"num_cst_points": 100,
"mask_sharpness": 50.0
}'curl -X POST http://localhost:8000/sessions/{session_id}/save \
-H "Content-Type: application/json" \
-d '{
"user_id": "00000000-0000-0000-0000-000000000001",
"cl": 1.0,
"cd": 0.1,
"lift": 2.0,
"drag": 0.2,
"angle_of_attack": 4.0
}'curl -X POST http://localhost:8000/optimize/sessions \
-H "Content-Type: application/json" \
-d '{
"user_id": "00000000-0000-0000-0000-000000000001",
"fidelity": "low",
"num_iterations": 30,
"learning_rate": 0.005,
"num_sim_steps": 80,
"cst_upper": [0.18, 0.22, 0.20, 0.18, 0.15, 0.12],
"cst_lower": [-0.10, -0.08, -0.06, -0.05, -0.04, -0.03]
}'curl -X POST http://localhost:8000/optimize/sessions/{session_id}/save \
-H "Content-Type: application/json" \
-d '{
"user_id": "00000000-0000-0000-0000-000000000001",
"cst_upper": [0.2, 0.22, 0.2],
"cst_lower": [-0.1, -0.08, -0.06],
"chord_length": 0.25,
"angle_of_attack": 3.0,
"cl": 1.2,
"cd": 0.08,
"lift": 2.5,
"drag": 0.2
}'Returns the final simulation result (from cache or database) without re-running
the simulation. Requires the session to be of type simulate.
curl -X GET http://localhost:8000/sessions/{session_id}/result \
-H "Authorization: Bearer <FIREBASE_ID_TOKEN>"Response (same JSON shape as the WebSocket stream):
{
"meta": {
"session_id": "...",
"height": 128,
"width": 256,
"cell_size": 0.04,
"chord_length": 1.0,
"airfoil_offset_x": 1.2,
"airfoil_offset_y": 2.56,
"time": 0.0,
"step": 0,
"cl": 0.123,
"cd": 0.045,
"l_d": 2.73
},
"fields": {
"u": [],
"v": [],
"curl": [],
"pressure": [],
"solid": [],
"tracer": []
}
}Note: When fetched from the database (not cache), the
fieldsarrays are empty because raw grid data is not persisted. The cached version (if the WebSocket ran in this server instance) contains full field data.
Returns the final optimization result (from cache or database) without
re-running the optimization loop. Requires the session to be of type optimize.
curl -X GET http://localhost:8000/optimize/sessions/{session_id}/result \
-H "Authorization: Bearer <FIREBASE_ID_TOKEN>"Response (same JSON shape as the WebSocket complete message):
{
"type": "complete",
"meta": {
"total_iterations": 30,
"final_cl": 0.123,
"final_cd": 0.045,
"final_cl_cd": 2.73,
"final_drag": 0.012,
"final_loss": 0.0
},
"shape": {
"cst_upper": [0.2, 0.22, 0.2, 0.18, 0.15, 0.12],
"cst_lower": [-0.1, -0.08, -0.06, -0.05, -0.04, -0.03],
"airfoil_x": [0.0, 0.01, "..."],
"airfoil_y_upper": [0.0, 0.05, "..."],
"airfoil_y_lower": [0.0, -0.03, "..."]
},
"initial_shape": {
"cst_upper": [0.18, 0.22, 0.20, 0.18, 0.15, 0.12],
"cst_lower": [-0.10, -0.08, -0.06, -0.05, -0.04, -0.03]
}
}Run the Postgres-backed repository test (uses TURBODIFF_DATABASE_URL):
TURBODIFF_DATABASE_URL="postgresql://USER:PASSWORD@HOST/DB?sslmode=require" \
pytest tests/test_storage_repository_postgres.py -m integrationClean up the test data after running the integration test:
TURBODIFF_DATABASE_URL="postgresql://USER:PASSWORD@HOST/DB?sslmode=require" \
python scripts/cleanup_neon_test_data.py- Modify files in docs
- Compile docs by going into
docs/and runningmake htmlcommand
Run the WebSocket streaming server:
uvicorn turbodiff.api.app:app --reloadCreate a session with a POST request (frontend defines conditions and shape):
curl -X POST http://localhost:8000/sessions \
-H "Content-Type: application/json" \
-d '{
"fidelity": "medium",
"sim_time": 2.0,
"dt": 0.01,
"cell_size": 0.01,
"diffusion": 0.001,
"viscosity": 0.0,
"boundary_type": 1,
"inflow_velocity": 2.0,
"stream_fps": 30.0,
"stream_every": 1,
"angle_of_attack": 5.0,
"cst_upper": [0.1, 0.2, 0.3],
"cst_lower": [-0.1, -0.2, -0.3],
"airfoil_offset_x": 0.2,
"airfoil_offset_y": 0.32,
"chord_length": 0.25,
"num_cst_points": 100,
"mask_sharpness": 50.0
}'Then connect to ws://localhost:8000/ws/{session_id} and the server will stream
cell-centered velocity and pressure fields as JSON arrays.
- PascalCase for classes
- snake_case for variables and functions
- imports at the top