Metadata-Version: 2.4
Name: incompy2d
Version: 0.1.5
Summary: A 2D incompressible Navier-Stokes CFD solver in Python
Project-URL: Repository, https://gitlab.git.nrw/phuong-nam.nguyen/sce_incompy2d
Author-email: Phuong-Nam Nguyen <phuong-nam.nguyen@rwth-aachen.de>
License: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: numba
Requires-Dist: numpy
Provides-Extra: dev
Requires-Dist: black; extra == 'dev'
Requires-Dist: cookiecutter; extra == 'dev'
Requires-Dist: geopandas; extra == 'dev'
Requires-Dist: ipympl; extra == 'dev'
Requires-Dist: jupyterlab; extra == 'dev'
Requires-Dist: matplotlib; extra == 'dev'
Requires-Dist: meshio; extra == 'dev'
Requires-Dist: osmnx; extra == 'dev'
Requires-Dist: overturemaps; extra == 'dev'
Requires-Dist: pandas; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: scikit-learn; extra == 'dev'
Requires-Dist: scipy; extra == 'dev'
Requires-Dist: shapely; extra == 'dev'
Requires-Dist: sphinx; extra == 'dev'
Provides-Extra: docs
Requires-Dist: sphinx; extra == 'docs'
Provides-Extra: geo
Requires-Dist: geopandas; extra == 'geo'
Requires-Dist: meshio; extra == 'geo'
Requires-Dist: osmnx; extra == 'geo'
Requires-Dist: overturemaps; extra == 'geo'
Requires-Dist: shapely; extra == 'geo'
Provides-Extra: test
Requires-Dist: pytest; extra == 'test'
Requires-Dist: pytest-cov; extra == 'test'
Provides-Extra: viz
Requires-Dist: matplotlib; extra == 'viz'
Description-Content-Type: text/markdown

# incompy2d

A 2D incompressible Navier-Stokes CFD solver in Python.

## Installation

```bash
pip install incompy2d
```

## Usage

`incompy2d` allows you to simulate global flowfield behavior around buildings or other structures.

Below is a general guideline on how to use the package. For more in-depth instructions, check out `tutorials\tutorial.ipynb`.

### 1. Fetching Building Footprints

First, define a geographic bounding box for your area of interest. You can use the `fetch_overture` module to download building footprints from the Overture Maps dataset and save them as a GeoJSON file.

1. Go to the website: https://boundingbox.klokantech.com/
2. Resize and move the bounding box to your region of interest.
3. At the bottom left, select `CSV` file format
4. Copy the `CSV` coordinates output
5. Paste to bbox:
        
        bbox = fetch_overture.overture_bbox_blds(
                (PASTE HERE), output_dir=None, output_filename="overture_bld.geojson")
6. Building map is then automatically fetched

Example:

```python
from incompy2d import fetch_overture

# Bounding box: (min_lon, min_lat, max_lon, max_lat)
bbox = fetch_overture.overture_bbox_blds(
    (6.088331247, 50.7762610712, 6.0918783729, 50.7780427173), 
    output_dir="geom", 
    output_filename="overture_bld.geojson"
)
```

### 2. Running the Simulation

Next, run the 2D incompressible Navier-Stokes solver using the downloaded GeoJSON geometry. The solver will automatically generate a structured mesh, rasterize the building footprints as solid obstacles, and run a projection-method CFD calculation. 

Make sure to select an appropriate timestep (`dt`) to satisfy the Courant-Friedrichs-Lewy (CFL) condition. If `dt` is omitted or too large, the solver will warn you and recommend a stable range based on the estimated maximum velocity around buildings.

```python
from incompy2d.run import run_simulation

result = run_simulation(
    geojson_path="geom/overture_bld.geojson",
    dx=1.0,           # Grid spacing (m)
    wind_u=5.0,       # Wind velocity X (m/s)
    wind_v=0.0,       # Wind velocity Y (m/s)
    nit=50,           # Pressure Poisson iter
    nt=7000,          # Number of timesteps
    dt=0.01,          # Timestep size (s)
    output_dir="outputs",
    sim_name="bushof" # Prefix for all output files
)
```

**CLI Usage:**
You can also run the simulation directly from your terminal:
```bash
python -m incompy2d.run --geojson geom/overture_bld.geojson --dx 1.0 --wind-u 5.0 --nt 7000 --dt 0.01 --output-dir outputs --sim-name bushof
```

### 3. Postprocessing

Once the simulation completes, you can visualize the results. The `post` module will generate contour plots of the velocity magnitude, streamline plots of the wind flow, and a convergence history of the solver (averaged residual).

```python
from incompy2d import post

# This will load bushof_sim_data.npz and generate plots in the output directory
post.main(output_dir="outputs", sim_name="bushof")
```

This generates `bushof_flow_result.png`, `bushof_streamlines.png`, and `bushof_convergence.png` in your `outputs` directory.

### 4. Example for global flowfield around Aachen Bushof area

![](tutorials/outputs/bushof_geometry_mask.png){: width="70%"}
![](tutorials/outputs/bushof_streamlines.png){: width="70%"}
![](tutorials/outputs/bushof_flow_result.png){: width="70%"}
![](tutorials/outputs/bushof_convergence.png){: width="70%"}

### 5. Theoretical Background
Check out `docs\incompy2d\incomp_2d_theory.md`

### 6. Validation 
We test the solver capability and reproducibility for 2D incompressible solver (`incompy2d`) by simulating flow past a circular cylinder at Reynolds numbers $Re \in \{20, 40, 100, 200\}$.

#### 6.1. Solver Convergence
![Convergence History](validation/validation_outputs/fig_residuals.png){: width="70%"}

The Poisson pressure solver demonstrates stable and consistent convergence behavior across all Reynolds numbers. The average absolute divergence drops smoothly, with the low-Re cases settling around $3 \times 10^{-6}$. The mass conservation is strictly enforced by the `wind_pressure_bc` Dirichlet condition at the outlet.

#### 6.2. Drag Coefficient ($C_D$)
![CD vs Re](validation/validation_outputs/fig_cd_vs_re.png){: width="70%"}

| $Re$ | Simulated $C_D$ | Reference $C_D$ |
|------|-----------------|-----------------|
| 20   | 3.494           | ~2.05           |
| 40   | 2.853           | ~1.54           |
| 100  | 2.332           | ~1.33           |
| 200  | 2.035           | ~1.34           |

The calculated surface drag coefficient correctly captures the physical trend of decreasing $C_D$ as the Reynolds number increases. However, the absolute magnitude of the drag is heavily over-predicted. This discrepancy is characteristic of first-order upwind numerical schemes, which introduce significant artificial numerical diffusion i.e. viscosity into the flow field, effectively artificially increasing the drag on the body.

#### 6.3. Wake Dynamics & Vortex Shedding

![Re=100 Vorticity](validation/validation_outputs/fig_vorticity_re100.png){: width="70%"}

![Re=200 Vorticity](validation/validation_outputs/fig_vorticity_re200.png){: width="70%"}

While an initial asymmetric perturbation was applied to trigger instability, the flow rapidly dampens back into an elongated, steady symmetric wake. 
The measured Strouhal number ($St = 0.050$) at $Re=100$ and $Re=200$ is extremely weak compared to the expected physical shedding frequency of $St \approx 0.16 - 0.20$.

**Conclusion:** 
The solver correctly models the steady-state physics and stability requirements of the Navier-Stokes equations. However, the prominent numerical diffusion generated by the 1st-order upwinding heavily suppresses the natural physical instabilities required to sustain a von Kármán vortex street. To achieve quantitative accuracy for unsteady wake dynamics, the solver's advection scheme must be upgraded to a higher-order method (e.g., QUICK or 2nd-order upwind).