diff --git a/_config.yml b/_config.yml
index 95970070..e63bd734 100644
--- a/_config.yml
+++ b/_config.yml
@@ -16,7 +16,7 @@ bootwatch: yeti
# Build settings
markdown: kramdown
highlighter: rouge
-gems:
+plugins:
- jekyll-feed
- jekyll-redirect-from
- jekyll-seo-tag
@@ -86,6 +86,15 @@ defaults:
seo:
type: "WebPage"
+- scope:
+ path: _gsoc
+ type: gsoc
+ values:
+ layout: gsoc
+ sectionid: gsoc
+ seo:
+ type: "WebPage"
+
collections:
docs:
permalink: /:collection/:path/
@@ -102,6 +111,9 @@ collections:
su2gui:
permalink: /:collection/:path/
output: true
+ gsoc:
+ permalink: /:collection/:path/
+ output: true
posts:
permalink: /blog/:year/:month/:day/:title/
output: true
diff --git a/_data/gsoc.yml b/_data/gsoc.yml
new file mode 100644
index 00000000..fad92a45
--- /dev/null
+++ b/_data/gsoc.yml
@@ -0,0 +1,11 @@
+- title: GSOC
+ gsoc:
+ - Introduction
+
+- title: Participation
+ gsoc:
+ - Participation
+
+- title: Assignments
+ gsoc:
+ - Assignments
diff --git a/_data/tutorials.yml b/_data/tutorials.yml
index 76c6f2a0..873857e1 100644
--- a/_data/tutorials.yml
+++ b/_data/tutorials.yml
@@ -17,7 +17,9 @@
- Unsteady_NACA0012
- UQ_NACA0012
- NICFD_nozzle
+ - NICFD_nozzle_datadriven
- Aachen_Turbine
+ - Actuator_Disk
- title: Incompressible Flow
tutorials:
@@ -32,6 +34,7 @@
- Inc_Species_Transport_Composition_Dependent_Model
- Inc_Von_Karman
- Inc_Turbulent_Bend
+ - Inc_Urban_City
- title: Structural Mechanics
tutorials:
@@ -39,7 +42,7 @@
- Linear_Dynamics
- Nonlinear_Elasticity
- Multiple_Material
-
+
- title: Multiphysics
tutorials:
- Static_FSI
@@ -47,7 +50,8 @@
- Static_CHT
- Inc_Heated_Cylinders_Unsteady
- SS_CR_CHT
- - Inc_Combustion
+ - Inc_Combustion
+ - TFC_python
- title: Design Features
tutorials:
diff --git a/_gsoc/Assignments.md b/_gsoc/Assignments.md
new file mode 100644
index 00000000..a1a3ce60
--- /dev/null
+++ b/_gsoc/Assignments.md
@@ -0,0 +1,41 @@
+---
+title: Student Assignments
+permalink: /gsoc/Assignments/
+---
+
+**Welcome to SU2 - GSOC!**
+What is Google Summer of Code?
+
+[Google Summer of Code](https://summerofcode.withgoogle.com/)
+
+
+## SU2 introduction assignments
+
+To help newcomers start with SU2 and to help GSOC mentors with evaluating the level of students who would like to participate in Google Summer of Code, we have prepared a couple of introduction assignments. These assignments have to be made in the order they are given. These assignments give us an indication of your familiarity with SU2 and the SU2 code. These assignments, together with your active participation in the SU2 community, will be taken into account when deciding on GSOC projects.
+
+## Assignment 1: Compile SU2
+
+- Clone SU2 from github [SU2](https://github.com/su2code/SU2) on your system and compile it [compile instructions](https://su2code.github.io/docs_v7/Build-SU2-Linux-MacOS/) with different options, and run some tutorials [Tutorials](https://su2code.github.io/tutorials/home/). Get a proper understanding of the input and output of SU2.
+- Deliverable: None
+
+## Assignment 2: Set up a test case from scratch
+
+- Generate a 2D mesh for an axisymmetric, steady-state, turbulent jet case (for instance with [gmsh](https://gmsh.info/)), setup the configuration file, run the simulation, and extract results.
+- Deliverable: Testcase and small report (markdown) describing motivation for set-up, configuration options, convergence history, comparison with experimental values.
+Reference paper that could be used for comparison [report](https://www.researchgate.net/publication/254224677_Investigation_of_the_Mixing_Process_in_an_Axisymmetric_Turbulent_Jet_Using_PIV_and_LIF)
+
+## Assignment 3: Python wrapper test case
+
+- Set up a problem in the python wrapper (compile with python support) and run a test case.
+Testcase for the python wrapper: [flatplate](https://github.com/su2code/SU2/blob/master/TestCases/py_wrapper/flatPlate_unsteady_CHT/launch_unsteady_CHT_FlatPlate.py)
+- Deliverable: Testcase and small report describing the test case and showing the results.
+
+## Assignment 4: Modification of the python wrapper setup
+
+- Enable a spatially varying wall temperature for a steady-state compressible turbulent flat plate testcase.
+- Deliverable: Testcase and small report describing the results.
+
+## Assignment 5: Addition of new volume output:
+
+- Add the local speed of sound as computed by SU2 in the volume output (paraview files) and the screen output. Run the turbulent test case from point 2 with this new volume and screen output enabled.
+- Deliverable: explain implementation, show the history output of the new screen output and show some image with the volume output.
diff --git a/_gsoc/Introduction.md b/_gsoc/Introduction.md
new file mode 100644
index 00000000..f402af6d
--- /dev/null
+++ b/_gsoc/Introduction.md
@@ -0,0 +1,62 @@
+---
+title: Ideas List for SU2 Google Summer of Code
+permalink: /gsoc/Introduction/
+---
+
+**Welcome to SU2 - GSOC!**
+
+This is the updated ideas list for GSOC 2026. If you are interested in participating in [Google Summer of Code](https://summerofcode.withgoogle.com/about) with the SU2 team, then please read the page on [participation](https://su2code.github.io/gsoc/Participation/). The projects listed below have been tuned to fit within the google summer of code program and they have mentors assigned to them. We can also accept personal ideas beyond the ones presented below but you need to convince one of the mentors to support you. We also need you to be proficient in SU2 and have some kind of technical background beyond general computer science (studying physics, mechanical engineering, aerospace engineering,...).
+
+## Project BP: Adding pressure-based solver
+Project Description (max. 5 Sentences)
+The pressure-based solver has been requested for a long time. This solver is an important addition to the CFD solvers, especially for low Mach and incompressible flows. People have worked on it (detailed documentation available), and there is a branch that contains a working version, but this was never finalized and added to the main SU2 branch. Hence, the project's objective is to evaluate the current status of attempts, and propose a strategy for getting the pressure-based solver in the latest version of SU2.
+Expected Outcome (deliverables): Finalize pressure-based solver, validate with test cases, tutorial and merge the PR.
+- Skills Required: C++, experience with CFD and numerical methods
+- Possible Mentors: Nitish Anand and Edwin van der Weide
+- Expected Project Size: 175 hrs/medium
+- Difficulty rating: **medium-hard** (needs experience with Computational Fluid Dynamics)
+
+## Project GPU: Continuation of GPU acceleration in SU2
+Project Description (max. 5 Sentences)
+The SU2 code relies heavily on sparse linear algebra. In this area, there is significant speed-up potential with the adoption of GPU-based processing, as was demonstrated in the GSOC 24 project that applied CUDA to sparse matrix-vector multiplications in SU2. The objective of this project is to move more linear algebra operations to GPU in order to avoid host-device communication bottlenecks within the sparse linear system solver.
+Expected Outcome (deliverables): Make SU2’s sparse linear solver GPU-native, i.e. minimal host-device communication after the initial setup of the system.
+- Skills Required C++
+- Possible Mentors Pedro Gomes (lead), Ole Burghardt
+- Expected Project Size (90 hrs/ small , 175 hrs/medium, 350 hrs/large): 175 hrs (medium)
+- Difficulty rating: **medium**
+
+## Project AMR: Quick Adaptive Mesh refinement for 2D testcases
+Project Description (max. 5 Sentences)
+Many users have asked for adaptive mesh refinement capabilities. Several research groups are working on this. The aim of this project is to introduce a quick and easy adaptive mesh refinement that simply reads an existing results file and adaptively refines the meshes based on the value of a field.
+Expected Outcome (deliverables): SU2_AMR, an added executable that simply splits 2D quad and triangle cells
+- Skills Required: C++
+- Possible Mentors: Nijso Beishuizen (lead)
+- Expected Project Size (90 hrs/ small , 175 hrs/medium, 350 hrs/large): 175 hrs (medium)
+- Difficulty rating: **medium**
+
+## Project CMPLX: Performance Optimization of Complex Arithmetic in SU2
+Project Description (max. 5 Sentences)
+Complex arithmetic operations currently cause significant performance degradation in SU2 when features requiring complex numbers are enabled. This limitation affects the efficiency of certain solver capabilities and restricts their practical application in industrial-scale problems. Preliminary observations suggest that complex arithmetic is a primary bottleneck, but systematic profiling is needed to confirm and quantify these losses. The project's objective is to profile the solver to identify performance hotspots, validate that complex arithmetic is the root cause, and develop a custom complex arithmetic library optimised for SU2's specific use cases. This work will enable more efficient execution of complex-number-dependent features without compromising computational performance.
+Expected Outcome (deliverables): Performance profiling report, custom complex arithmetic library (if validated as necessary), benchmark comparisons demonstrating speedup, integration into SU2 codebase, and documentation with usage guidelines.
+- Skills Required: C++
+- Possible Mentors: Joshua A. Kelly (lead), Harsh Mishra
+- Expected Project Size (90 hrs/ small , 175 hrs/medium, 350 hrs/large): 175 hrs (medium)
+- Difficulty rating: **medium**
+
+## Project PIML: Towards physics-informed machine learning with SU2
+Project Description (max. 5 Sentences)
+SU2 uses algorithmic differentiation (AD) for the adjoint solver and has the ability to use multi-layer perceptrons in data-driven equation of state models through the [MLPCpp](https://github.com/EvertBunschoten/MLPCpp.git) submodule. The aim of this project is to combine these two functionalities to enable physics-informed machine learning (PIML) in SU2 by updating the weights and biases of multi-layer perceptrons using AD for sensitivity calculation. PIML would enable data-driven turbulence modeling, solving partial differential equations without a mesh, and open the door to many other interesting research opportunities.
+Expected Outcome (deliverables): Demonstration of training a MLP for a reference data set within SU2 and comparison, MLP training library including at least one commonly used training algorithm (e.g. Adam), and documentation explaining usage.
+- Skills Required: C++, experience with machine learning
+- Possible Mentors: Evert Bunschoten (lead)
+- Expected Project Size (90 hrs/ small , 175 hrs/medium, 350 hrs/large): 175 hrs (medium)
+- Difficulty rating: **medium-hard**
+
+## Project FWH: Generalizing FWH-Based Aeroacoustic Noise Prediction
+This project aims to generalize a Python tool that implements Farassat’s 1A formulation of the Ffowcs Williams-Hawkings (FWH) equation for far-field noise prediction. While originally developed for tandem cylinder test cases and recently extended to airfoils, the current implementation is limited by case-specific logic. The primary objective is to refactor the codebase into a robust, geometry-agnostic framework capable of handling diverse and complex flow configurations. Test cases should be included in the regression tests.
+Expected Outcome (deliverables): A stand-alone Python code.
+- Skills Required: Python
+- Possible Mentors: Huseyin Ozdemir, (lead) Nijso Beishuizen
+- Expected Project Size (90 hrs/ small, 175 hrs/medium, 350 hrs/large): 175 hrs (medium)
+- Difficulty rating: **medium**
+-
diff --git a/_gsoc/Participation.md b/_gsoc/Participation.md
new file mode 100644
index 00000000..3e851834
--- /dev/null
+++ b/_gsoc/Participation.md
@@ -0,0 +1,39 @@
+---
+title: Student Participation
+permalink: /gsoc/Participation/
+---
+
+**Welcome to SU2 - GSOC!**
+
+## What is Google Summer of Code?
+
+[Google Summer of Code](https://summerofcode.withgoogle.com/)
+
+Google Summer of Code is a program sponsored by Google to let students interested in Open Source work on cool and challenging open source projects. The basic idea is that you will work together with a mentor on a project. We have selected a couple of main topics for you but it is up to you to write a more complete project proposal. If you have ideas of your own, that is fine too but you will need to find a mentor to supervise such a project.
+
+If you would like to apply, please make sure that you have subscribed to [CFD-online](https://www.cfd-online.com/Forums/su2/) and github, and additionally join the developers team on slack (see our main website [su2code](https://su2code.github.io/). In that way you can stay informed about SU2, and our GSOC involvement.
+
+If you are interested in applying for GSOC with an SU2 project, please do the following:
+1. send an application email to gsoc@su2foundation.org with some personal details, education background and motivation.
+2. become a member of our slack channel and subscribe to the **general** and **gsoc** subchannel of SU2. Please introduce yourself :-)
+
+## To Apply:
+To be considered as a GSOC student for an SU2 project, it is not sufficient to simply write a proposal and send it to the google website. We will not accept students who never contacted us or did not finish the assignments. We encourage students to participate in code development by fixing bugs or working on features. The minimum requirements to get accepted by the SU2 team for a GSOC project are:
+
+- Create a brief resume with your contact details, your education and code experience. If we cannot contact you, *we will not contact you*.
+- Briefly write about your experience and interests in Computational Fluid Dynamics and SU2, and the specific project you would like to work on (if you know this already)
+- Briefly write about your current work schedule: are you studying/working, how will you manage the time, etc..
+- Send it to gsoc@su2foundation.org
+- Make the assignments on the assignment page and send us your github page with your results.
+- work on fixing things in SU2
+
+Then in the last stage:
+
+- **Together with a mentor** you will create a project proposal and a planning with a timeline containing a breakdown of the project in parts with periodic deliverables/milestones.
+
+## Evaluation
+Note that applying does not mean acceptance into the program. We will carefully consider your application to see if you are capable of the job, taking into account your experience and availability. We heavily weigh your participation and visibility in the introduction phase.
+Please note that experience with SU2 is required. A merged Pull Request on github is highly recommended experience. A pull request for a tutorial or validation testcase is also acceptable and will count as experience.
+
+## Use of AI
+We allow usage of AI tools to *assist* you with your work. However, we do not allow the use of AI in vibe-coding where you let AI generate code that you do not understand. We also do not allow AI for automatic discussions with mentors or other users. AI is your assistant, not your replacement. If you do everything with AI, we might as well do it ourselves without you.
diff --git a/_includes/gsoc_nav.html b/_includes/gsoc_nav.html
new file mode 100644
index 00000000..4065d44b
--- /dev/null
+++ b/_includes/gsoc_nav.html
@@ -0,0 +1,22 @@
+
+{% for section in site.data.gsoc %}
+
+
+
+
+ {% for item in section.gsoc %}
+ {% assign item_url = item | prepend:"/gsoc/" | append:"/" %}
+ {% assign p = site.gsoc | where:"url", item_url | first %}
+ {{ p.title }}
+ {% endfor %}
+
+
+
+{% endfor %}
+
diff --git a/_includes/gsoc_section_nav.html b/_includes/gsoc_section_nav.html
new file mode 100644
index 00000000..f5feadf8
--- /dev/null
+++ b/_includes/gsoc_section_nav.html
@@ -0,0 +1,52 @@
+{% comment %}
+Map grabs the doc sections, giving us an array of arrays. Join, flattens all
+the items to a comma delimited string. Split turns it into an array again.
+{% endcomment %}
+{% assign gsoc = site.data.gsoc | map: 'gsoc' | join: ',' | split: ',' %}
+
+{% comment %}
+Because this is built for every page, lets find where we are in the ordered
+document list by comparing url strings. Then if there's something previous or
+next, lets build a link to it.
+{% endcomment %}
+
+{% for document in gsoc %}
+ {% assign document_url = document | prepend:"/gsoc/" | append:"/" %}
+ {% if document_url == page.url %}
+
diff --git a/_layouts/gsoc.html b/_layouts/gsoc.html
new file mode 100644
index 00000000..1276685d
--- /dev/null
+++ b/_layouts/gsoc.html
@@ -0,0 +1,55 @@
+---
+layout: default
+---
+
+
+
+
+ {% include gsoc_nav.html %}
+
+
+
+
+
+
+
+
+
diff --git a/_tutorials/compressible_flow/ActuatorDisk_VariableLoad/ActuatorDisk_VariableLoad.md b/_tutorials/compressible_flow/ActuatorDisk_VariableLoad/ActuatorDisk_VariableLoad.md
index f532d610..c1f7c967 100644
--- a/_tutorials/compressible_flow/ActuatorDisk_VariableLoad/ActuatorDisk_VariableLoad.md
+++ b/_tutorials/compressible_flow/ActuatorDisk_VariableLoad/ActuatorDisk_VariableLoad.md
@@ -55,8 +55,8 @@ The global propeller data are:
- Advance Ratio = 2.81487
- Radius = 2.5146 m
-The thrust coefficient is defined using the "Renard" definition: the reference force is
, where *n* are the propeller rounds per second and *D* is the propeller diameter
-The advance ratio is defined as
.
+The thrust coefficient is defined using the "Renard" definition: the reference force is $$\rho n^2D^4$$, where *n* are the propeller rounds per second and *D* is the propeller diameter
+The advance ratio is defined as $$J=\frac{V_\infty}{nD}$$.
### Mesh Description
@@ -167,16 +167,16 @@ The `MARKER_ACTDISK` option, as the same for the configuration file, is used to
The `CENTER` option contains the coordinates of the actuator disk center, expressed in the grid reference system.
The `AXIS` option contains the components of the unit vector normal to the actuator disk surface.
The `RADIUS` option is used to specify the actuator disk radius.
-The `ADV_RATIO` option contains the advance ratio of the propeller defined as
, where *n* are the propeller rounds per second and *D* is the propeller diameter.
+The `ADV_RATIO` option contains the advance ratio of the propeller defined as $$J=\frac{V_\infty}{nD}$$, where *n* are the propeller rounds per second and *D* is the propeller diameter.
The `NROW` option isused to indicate the number of radial stations of the actuator disk in which we assign the load distribution.
The next row is a dummy row, so it is skipped.
Then there are 4 columns containing respectively:
-- The non dimensional radial station
-- The thrust coefficient distribution
-- The power coefficient distribution
-- The radial force coefficient distribution
+- The non dimensional radial station $$\overline{r}=\frac{r}{R}$
+- The thrust coefficient distribution $$\frac{\mathrm{d}C_T}{\mathrm{d}\overline{r}}$$
+- The power coefficient distribution $$\frac{\mathrm{d}C_P}{\mathrm{d}\overline{r}}$
+- The radial force coefficient distribution $$\frac{\mathrm{d}C_R}{\mathrm{d}\overline{r}}$$
-These coefficients are defined using the "Renard" definition: the reference force is
, while the reference power is reference force is
+These coefficients are defined using the "Renard" definition: the reference force is $$\rho n^2D^4$$, while the reference power is reference force is $$\rho n^3D^5$$
*It is possible to append other propellers data at the end of the input file. Note that the order and the format of the options should not be changed.*
@@ -195,13 +195,13 @@ This script allows the user to use the `VARIABLE_LOAD` actuator disk type also w
The input is interactive, and requires the following data:
1. Number of radial stations (where local data should be generated).
-2. CT: the total thrust coefficient defined using the "Renard" definition.
-3. R: The propeller radius expressed in meters.
-4. r_hub: the hub radius expressed in meters.
-5. J: the advance ratio.
-6. Vinf: the free-stream velocity expressed in m/s.
+2. $$CT$$: the total thrust coefficient defined using the "Renard" definition.
+3. $$R$$: The propeller radius expressed in meters.
+4. $$r_{\textrm{hub}}$$: the hub radius expressed in meters.
+5. $$J$$: the advance ratio.
+6. $$V_{\textrm{inf}}$$: the free-stream velocity expressed in m/s.
7. Here, the script asks if you want to use the tip loss Prandtl correction (*yes* is the default choise).
-8. N: if you chose yes in the previous stage, it requires also the number of propeller blades.
+8. $$N$$: if you chose yes in the previous stage, it requires also the number of propeller blades.
Once the input is given, the script provides 3 plots showing the tip loss Prandtl correction function, the axial and rotational interference factors and the thrust and power coefficients distributions along the non dimentional radius.
The script also provides 2 files:
diff --git a/_tutorials/compressible_flow/NICFD_nozzle/NICFD_nozzle_datadriven.md b/_tutorials/compressible_flow/NICFD_nozzle/NICFD_nozzle_datadriven.md
new file mode 100644
index 00000000..6bac3dcf
--- /dev/null
+++ b/_tutorials/compressible_flow/NICFD_nozzle/NICFD_nozzle_datadriven.md
@@ -0,0 +1,115 @@
+---
+title: Non-ideal compressible flows with physics-informed neural networks
+permalink: /tutorials/NICFD_nozzle_datadriven/
+written_by: EvertBunschoten
+for_version: 8.4.0
+solver: RANS
+requires: SU2_CFD SU2_DataMiner
+complexity: advanced
+follows:
+---
+
+## Goals
+
+Upon completing this tutorial, the user will be familiar with performing simulations of nonideal compressible fluids through the use of physics-informed neural networks. The flow is simulated through the same supersonic convergent-divergent nozzle as in the [tutorial](https://su2code.github.io/tutorials/NICFD_nozzle/) for nonideal compressible fluid flows.
+The following capabilities of will be showcased in this tutorial:
+- Using [SU2 DataMiner](https://github.com/EvertBunschoten/SU2_DataMiner.git) to train physics-informed neural networks for fluid simulations.
+- Data-driven equation of state with multi-layer perceptrons.
+- Giles boundary conditions
+
+The intent of this tutorial is to explain how to use the data-driven equation of state in SU2 for fluid simulations and how to train the neural network(s) required for modeling the fluid properties. The fluid used in this tutorial is Siloxane MM, but the methods explained in this tutorial can also be repeated for different fluids available within CoolProp.
+
+## Background
+
+The data-driven equation of state in SU2 can be used to calculate the thermodynamic state properties of the fluid during CFD calculations. The method uses an equation of state based on entropy potential, allowing for the thermodynamic state variables to be directly calculated from the density and internal energy, which can be directly obtained from the solution of the flow transport equations.
+
+The thermodynamic state variables are calculated from the Jacobian and Hessian of entropy w.r.t. density and internal energy. The data-driven fluid model in SU2 uses a physics-informed neural network to calculate the entropy Jacobian and Hessian such that thermodynamic consistency is maintained.
+
+The data-driven, entropy-based equation of state is discussed more in detail in [this paper](https://doi.org/10.1016/j.compfluid.2025.106932).
+
+## Resources and Set-Up
+
+You can find the resources for this tutorial in the folder [compressible_flow/NICFD_nozzle/PhysicsInformed](https://github.com/su2code/Tutorials/tree/master/compressible_flow/NICFD_nozzle/PhysicsInformed) in the [tutorial repository](https://github.com/su2code/Tutorials).
+You will need the python scripts and the [nozzle contour file](https://github.com/su2code/Tutorials/tree/master/compressible_flow/NICFD_nozzle/PhysicsInformed/nozzle_curve.csv) which describes the shape of the nozzle. The SU2 config file and mesh will be automatically generated using the scripts in this tutorial.
+
+This tutorial requires [SU2 DataMiner](https://github.com/EvertBunschoten/SU2_DataMiner.git) to run. Follow the installation instructions on the repository page, and install the required python packages. To be able to use the data-driven fluid model in SU2, compile SU2 with the following command:
+
+```
+meson.py build -Denable-mlpcpp=true
+```
+
+which will download the [MLPCpp](https://github.com/EvertBunschoten/MLPCpp.git) submodule enabling the evaluation of deep, dense multi-layer perceptrons in SU2.
+
+
+## Tutorial
+
+The following steps explain how to train physics-informed neural networks for data-driven fluid simulations in SU2.
+
+### 1. Generate SU2 DataMiner Configuration
+Similar to SU2, SU2 DataMiner uses a configuration file to store information regarding the type of fluid, the resolution of the training data set, and the hyperparameters of the networks. Running the script [0:generate_config.py](https://github.com/su2code/Tutorials/tree/master/compressible_flow/NICFD_nozzle/PhysicsInformed/0:generate_config.py) generates the SU2 DataMiner configuration used in this tutorial and is saved as a binary file named ```SU2DataMiner_MM.cfg```.
+
+### 2. Generate Training Data
+The thermodynamic state data used to train the network for the data-driven fluid simulation is generated using the Helmholtz equation of state evalauted through the python module for CoolProp. The thermodynamic state data are generated on a density-static energy grid for the gas and supercritical phase between the minimum and maximum density of the fluid supported by CoolProp. The ranges for density and energy or pressure and temperature within which training data are generated can be manually specified.
+
+By running the script [1:generate_fluid_data.py](https://github.com/su2code/Tutorials/tree/master/compressible_flow/NICFD_nozzle/PhysicsInformed/1:generate_fluid_data.py) will generate the thermodynamic state data used for the training of the network and generate contour plots of the temperature, pressure, and speed of sound. The complete set of thermodynamic state data is stored in the file titled *fluid_data_full.csv*. 80% of the randomly sampled fluid data is used to update the weights of the network during training, 10% is used to monitor the convergence of the training process, and the remaining 10% is used to validate the accuracy of the network upon completion of the training process. The complete data set contains approximately 2.3e5 unique data points.
+
+
+Figure (1): Section of training data set near the critial point ('cp').
+
+
+### 3. Train physics-informed neural network
+The network used in this tutorial uses two hidden layers with 12 nodes each. The exponential function is used as the hidden layer activation function. This is an unusual choice, but is motivated by the fact that it reduces the computational cost required to calculate the network Jacobian and Hessian during the CFD solution process.
+The training process uses an exponential decay function for the learning rate, with an initial value of 1e-3. During each update step, the weights and biases of the network are adjusted according to the value of the loss function evaluated on a batch of 64 training data points. The hidden layer architecture and learning rate of the network used in this tutorial were selected through trial and error.
+More details regarding the training method are presented in the [literature](https://doi.org/10.1016/j.compfluid.2025.106932).
+
+The training progress can be followed from the terminal, but can also be monitored from the training convergence plots that are periodically updated under ```Worker_0/Model_0/```.
+
+After training, the weights and biases of the network are stored in the SU2 DataMiner configuration.
+
+IMAGE: training history plot, predicted vs training data
+
+
+Figure (2): Training convergence history
+
+
+
+Figure (3): Compressibility factor from reference data (black) and evaluated by data-driven equation of state (red).
+
+
+### 4. Preparation of the Simulation
+
+To run data-driven fluid simulations in SU2, you need the SU2 configuration file, the mesh, and the file describing the multilayer perceptron. Running the script [3:prepare_simulation.py](https://github.com/su2code/Tutorials/tree/master/compressible_flow/NICFD_nozzle/PhysicsInformed/3:prepare_simulation.py) generates the computational mesh, writes the SU2 configuration file, and writes the ASII file containing the weights and biases of the network.
+
+The mesh is generated using gmesh in which the computational domain is generated accoring to the nozzle contour. The nozzle wall is modeled as a non-slip surface and prism layer refinement is applied to resolve the boundary layer.
+
+
+Figure (4): Computational mesh used for the nozzle flow simulation (top) and highlight of the throat area near the wall (bottom).
+
+The inflow condition is modeled as a non-reflective Giles boundary condition where the pressure and temperature of the critical point of the fluid are imposed as the stagnation pressure and stagnation temperature. The outflow is also modeled as a non-reflective Giles boundary condition, where a static pressure 10 times lower than the inflow pressure is imposed. To improve the stability of the solution process, the pressure imposed at the outflow boundary ramps down from the inflow stagnation pressure to the target value over the course of the first 200 flow iterations.
+
+The data-driven fluid model with the physics-informed entropy-based equation of state is enabled through the following options in the [SU2 configuration file](https://github.com/su2code/Tutorials/tree/master/compressible_flow/NICFD_nozzle/PhysicsInformed/config_NICFD_PINN.cfg):
+```
+FLUID_MODEL= DATADRIVEN_FLUID
+USE_PINN= YES
+INTERPOLATION_METHOD= MLP
+FILENAMES_INTERPOLATOR= MLP_siloxane_MM.mlp
+```
+where the ```USE_PINN= YES``` option enables the use of a physics-informed neural network for thermodynamic state calculations. The file ```MLP_siloxane_MM.mlp``` is the ASII file which describes the network architecture and the network weights and biases. At the start of the SU2 solution process, the network weigths and biases are imported into SU2 through the MLPCpp sub-module.
+
+
+### 5. Run SU2
+The simulation us run by the following command
+```
+mpirun -n SU2_CFD config_NICFD_PINN.cfg
+```
+where `````` is the number of cores. [Figure 5](#mesh_highlight) shows the residual trends of the flow solution of the course of 10k iterations.
+
+
+Figure (5): Convergence history of the SU2 simulation.
+
+
+### Results
+
+The Mach number and compressibility factor of the flow solution is visualized in the image below. The flow expands to supersonic speeds in the divergent section of the nozzle, where the compressibility factor approaches a value of 1.0, while the compressibility factor is significantly lower in the convergent section of the nozzle.
+
+Figure (6): Mach number of the flow solution (top) and compressibility factor of the fluid (bottom).
diff --git a/_tutorials/incompressible_flow/Inc_Urban_City/Inc_Urban_City.md b/_tutorials/incompressible_flow/Inc_Urban_City/Inc_Urban_City.md
new file mode 100644
index 00000000..78a43d65
--- /dev/null
+++ b/_tutorials/incompressible_flow/Inc_Urban_City/Inc_Urban_City.md
@@ -0,0 +1,181 @@
+---
+title: Wind velocity and pollutant dispersion in a city
+permalink: /tutorials/Inc_Urban_City/
+written_by: Nijso Beishuizen
+for_version: 8.4.0
+revised_by:
+revision_date:
+revised_version:
+solver: INC_NAVIER_STOKES
+requires: SU2_CFD
+complexity: intermediate
+---
+
+
+
+Figure (1): Flow through the streets of the city center of amsterdam.
+
+## Goals
+
+In this tutorial we will look at the modeling of wind speed and pollutant dispersion in a realistic depiction of a city - in this case the city center of Amsterdam. We will show the following aspects:
+- outline of creating a mesh of a city using GIS data
+- obtaining weather data of a specific date
+- simulate wind velocity to map pedestrian comfort.
+- simulate dispersion of a smoke cloud from a point source (burning car for instance) through streets to assess evacuation zone
+
+
+## Resources
+
+The resources for this tutorial can be found in the [incompressible_flow/Inc_Urban_City](https://github.com/su2code/Tutorials/tree/develop/incompressible_flow/Inc_Urban_City) directory in the [tutorial repository](https://github.com/su2code/Tutorials).
+
+
+### Background
+
+CFD simulations around buildings in a city have many applications. One application is to investigate pedestrian comfort due to strong winds that can occur around large buildings. Another application is the risk assessment in case of an accident where harmful polutants are released, for instance the smoke of a car fire. Both of these cases can be simulated with SU2 very easily. The challenge is in obtaining a mesh of the region of interest.
+
+Information about buildings, specifically their surface shape and often even their height and 3D shape, is available in many countries like the Netherlands. This data is stored in GIS-databases (Geographic Information System). Google maps and google earth are examples of well-known GIS databases. We will use 2D building information and create a 2D mesh from it.
+With a simple python script it is possible to retrieve information of buildings given a city name, or longitude-latitude coordinates.
+These building contours then need to be converted into a set of closed contours that can be given to a mesher like gmsh to create a 2D mesh for the SU2 CFD solver.
+
+
+### Pedestrian comfort: flow through the streets of Amsterdam
+
+What was the least comfortable street to walk on in the city center of Amsterdam, on new years day 2026 at noon?
+
+First, we need the average wind velocity at that time. A simple python script can get that information from open-meteo, given the latitude and longitude, using the python requests library:
+
+```python
+import requests
+city center of amsterdam
+[lat,lon]=[52.373080,4.892453]
+# Open-Meteo API endpoint (free, no API key needed)
+url = "https://api.open-meteo.com/v1/forecast"
+date="2026-01-01"
+#time=12:00
+params = {
+ 'latitude': lat,
+ 'longitude': lon,
+ 'start_date': date,
+ 'end_date': date,
+ 'daily': 'temperature_2m_mean,pressure_msl_mean,wind_speed_10m_max,wind_speed_10m_mean,wind_direction_10m_dominant,relative_humidity_2m_mean',
+ 'timezone': 'auto'
+ }
+response = requests.get(url, params=params)
+data = response.json()
+print(data)
+```
+
+We can also get the building information as polygons. For this information, we can use the overpass API (). We can grab all buildings within a certain radius from our given latitude and longitude. The core of the program is:
+```python
+overpass_url = "https://overpass-api.de/api/interpreter"
+ overpass_query = f"""
+ [out:json][timeout:60];
+ (
+ way["building"](around:{radius_meters},{latitude},{longitude});
+ relation["building"](around:{radius_meters},{latitude},{longitude});
+ way["building:part"](around:{radius_meters},{latitude},{longitude});
+ relation["building:part"](around:{radius_meters},{latitude},{longitude});
+ );
+ out geom;
+ """
+```
+This information can then be saved as polygon data to a file. The information that we retrieved is visualized in Figure 2:
+
+
+
+Figure 2: Visualization of the building information that was retrieved. The polygon data was saved as a pickle file for further processing.
+
+We found 1615 buildings in this region.
+With this polygon information, we can build a 2D CFD mesh, but the first step towards that goal is to merge all the connecting and overlapping buildings. For buildings that are not touching but are separated by a very small distance, we use the dilate-erode method. First, let buildings that are very close grow in size, then compute the new contours of the merged shapes, then erode the shapes again to go back to their original size. We also remove any contours with small surface areas. We are then left with only 125 buildings, a huge reduction!
+
+We can further smooth the shapes by using dilation-erosion, and also by using the Douglas-Peucker method to simplify polygons and reduce node and edge count. After this procedure we went from 10730 vertices to 2374 . We also remove all buildings with an are smaller than 25 square meters. We are now left with 115 buildings and 2209 edges.
+
+
+### Mesh Description
+
+The final step in creating this setup is to give all polygons to gmsh and create the mesh. We use a triangulated mesh with a quadrilateral inflation layer around the buildings. The final mesh is 786k cells.
+
+
+Figure 3: Gmsh mesh and zoom of the mesh around the royal palace showing the inflation layers.
+
+Note that the mesh is very coarse close to the wall and needs to be refined for more accurate results. For resolved boundary layers the dimensionless wall cell size needs to be $y^{+} < 1$.
+We have also made some simplifications in the geometry and physics. Note that cutting to a circular shape means that at the edges the results are inaccurate because we are missing the effect of buildings outside of the circular area. Also note that assuming a 2D planar scenario is not accurate. We do not take into account the different heights of the buildings and we also do not take into account that the wind blows over the entire city and has a downward component as well. So although this simulation will not accurately represent the wind velocity through the streets in a quantitative way, at least qualitatively they give an impression of the accelerating wind speeds through the streets of the city center.
+
+
+### Configuration File Options
+
+The setup itself is a standard setup. The main input is the velocity:
+```
+% wind speed on 01-01-2026 at 12:00 in Amsterdam (262 degrees 6.81 m/s)
+INC_VELOCITY_INIT = (6.74, 0.95, 0.0 )
+```
+
+And we need to define the boundaries. We only have a far-field and the walls of the building:
+```
+MARKER_FAR= farfield
+MARKER_HEATFLUX= wall_buildings
+```
+
+
+### Running SU2
+
+If possible, always use a parallel setup to reduce computational time (wall clock time). Run the SU2_CFD executable in parallel using MPI and 4 nodes by entering:
+
+ $ mpirun -n 4 SU2_CFD amsterdam.cfg
+
+
+
+
+### Results
+
+
+
+Figure (4): Zoom of the flow through the streets of the city center of amsterdam.
+
+The zoom of the wind velocity in Figure 4 shows the wind around the royal palace on the 'dam' with the national monument for war victims on the right. It is clear that walking through the 'paleisstraat' into the main square would have been very uncomfortable.
+
+
+### Fire!
+
+What if there was a fire on the main square? How would the smoke be transported by the wind through the streets of Amsterdam? This can be simulated very easily by adding a species source term using the python wrapper. We will use a simple circular constant source of smoke and add it to the species transport equation.
+```python
+# ################################################################## #
+# Source term for smoke/fire #
+# ################################################################## #
+def smoke(SU2Driver, iPoint, nDim):
+
+ allCoords = SU2Driver.Coordinates()
+ coord = allCoords.Get(iPoint)
+ x = coord[0]
+ y = coord[1]
+ R = np.sqrt(x*x + y*y)
+ # source size: R = 5 m, source term located at center (0,0)
+ if (R < 5.0):
+ # source is kg.m^-3.s^-1
+ Sc = 0.1
+ else:
+ Sc = 0.0
+ return Sc
+
+```
+
+This source term was added to a standard python wrapper script, the configuration file was left unchanged, except for the addition of the keyword
+```bash
+PYTHON_CUSTOM_SOURCE= YES
+```
+to activate the custom source term.
+
+
+
+Figure (5): Zoom of the 'Dam' square in front of the royal palace.
+
+Figure 5 clearly shows that the smoke blows into the 'Damrak' street.
+
+
diff --git a/_tutorials/index.md b/_tutorials/index.md
index 778e8d80..0a0c86f1 100644
--- a/_tutorials/index.md
+++ b/_tutorials/index.md
@@ -50,8 +50,12 @@ Simulation of unsteady, external, viscous flow around an airfoil.
Perform uncertainty quantification of errors arising due to assumptions inherent in turbulence models.
* [Non-ideal compressible flow in a supersonic nozzle](/tutorials/NICFD_nozzle/)
Simulation of compressible flow in a nozzle using non-ideal thermodynamic models.
+* [Data-driven equation of state for non-ideal compressible fluids](/tutorials/NICFD_nozzle_datadriven/)
+Demonstration of data-driven equation of state using a physics-informed neural network.
* [Turbomachinery: Aachen Turbine stage with mixing plane](/tutorials/Aachen_Turbine/)
Simulation of compressible flow of the Aachen turbine demonstrating turbomachinery application.
+* [Actuator Disk with Variable Load](/tutorials/ActuatorDisk_VariableLoad/)
+Simulation of an actuator disk with variable load.
#### Incompressible Flow
@@ -77,6 +81,8 @@ Simulation of internal, turbulent, 3D incompressible flow through a Kenics stati
Simulation of unsteady laminar vortex shedding behind a circular cylinder.
* [Turbulent Bend](/tutorials/Inc_Turbulent_Bend/)
Simulation of turbulent flow in a 90 degree pipe bend using wall functions.
+* [Urban City](/tutorials/Inc_Urban_City/)
+Simulation of wind velocity and smoke through the city center of Amsterdam.
#### Structural Mechanics
@@ -94,7 +100,7 @@ Simulation of a non-linear problem with multiple material definitions
* [Static Fluid-Structure Interaction](/tutorials/Static_FSI/)
Non-linear structural mechanics coupled with incompressible Navier-Stokes flow
* [Dynamic Fluid-Structure Interaction with the Python wrapper](/tutorials/Dynamic_FSI_Python/)
-Linear Nastran-like model coupled with compressible unsteady RANS equations using the Python wrapper
+Linear Nastran-like model coupled with compressible unsteady RANS equations using the Python wrapper.
* [Static Conjugate Heat Transfer](/tutorials/Static_CHT/)
Simulation of multiple heated cylinders in incompressible fluid flow.
* [Unsteady Conjugate Heat Transfer](/tutorials/Inc_Heated_Cylinders_Unsteady/)
@@ -103,6 +109,8 @@ Simulation of an unsteady coupled CHT problem incorporating multiple physical zo
Simulation of CHT between solid domains with contact resistance.
* [Pre-mixed Hydrogen Combustion](/tutorials/Inc_Combustion/)
Simulation of a laminar, pre-mixed hydrogen flame on a cooled burner plate.
+* [Python wrapper for User Defined Functionality](/tutorials/TFC_python/)
+Use the Python wrapper to setup user defined source terms, initial conditions and boundary conditions for combustion.
#### Shape Design Features
diff --git a/_tutorials/multiphysics/TFC_python/TFC_python.md b/_tutorials/multiphysics/TFC_python/TFC_python.md
new file mode 100644
index 00000000..c68c858b
--- /dev/null
+++ b/_tutorials/multiphysics/TFC_python/TFC_python.md
@@ -0,0 +1,319 @@
+---
+title: User Defined combustion model with Python
+permalink: /tutorials/TFC_python/
+written_by: Nijso Beishuizen
+for_version: 8.3.0
+revised_by:
+revision_date:
+revised_version:
+solver: INC_NAVIER_STOKES
+requires: SU2_CFD, python
+complexity: advanced
+---
+
+
+Figure (1): high-pressure turbulent premixed flame of the Paul-Scherrer Institute (PSI), Switzerland
+
+## Goals
+
+In this tutorial we will simulate a high pressure turbulent premixed flame using the Turbulent Flamespeed Closure (TFC) model. This is a simple turbulent combustion model that can be implemented with a User Defined Source (UDS) in python.
+In this tutorial we will touch upon the following aspects:
+- Compile and run SU2 from within python using the python wrapper
+- Create a User Defined Source
+- Create User Defined boundary conditions
+- Create User Defined initial conditions
+- Overwrite the enthalpy (temperature) field with a user defined field
+
+
+## Resources
+
+The resources for this tutorial can be found in the [TFC_python](https://github.com/su2code/Tutorials/tree/develop/multiphysics/TFC_python) directory in the [tutorial repository](https://github.com/su2code/Tutorials). You will need the configuration file ([psi.cfg](https://github.com/su2code/Tutorials/tree/develop/multiphysics/TFC_python/adiabatic/psi.cfg)) and the mesh file ([psi.su2](https://github.com/su2code/Tutorials/tree/develop/multiphysics/TFC_python/adiabatic/psi.su2)). Additionally, the Gmsh geometry is also provided so you can recreate the mesh yourself: [psi.geo](https://github.com/su2code/Tutorials/tree/develop/multiphysics/TFC_python/psi.geo). Files for the non-adiabatic case are in the folder [enthalpy](https://github.com/su2code/Tutorials/tree/develop/multiphysics/TFC_python/enthalpy) and files for the case with source term quenching and custom wall boundary conditions are in the folder [quench](https://github.com/su2code/Tutorials/tree/develop/multiphysics/TFC_python/quench)
+
+
+### Background
+
+Turbulent combustion can be very expensive to simulate, especially when high accuracy is required and detailed information about specific species (NOx emissions for instance) is required. The Turbulent Flamespeed Closure (TFC) model, first pioneered by Zimont, is a very simple model for turbulent premixed combustion that aims to give accurate predictions for temperature in turbulent premixed combustion. When the goal is to mainly resolve the flame structure and gas temperature to compute for instance burner temperatures in a conjugate heat transfer setup, then the TFC model has sufficient accuracy. In the TFC model, a transport equation for the progress variable is solved:
+
+$$ \frac{\partial c}{dt} + \nabla \cdot (\rho u c) = \nabla\cdot (\frac{\mu_t}{Sc_t}\nabla c) + \rho S_c$$
+
+and the combustion source term is given by
+$$ S_c = \rho_u U_t \nabla c$$,
+with $$\rho_u$$ the unburnt density of the gas an $$U_t$$ the turbulent flamespeed. The Zimont model uses the gradient of the progress variable to construct the source term, but other models exist as well. These models can be implemented easily in SU2 by writing a user defined source term in the python wrapper.
+
+### Problem Setup
+
+First, SU2 needs to be compiled with python support. Add the option *-Denable-pywrapper=true* to the meson setup. In this case, we compile including mpi support:
+
+```bash
+$ ./meson.py setup build --optimization=2 -Ddebug=false -Denable-pywrapper=true -Dwith-mpi=enabled -Dcustom-mpi=true --prefix=/home/user/Codes/su2code/su2/
+```
+
+Note that the python wrapper (or your python setup) might need additional python packages that you need to install. Especially the python package mpi4py is important if you would like to work with mpi. The mpi4py package should match your installed mpi library, usually openmpi or mpich. The easiest way to setup a working environment is to create a conda environment and install all necessary packages in this environment.
+
+
+### Mesh Description
+
+The geometry of this testcase is provided as a gmsh file and matches the of the experimental setup of Griebel et al (2007), [doi](https://doi.org/10.1016/j.proci.2006.07.042).
+
+The mesh consists of a a coarse structured mesh with 16.3k cells and 16.6k points. The mesh was created using Gmsh and the configuration file to create the mesh can be found here: [psi.geo](https://github.com/su2code/Tutorials/tree/develop/multiphysics/TFC_python/psi.geo). The only thing you need to do to create a mesh from the geometry is start Gmsh, and then load the .geo file. You will then see the geometry in the Gmsh visualization window. If you click on *Mesh->2D* the 2D mesh will be generated. You can then export the mesh as a .su2 file by choosing *File->Export*. The mesh will automatically be saved in su2 format when the filename has been given the extension .su2. In general, you should not choose *save all elements* because this will also save additional points that were used to construct the geometry but are not part of the final mesh, like for example the center of a circle.
+
+
+### Configuration File Options
+
+The setup for this testcase consists of 2 files:
+- the run.py file is the python file that runs the case. in this file, we simply import the su2 capabilities using *import pysu2*
+- the psi.cfg file for the basic setup. Some settings will be overwritten by python.
+
+In the configuration file, we have set up a case for turbulent incompressible flow using the k-omega SST model. We have also deactivated the energy equation (sensible enthalpy) and species transport.
+```
+SOLVER= INC_RANS
+KIND_TURB_MODEL= SST
+INC_ENERGY_EQUATION= NO
+KIND_SCALAR_MODEL= SPECIES_TRANSPORT
+PYTHON_CUSTOM_SOURCE= YES
+```
+
+Note that the although the enthalpy equation is not solved, the enthalpy field is present. It is just a constant field and not altered by any computation. Every iteration, the temperature is computed from the enthalpy field, independent of the energy equation being active or not.
+To activate the custom source term, the keyword **PYTHON_CUSTOM_SOURCE= YES** is used.
+
+in the python file, we have defined several functions to set up the testcase. The first function creates a simple initial condition for the progress variable c:
+```python
+
+################################################################## #
+# create a function for the initial progress variable c #
+# ################################################################ #
+def initC(coord):
+ x = coord[0]
+ #y = coord[1]
+ #z = coord[2]
+ # location where the flame should be
+ flame_x = 0.012
+ if (x < flame_x):
+ C = 0.0
+ else:
+ C = 1.0
+
+ return C
+
+# ###################################################################
+# loop over all vertices and set the species progress variable c #
+# ################################################################# #
+def SetInitialSpecies(SU2Driver):
+ allCoords = SU2Driver.Coordinates()
+ iSPECIESSOLVER = SU2Driver.GetSolverIndices()['SPECIES']
+ for iPoint in range(SU2Driver.GetNumberNodes()):
+ coord = allCoords.Get(iPoint)
+ C = initC(coord)
+ # now update the initial condition for the species
+ SU2Driver.Solution(iSPECIESSOLVER).Set(iPoint,0,C)
+
+```
+
+
+Figure(2): initial temperature field created using the python wrapper
+
+Note that when setting the species solution, we use the index=0, because we have only 1 species transport equation. When setting a flow solution, the index to the required field should be used. The indices can be retrieved using:
+```python
+print("indices of solver variables: ", getsolvar(driver))
+```
+with the result:
+```
+indices of solver variables: {'PRESSURE': 0, 'VELOCITY_X': 1, 'VELOCITY_Y': 2, 'TEMPERATURE': 3}
+```
+
+Note that index 3 is actually the sensible enthalpy, and from this, we compute the temperature. In our setup we want to overwrite the temperature. Because internally, temperature is always computed from the energy equation (be that temperature, or enthalpy), it is best to overwrite the enthalpy and let SU2 compute the temperature internally.
+
+in the main function, we simply check in the config file if **RESTART=YES** to decide if we want to overwrite the initial solution for the progress variable with our user defined initial solution:
+
+```python
+ # ### Check if we do a restart or not. ###
+ with open('psi.cfg') as f:
+ if 'RESTART_SOL= YES' in f.read():
+ if rank == 0:
+ print("restarting from file")
+ else:
+ # We can set an initial condition by calling this function:
+ if rank == 0:
+ print("Using user defined initial condition.")
+ SetInitialSpecies(driver)
+
+```
+
+The functions *update_temperature* and *zimont* implement the algebraic temperature relationship and the source term for the progress variable. Then in the main function, we simply loop over all points in the domain, compute the source term and add it to the progress variable equation:
+```python
+ Source = driver.UserDefinedSource(iSPECIESSOLVER)
+
+ # set the source term, per point
+ for i_node in range(driver.GetNumberNodes() - driver.GetNumberHaloNodes()):
+ # add source term:
+ # default TFC of Zimont: rho*Sc = rho_u * U_t * grad(c)
+ S = zimont(driver,i_node)
+ Source.Set(i_node,0,S)
+
+```
+
+Note that we do not add the source term to the halo nodes, which are used in parallel computing. In parallel computing we divide the mesh in parts and each cpu gets a part, which is called a rank. The points on the interface between ranks need information from the other side of the interface. But this information is computed on another rank and not directly available. This is why each rank has halo points, which are copies of the points on the other side of the rank. During a synchronisation step, all information in the halo nodes is updated so the local computations on each of the ranks can proceed with the correct information present at the rank boundaries. They should not be taken into account in any computation, because they are in fact copies of points that *are* taken into account during the computation.
+For the temperature update, we update all points, including the halo points. Because we overwrite the entire enthalpy (and temperature) field, and no other computations are performed on the enthalpy field, the halo points would not get updated and not updating them will lead to incorrect computations of gradients at the rank interface.
+
+```python
+ # for the update of temperature, we need to update also the halo nodes
+ for i_node in range(driver.GetNumberNodes()):
+ # set the temperature to T = c*Tf + (1-c)*Tu
+ update_temperature(driver, i_node)
+```
+
+The actual temperature update is done in update_temperature:
+```python
+# ################################################################## #
+# Temperature is an algebraic function of c
+# ################################################################## #
+def update_temperature(SU2Driver, iPoint):
+ # first, get the progress variable
+ iSPECIESSOLVER = SU2Driver.GetSolverIndices()['SPECIES']
+ # Note: returns a list
+ C = SU2Driver.Solution(iSPECIESSOLVER)(iPoint,0)
+ T = Tu*(1-C) + Tf*C
+
+ iFLOWSOLVER = SU2Driver.GetSolverIndices()['INC.FLOW']
+ # 0 1 2 3
+ # pressure, velocity-x, velocity-y, enthalpy
+ iENTH = 3
+ SU2Driver.Solution(iFLOWSOLVER).Set(iPoint,iENTH, cp_u*(T-Tref))
+```
+We update the sensible enthalpy and let SU2 compute the temperature internally.
+
+The computation of the source term according to Zimon is given by:
+```python
+# ################################################################## #
+# Source term according to Zimont
+# ################################################################## #
+def zimont(SU2Driver, iPoint, nDim):
+
+ iSSTSOLVER = SU2Driver.GetSolverIndices()['SST']
+ tke, dissipation = SU2Driver.Solution(iSSTSOLVER)(iPoint)
+
+ iSPECIESSOLVER = SU2Driver.GetSolverIndices()['SPECIES']
+ # get the gradient of species_0
+ gradc = SU2Driver.Gradient(iSPECIESSOLVER)(iPoint,0)
+ primindex = SU2Driver.GetPrimitiveIndices()
+ iDENSITY = primindex.get("DENSITY")
+ iMU = primindex.get("LAMINAR_VISCOSITY")
+
+ rho = SU2Driver.Primitives()(iPoint,iDENSITY)
+ mu = SU2Driver.Primitives()(iPoint,iMU)
+ nu=mu/rho
+ # Turbulent Flamespeed Closure with Dinkelacker correction
+ up = np.sqrt((2.0/3.0) * tke )
+ lt = (0.09**0.75) * (tke**1.5) / dissipation
+ Re = up*lt/nu
+
+ Ut = Slu * (1.0 + (0.46/Le) * np.power(Re,0.25) * np.power(up/Slu,0.3) * np.power(Pu,0.2) )
+
+ norm_gradc = 0.0
+ for idim in range(nDim):
+ norm_gradc += gradc[idim]*gradc[idim]
+ norm_gradc = np.sqrt(norm_gradc)
+ Sc = rho_u * Ut * norm_gradc
+
+ return Sc
+```
+Note that we are using here a different formulation for the turbulent velocity, as described in papers by Dinkelacker and Muppala. The turbulence velocity is now such that if the turbulent kinetic energy is zero, the laminar flame velocity is retrieved. We also include a correction for the Lewis number to take into account the effect of preferential diffusion on the flame speed. This allows to work with hydrogen as a fuel as well. Note that for accurate simulations of hydrogen flames, especially in the laminar regime, preferential diffusion needs to be taken into account in a more elaborate way by introducing the mixture fraction and by considering the flame as a partially premixed flame (even a perfectly premixed hydrogen flame will become partially premixed in the flame zone).
+
+
+### Running SU2
+
+If possible, always use a parallel setup to reduce computational time (wall clock time). Run the SU2_CFD executable in parallel using MPI and 4 nodes by entering:
+
+```bash
+$ mpirun -n 4 python run.py
+```
+
+
+### Results
+
+
+Figure (3): visualization of the TFC source term for the turbulent combustion model, together with the streamlines.
+
+
+The solution of the flame region can be visualised using the User Defined Source UDS_0. A large recirculation region has also formed between the flame and the wall of the combustion chamber, causing heat transfer between the flame and the wall.
+
+Note that we clip the species between [0,1] to prevent (temporary) unphysical values and prevent divergence. Sometimes this clipping is necessary on coarse grids. The clipping will be visible in the residuals of the progress variable, since clipping prevents local convergence.
+
+### Heat losses: enthalpy source term
+
+When heat losses at the wall need to be taken into account, this can be done by solving the energy (enthalpy) equation. The wall boundaries can be given a wall temperature in the usual way. To take into account combustion, we add a source term similar to the Zimont source term for the progress variable. This replaces the algebraic computation of the enthalpy and temperature.
+
+```python
+ iENTH = 3
+ Source_h.Set(i_node, iENTH, 0.0284*50.5*1e6*S)
+```
+
+The source term S needs to be multiplied by the lower heating value of the mixture 50.5 MJ/kg and the mass fraction of methane in the mixture (with equivalence ratio 0.5, this amounts to Y=0.0284).
+In the config file, the wall temperature can now be set to an appropriate temperature. Since the combustion chamber is known to become very hot, we set the walls to 1200K. The wall temperature is imposed weakly, meaning that we compute the wall flux from Fourier's law and impose that flux:
+
+$$q_w = k_f \left(
+ \frac{\partial T}{\partial n}
+\right)_{\textrm{wall}}$$
+
+The near wall mesh needs to be fine enough to accurately represent the flux.
+
+
+
+Figure (4): visualization of the temperature with a line plot showing the wall temperature.
+
+Figure 4 shows the temperature in the domain, clearly showing the thermal boundary layer at the wall. In the upper left corner the temperature redistribution due to the recirculation zone is also clearly present.
+More accurate modeling can be achieved by taking into account the effect of heat loss on the laminar flame speed. In cantera, the laminar flame speed can be computed and a relationship for the laminar flame speed $$S_L(\Delta h_t)$$ as a function of the enthalpy deficiency can be used instead of a constant $$S_L$$. The local flame quenching due to heat losses at the wall can then be taken into account. The total enthalpy can be computed from the sensible enthalpy and the progress variable, noticing that when the progress variable is 1, then all chemical enthalpy has been converted into sensible enthalpy.
+
+The heating value of methane is 50.5 MJ/kg, and with a mass fraction of $$Y=0.0284$$ we have a chemical enthalpy of
+
+$$h_{\textrm{chem}}^{\textrm{max}} = 1.4342 MJ/kg$$.
+
+With our chemical enthalpy converted to sensible enthalpy, our flame temperature ends up to be around 1735K.
+
+With the measured flame temperature of T=1777K (used in our algebraic relationship for temperature), we compute a sensible enthalpy of 2MJ/kg, very close to the total enthalpy computed using the sum of the initial sensible enthalpy and the chemical enthalpy.
+
+Our progress variable now tells us that the chemical enthalpy that we have left is
+
+$$h_{\textrm{chem}} = c \cdot h_{chem}^{\textrm{max}}$$.
+
+Since total enthalpy is conserverved during combustion, the temperature difference between the actual temperature and the temperature computed from the sensible enthalpy gives us the heat loss. With this enthalpy deficiency we can compute the local laminar flame speed and the corrected source term.
+
+
+Figure (4): visualization of the source term showing the results using constant laminar flamespeed (top) and the variable laminar flamespeed (bottom).
+
+
+
+Figure (5): visualization of the temperature showing the result using constant laminar flamespeed (top) and the variable laminar flamespeed (bottom).
+
+Figures 4 and 5 show a comparison between the simulation results using a constant laminar flamespeed and a variable laminar flamespeed that takes into account local heat losses. In Figure 4, the source term is smaller close to the wall where the flame attaches. The temperature field in Figure 5 shows that the flame zone is more diffusive.
+
+Figure (6): Line plot of the progress variable on the centerline showing the result using constant laminar flamespeed and variable laminar flamespeed.
+
+Figure 6 confirms that the flame is more diffuse with variable S_L.
+
+Note that with python it is also very easy to impose temperature profiles. The code is simply:
+```python
+
+def ApplyWallTemperature(SU2Driver, marker_ids):
+ """Applies an isothermal wall boundary condition on a specific wall"""
+ for marker_id in marker_ids:
+ if marker_id < 0:
+ continue
+ allCoords = SU2Driver.Coordinates()
+ # the top horizontal wall is 1200K, but the vertical side wall goes from 1200K down to 700K
+ # to match the inlet pipe temperature (which has zero heat flux BC)
+ for i_vertex in range(driver.GetNumberMarkerNodes(marker_id)):
+ i_point = SU2Driver.GetMarkerNode(marker_id, i_vertex)
+ coord = allCoords.Get(i_point)
+ Tw = 673 + 500.0 * (coord[1]-0.0125)/0.025
+ driver.SetMarkerCustomTemperature(marker_id, i_vertex, hf)
+```
+And in the config file, we simply add
+```bash
+MARKER_PYTHON_CUSTOM= (wall_top, wall_side )
+```
+
+The wall temperature is now a function of the vertical coordinate y.
+
+
+
diff --git a/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/Mach_Z.png b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/Mach_Z.png
new file mode 100644
index 00000000..3a81df59
Binary files /dev/null and b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/Mach_Z.png differ
diff --git a/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/PT_diagram.png b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/PT_diagram.png
new file mode 100644
index 00000000..ff8a7b3a
Binary files /dev/null and b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/PT_diagram.png differ
diff --git a/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/TrainingHistory.png b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/TrainingHistory.png
new file mode 100644
index 00000000..e56a70d5
Binary files /dev/null and b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/TrainingHistory.png differ
diff --git a/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/Z_contours_comp.png b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/Z_contours_comp.png
new file mode 100644
index 00000000..dfb41c13
Binary files /dev/null and b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/Z_contours_comp.png differ
diff --git a/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/convergence_history.png b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/convergence_history.png
new file mode 100644
index 00000000..db3b05fe
Binary files /dev/null and b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/convergence_history.png differ
diff --git a/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/mesh_highlight.png b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/mesh_highlight.png
new file mode 100644
index 00000000..47e451e6
Binary files /dev/null and b/tutorials_files/compressible_flow/NICFD_nozzle_datadriven/images/mesh_highlight.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/amsterdam_buildings.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/amsterdam_buildings.png
new file mode 100644
index 00000000..db358a84
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/amsterdam_buildings.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/amsterdam_complete_map.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/amsterdam_complete_map.png
new file mode 100644
index 00000000..e6a05dc5
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/amsterdam_complete_map.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/fire_zoom.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/fire_zoom.png
new file mode 100644
index 00000000..445672c6
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/fire_zoom.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/gmsh_mesh.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/gmsh_mesh.png
new file mode 100644
index 00000000..e140f016
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/gmsh_mesh.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/gmsh_mesh_zoom.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/gmsh_mesh_zoom.png
new file mode 100644
index 00000000..8c99827b
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/gmsh_mesh_zoom.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/residuals_fine.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/residuals_fine.png
new file mode 100644
index 00000000..255af663
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/residuals_fine.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/wind_velocity.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/wind_velocity.png
new file mode 100644
index 00000000..c5170858
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/wind_velocity.png differ
diff --git a/tutorials_files/incompressible_flow/Inc_Urban_City/images/wind_velocity_zoom.png b/tutorials_files/incompressible_flow/Inc_Urban_City/images/wind_velocity_zoom.png
new file mode 100644
index 00000000..b8b99c1c
Binary files /dev/null and b/tutorials_files/incompressible_flow/Inc_Urban_City/images/wind_velocity_zoom.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_c_correction.png b/tutorials_files/multiphysics/TFC_python/images/TFC_c_correction.png
new file mode 100644
index 00000000..d028b921
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_c_correction.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_c_init.png b/tutorials_files/multiphysics/TFC_python/images/TFC_c_init.png
new file mode 100644
index 00000000..352caac5
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_c_init.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_source.png b/tutorials_files/multiphysics/TFC_python/images/TFC_source.png
new file mode 100644
index 00000000..bfd9537a
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_source.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_source_correction.png b/tutorials_files/multiphysics/TFC_python/images/TFC_source_correction.png
new file mode 100644
index 00000000..8e0de368
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_source_correction.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_temp.png b/tutorials_files/multiphysics/TFC_python/images/TFC_temp.png
new file mode 100644
index 00000000..57b094e4
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_temp.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_temp_correction.png b/tutorials_files/multiphysics/TFC_python/images/TFC_temp_correction.png
new file mode 100644
index 00000000..0cc45b5b
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_temp_correction.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_temp_heatloss.png b/tutorials_files/multiphysics/TFC_python/images/TFC_temp_heatloss.png
new file mode 100644
index 00000000..9ec0c1bf
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_temp_heatloss.png differ
diff --git a/tutorials_files/multiphysics/TFC_python/images/TFC_temp_init.png b/tutorials_files/multiphysics/TFC_python/images/TFC_temp_init.png
new file mode 100644
index 00000000..61f53a64
Binary files /dev/null and b/tutorials_files/multiphysics/TFC_python/images/TFC_temp_init.png differ