Initial commit
This commit is contained in:
398
skills/fluidsim/references/advanced_features.md
Normal file
398
skills/fluidsim/references/advanced_features.md
Normal file
@@ -0,0 +1,398 @@
|
||||
# Advanced Features
|
||||
|
||||
## Custom Forcing
|
||||
|
||||
### Forcing Types
|
||||
|
||||
FluidSim supports several forcing mechanisms to maintain turbulence or drive specific dynamics.
|
||||
|
||||
#### Time-Correlated Random Forcing
|
||||
|
||||
Most common for sustained turbulence:
|
||||
|
||||
```python
|
||||
params.forcing.enable = True
|
||||
params.forcing.type = "tcrandom"
|
||||
params.forcing.nkmin_forcing = 2 # minimum forced wavenumber
|
||||
params.forcing.nkmax_forcing = 5 # maximum forced wavenumber
|
||||
params.forcing.forcing_rate = 1.0 # energy injection rate
|
||||
params.forcing.tcrandom_time_correlation = 1.0 # correlation time
|
||||
```
|
||||
|
||||
#### Proportional Forcing
|
||||
|
||||
Maintains a specific energy distribution:
|
||||
|
||||
```python
|
||||
params.forcing.type = "proportional"
|
||||
params.forcing.forcing_rate = 1.0
|
||||
```
|
||||
|
||||
#### Custom Forcing in Script
|
||||
|
||||
Define forcing directly in the launch script:
|
||||
|
||||
```python
|
||||
params.forcing.enable = True
|
||||
params.forcing.type = "in_script"
|
||||
|
||||
sim = Simul(params)
|
||||
|
||||
# Define custom forcing function
|
||||
def compute_forcing_fft(sim):
|
||||
"""Compute forcing in Fourier space"""
|
||||
forcing_vx_fft = sim.oper.create_arrayK(value=0.)
|
||||
forcing_vy_fft = sim.oper.create_arrayK(value=0.)
|
||||
|
||||
# Add custom forcing logic
|
||||
# Example: force specific modes
|
||||
forcing_vx_fft[10, 10] = 1.0 + 0.5j
|
||||
|
||||
return forcing_vx_fft, forcing_vy_fft
|
||||
|
||||
# Override forcing method
|
||||
sim.forcing.forcing_maker.compute_forcing_fft = lambda: compute_forcing_fft(sim)
|
||||
|
||||
# Run simulation
|
||||
sim.time_stepping.start()
|
||||
```
|
||||
|
||||
## Custom Initial Conditions
|
||||
|
||||
### In-Script Initialization
|
||||
|
||||
Full control over initial fields:
|
||||
|
||||
```python
|
||||
from math import pi
|
||||
import numpy as np
|
||||
|
||||
params = Simul.create_default_params()
|
||||
params.oper.nx = params.oper.ny = 256
|
||||
params.oper.Lx = params.oper.Ly = 2 * pi
|
||||
|
||||
params.init_fields.type = "in_script"
|
||||
|
||||
sim = Simul(params)
|
||||
|
||||
# Get coordinate arrays
|
||||
X, Y = sim.oper.get_XY_loc()
|
||||
|
||||
# Define velocity fields
|
||||
vx = sim.state.state_phys.get_var("vx")
|
||||
vy = sim.state.state_phys.get_var("vy")
|
||||
|
||||
# Taylor-Green vortex
|
||||
vx[:] = np.sin(X) * np.cos(Y)
|
||||
vy[:] = -np.cos(X) * np.sin(Y)
|
||||
|
||||
# Initialize state in Fourier space
|
||||
sim.state.statephys_from_statespect()
|
||||
|
||||
# Run simulation
|
||||
sim.time_stepping.start()
|
||||
```
|
||||
|
||||
### Layer Initialization (Stratified Flows)
|
||||
|
||||
Set up density layers:
|
||||
|
||||
```python
|
||||
from fluidsim.solvers.ns2d.strat.solver import Simul
|
||||
|
||||
params = Simul.create_default_params()
|
||||
params.N = 1.0 # stratification
|
||||
params.init_fields.type = "in_script"
|
||||
|
||||
sim = Simul(params)
|
||||
|
||||
# Define dense layer
|
||||
X, Y = sim.oper.get_XY_loc()
|
||||
b = sim.state.state_phys.get_var("b") # buoyancy field
|
||||
|
||||
# Gaussian density anomaly
|
||||
x0, y0 = pi, pi
|
||||
sigma = 0.5
|
||||
b[:] = np.exp(-((X - x0)**2 + (Y - y0)**2) / (2 * sigma**2))
|
||||
|
||||
sim.state.statephys_from_statespect()
|
||||
sim.time_stepping.start()
|
||||
```
|
||||
|
||||
## Parallel Computing with MPI
|
||||
|
||||
### Running MPI Simulations
|
||||
|
||||
Install with MPI support:
|
||||
```bash
|
||||
uv pip install "fluidsim[fft,mpi]"
|
||||
```
|
||||
|
||||
Run with MPI:
|
||||
```bash
|
||||
mpirun -np 8 python simulation_script.py
|
||||
```
|
||||
|
||||
FluidSim automatically detects MPI and distributes computation.
|
||||
|
||||
### MPI-Specific Parameters
|
||||
|
||||
```python
|
||||
# No special parameters needed
|
||||
# FluidSim handles domain decomposition automatically
|
||||
|
||||
# For very large 3D simulations
|
||||
params.oper.nx = 512
|
||||
params.oper.ny = 512
|
||||
params.oper.nz = 512
|
||||
|
||||
# Run with: mpirun -np 64 python script.py
|
||||
```
|
||||
|
||||
### Output with MPI
|
||||
|
||||
Output files are written from rank 0 processor. Analysis scripts work identically for serial and MPI runs.
|
||||
|
||||
## Parametric Studies
|
||||
|
||||
### Running Multiple Simulations
|
||||
|
||||
Script to generate and run multiple parameter combinations:
|
||||
|
||||
```python
|
||||
from fluidsim.solvers.ns2d.solver import Simul
|
||||
import numpy as np
|
||||
|
||||
# Parameter ranges
|
||||
viscosities = [1e-3, 5e-4, 1e-4, 5e-5]
|
||||
resolutions = [128, 256, 512]
|
||||
|
||||
for nu in viscosities:
|
||||
for nx in resolutions:
|
||||
params = Simul.create_default_params()
|
||||
|
||||
# Configure simulation
|
||||
params.oper.nx = params.oper.ny = nx
|
||||
params.nu_2 = nu
|
||||
params.time_stepping.t_end = 10.0
|
||||
|
||||
# Unique output directory
|
||||
params.output.sub_directory = f"nu{nu}_nx{nx}"
|
||||
|
||||
# Run simulation
|
||||
sim = Simul(params)
|
||||
sim.time_stepping.start()
|
||||
```
|
||||
|
||||
### Cluster Submission
|
||||
|
||||
Submit multiple jobs to a cluster:
|
||||
|
||||
```python
|
||||
from fluiddyn.clusters.legi import Calcul8 as Cluster
|
||||
|
||||
cluster = Cluster()
|
||||
|
||||
for nu in viscosities:
|
||||
for nx in resolutions:
|
||||
script_content = f"""
|
||||
from fluidsim.solvers.ns2d.solver import Simul
|
||||
|
||||
params = Simul.create_default_params()
|
||||
params.oper.nx = params.oper.ny = {nx}
|
||||
params.nu_2 = {nu}
|
||||
params.time_stepping.t_end = 10.0
|
||||
params.output.sub_directory = "nu{nu}_nx{nx}"
|
||||
|
||||
sim = Simul(params)
|
||||
sim.time_stepping.start()
|
||||
"""
|
||||
|
||||
with open(f"job_nu{nu}_nx{nx}.py", "w") as f:
|
||||
f.write(script_content)
|
||||
|
||||
cluster.submit_script(
|
||||
f"job_nu{nu}_nx{nx}.py",
|
||||
name_run=f"sim_nu{nu}_nx{nx}",
|
||||
nb_nodes=1,
|
||||
nb_cores_per_node=24,
|
||||
walltime="12:00:00"
|
||||
)
|
||||
```
|
||||
|
||||
### Analyzing Parametric Studies
|
||||
|
||||
```python
|
||||
import os
|
||||
import pandas as pd
|
||||
from fluidsim import load_sim_for_plot
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
results = []
|
||||
|
||||
# Collect data from all simulations
|
||||
for sim_dir in os.listdir("simulations"):
|
||||
sim_path = f"simulations/{sim_dir}"
|
||||
if not os.path.isdir(sim_path):
|
||||
continue
|
||||
|
||||
try:
|
||||
sim = load_sim_for_plot(sim_path)
|
||||
|
||||
# Extract parameters
|
||||
nu = sim.params.nu_2
|
||||
nx = sim.params.oper.nx
|
||||
|
||||
# Extract results
|
||||
df = sim.output.spatial_means.load()
|
||||
final_energy = df["E"].iloc[-1]
|
||||
mean_energy = df["E"].mean()
|
||||
|
||||
results.append({
|
||||
"nu": nu,
|
||||
"nx": nx,
|
||||
"final_energy": final_energy,
|
||||
"mean_energy": mean_energy
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"Error loading {sim_dir}: {e}")
|
||||
|
||||
# Analyze results
|
||||
results_df = pd.DataFrame(results)
|
||||
|
||||
# Plot results
|
||||
plt.figure(figsize=(10, 6))
|
||||
for nx in results_df["nx"].unique():
|
||||
subset = results_df[results_df["nx"] == nx]
|
||||
plt.plot(subset["nu"], subset["mean_energy"],
|
||||
marker="o", label=f"nx={nx}")
|
||||
|
||||
plt.xlabel("Viscosity")
|
||||
plt.ylabel("Mean Energy")
|
||||
plt.xscale("log")
|
||||
plt.legend()
|
||||
plt.savefig("parametric_study_results.png")
|
||||
```
|
||||
|
||||
## Custom Solvers
|
||||
|
||||
### Extending Existing Solvers
|
||||
|
||||
Create a new solver by inheriting from an existing one:
|
||||
|
||||
```python
|
||||
from fluidsim.solvers.ns2d.solver import Simul as SimulNS2D
|
||||
from fluidsim.base.setofvariables import SetOfVariables
|
||||
|
||||
class SimulCustom(SimulNS2D):
|
||||
"""Custom solver with additional physics"""
|
||||
|
||||
@staticmethod
|
||||
def _complete_params_with_default(params):
|
||||
"""Add custom parameters"""
|
||||
SimulNS2D._complete_params_with_default(params)
|
||||
params._set_child("custom", {"param1": 0.0})
|
||||
|
||||
def __init__(self, params):
|
||||
super().__init__(params)
|
||||
# Custom initialization
|
||||
|
||||
def tendencies_nonlin(self, state_spect=None):
|
||||
"""Override to add custom tendencies"""
|
||||
tendencies = super().tendencies_nonlin(state_spect)
|
||||
|
||||
# Add custom terms
|
||||
# tendencies.vx_fft += custom_term_vx
|
||||
# tendencies.vy_fft += custom_term_vy
|
||||
|
||||
return tendencies
|
||||
```
|
||||
|
||||
Use the custom solver:
|
||||
```python
|
||||
params = SimulCustom.create_default_params()
|
||||
# Configure params...
|
||||
sim = SimulCustom(params)
|
||||
sim.time_stepping.start()
|
||||
```
|
||||
|
||||
## Online Visualization
|
||||
|
||||
Display fields during simulation:
|
||||
|
||||
```python
|
||||
params.output.ONLINE_PLOT_OK = True
|
||||
params.output.periods_plot.phys_fields = 1.0 # plot every 1.0 time units
|
||||
params.output.phys_fields.field_to_plot = "vorticity"
|
||||
|
||||
sim = Simul(params)
|
||||
sim.time_stepping.start()
|
||||
```
|
||||
|
||||
Plots appear in real-time during execution.
|
||||
|
||||
## Checkpoint and Restart
|
||||
|
||||
### Automatic Checkpointing
|
||||
|
||||
```python
|
||||
params.output.periods_save.phys_fields = 1.0 # save every 1.0 time units
|
||||
```
|
||||
|
||||
Fields are saved automatically during simulation.
|
||||
|
||||
### Manual Checkpointing
|
||||
|
||||
```python
|
||||
# During simulation
|
||||
sim.output.phys_fields.save()
|
||||
```
|
||||
|
||||
### Restarting from Checkpoint
|
||||
|
||||
```python
|
||||
params = Simul.create_default_params()
|
||||
params.init_fields.type = "from_file"
|
||||
params.init_fields.from_file.path = "simulation_dir/state_phys_t5.000.h5"
|
||||
params.time_stepping.t_end = 20.0 # extend simulation
|
||||
|
||||
sim = Simul(params)
|
||||
sim.time_stepping.start()
|
||||
```
|
||||
|
||||
## Memory and Performance Optimization
|
||||
|
||||
### Reduce Memory Usage
|
||||
|
||||
```python
|
||||
# Disable unnecessary outputs
|
||||
params.output.periods_save.spectra = 0 # disable spectra saving
|
||||
params.output.periods_save.spect_energy_budg = 0 # disable energy budget
|
||||
|
||||
# Reduce spatial field saves
|
||||
params.output.periods_save.phys_fields = 10.0 # save less frequently
|
||||
```
|
||||
|
||||
### Optimize FFT Performance
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
# Select FFT library
|
||||
os.environ["FLUIDSIM_TYPE_FFT2D"] = "fft2d.with_fftw"
|
||||
os.environ["FLUIDSIM_TYPE_FFT3D"] = "fft3d.with_fftw"
|
||||
|
||||
# Or use MKL if available
|
||||
# os.environ["FLUIDSIM_TYPE_FFT2D"] = "fft2d.with_mkl"
|
||||
```
|
||||
|
||||
### Time Step Optimization
|
||||
|
||||
```python
|
||||
# Use adaptive time stepping
|
||||
params.time_stepping.USE_CFL = True
|
||||
params.time_stepping.CFL = 0.8 # slightly larger CFL for faster runs
|
||||
|
||||
# Use efficient time scheme
|
||||
params.time_stepping.type_time_scheme = "RK4" # 4th order Runge-Kutta
|
||||
```
|
||||
Reference in New Issue
Block a user