Introduction
This package computes coherent structures using the Finite-Time Lyapunov Exponent (FTLE) for flat Euclidean domains in 2D and 3D. It advects a uniform grid of particles through a (possibly time-dependent) velocity field sampled at scattered points, then estimates the deformation gradient from the advected grid to produce FTLE and a simple isotropy measure.
Repository: github.com/bfencil/EuclideanFTLEPythonMatlab
The MATLAB translation mirrors the Python pipeline and directory layout, but this page documents the Python side.
Pre-requisites
Developed with Python 3.11 on Windows 10. Required packages:
numpyscipymatplotlibnumba
Install them via:
pip install -r requirements.txt
Installation
git clone https://github.com/bfencil/EuclideanFTLEPythonMatlab.git
cd EuclideanFTLEPythonMatlab
pip install -r requirements.txt
Repository layout (Python)
FTLE/
PythonCode2D/
FlatFTLEMain2D.py # entry points: run_FTLE_2d, FTLE_2d
advection2D.py # RK4_advection_2d
FTLECompute2D.py # FTLE_2d_compute
utilities.py # subdivide_time_steps, plot_FTLE_2d
PythonCode3D/
FlatFTLEMain3D.py # entry points: run_FTLE_3d, FTLE_3d
advection3D.py # RK4_advection_3d
FTLECompute3D.py # FTLE_3d_compute
utilities.py # subdivide_time_steps, plot_FTLE_3d
Examples/
Data/
bickley_flow_data.h5 / .mat # 2D example data
abc_flow_data.h5 / .mat # 3D example data
BickleyFlowPython.py # 2D usage example
ABC_FlowPython.py # 3D usage example
Data format
The 2D and 3D codes differ only by the presence of a third spatial axis z. We refer to the spatial dimension as d ∈ {2,3}.
- Time steps: array of length
T(ints/floats) indexing your velocity snapshots. - Velocity sample locations:
velocity_pointswith shape(M, d).
These are fixed in time. - Velocity values:
velocity_vectorswith shape(M, d, T)for time-dependent data (or(M, d)for time-independent; broadcastingT=1also works). - Initial particle grid:
- 2D:
x_grid_parts, y_grid_partsas 2D arrays produced bynp.meshgrid(ornp.ndgrid-style layout). - 3D:
x_grid_parts, y_grid_parts, z_grid_partsas 3D arrays fromnp.meshgrid(..., indexing='ij')(or transposed equivalently).
- 2D:
Important: make sure the particle grid lies inside the convex hull of velocity_points. Outside the hull, the Python interpolator uses a fill value of 0 (see “Numerical details”).
Workflow overview
- Subdivide time: build a fine time vector between the chosen
initial_timeandfinal_timeusing stepdt ∈ (0,1].dtis a fraction of one integer frame; e.g., if your frames are at integers,dt=0.2creates 5 RK4 substeps per frame.
- Advection (RK4): integrate particle positions with a classical RK4 solver that:
- linearly interpolates in time between the two neighboring velocity frames;
- uses scattered linear interpolation in space over
velocity_pointsat each substep.
- Deformation/FTLE:
- estimate the deformation gradient on the advected grid via centered finite differences;
-
form the Cauchy–Green tensor
\(C = F^\top F\);
-
compute FTLE from the largest eigenvalue $$\lambda_{\max}(C)$:
$$\mathrm{FTLE} = \frac{1}{2\, t_f - t_i }\,\log \big(\sqrt{\lambda_{\max}(C)}\big)$$. -
compute a simple isotropy measure:
$$\mathrm{Iso} = \frac{1}{2\, t_f - t_i }\,\log \det(C)$$.
Both forward and backward FTLE are supported by reversing the time axis and using (dt<0) internally.
API summary (Python)
2D
run_FTLE_2d(...)— single call to run forward & backward FTLE, optionally plot results.- Inputs:
velocity_points,velocity_vectors,x_grid_parts,y_grid_parts,dt,initial_time,final_time,time_steps,plot_ftle=False,save_plot_path=None. - Outputs: forward
(ftle, trajectories, isotropy)and backward(back_ftle, back_trajectories, back_isotropy);ftleandisotropyare flattened to match plotting utilities;trajectorieshas shape(N_particles, 2, K).
- Inputs:
-
FTLE_2d(...)— internal driver used byrun_FTLE_2d. -
RK4_advection_2d(...)— RK4 particle integration with time/space interpolation. -
FTLE_2d_compute(...)— centered differences + Cauchy–Green + FTLE/isotropy (returns flattened arrays). subdivide_time_steps(...),plot_FTLE_2d(...)— utilities.
3D
-
run_FTLE_3d(...)— as above, withz_grid_parts. -
FTLE_3d(...),RK4_advection_3d(...),FTLE_3d_compute(...), and utilities mirror the 2D versions with 3D arrays and a (3\times 3) deformation gradient.
Numerical details & choices
- Time interpolation: linear interpolation between the integer frames bracketing each substep time (t).
- Spatial interpolation:
scipy.interpolate.LinearNDInterpolatoron a Delaunay triangulation ofvelocity_pointseach step; outside the convex hull, the fill value is 0 (particles feel zero velocity). - Time stepping: classical RK4 with constant
dtacross the fine time vector. - Deformation gradient:
- 2D: centered differences along the advected grid axes to form (F \in \mathbb{R}^{2\times 2}).
- 3D: centered differences give (F \in \mathbb{R}^{3\times 3}).
- Cauchy–Green (C = F^\top F); eigenvalues from the symmetric part for robustness.
- Outputs:
trajectories:(N_particles, d, K)positions for all substeps including the start.ftle,isotropy: returned flattened (length (N_{\text{particles}})) for easy plotting; reshape with the original grid dimensions if needed.
Running the examples
Example scripts live in FTLE/Examples/:
-
2D (Bickley jet):
BickleyFlowPython.py
LoadsExamples/Data/bickley_flow_data.(h5|mat)and runsrun_FTLE_2d(...)with plotting. -
3D (ABC flow):
ABC_FlowPython.py
LoadsExamples/Data/abc_flow_data.(h5|mat)and runsrun_FTLE_3d(...)with plotting.
Run them from the repo root or FTLE/Examples (they set up relative paths internally).
Practical tips
- Grid inside data hull: choose the particle grid so it stays inside the convex hull of
velocity_pointsduring the integration window. Otherwise, parts of the domain will behave as if the velocity were zero. dtvs.time_steps:dtis a fraction of one unit gap intime_steps. If your velocity frames are not unit-spaced, scale accordingly.- Boundaries: centered differences skip the outermost ring of the grid; FTLE there is returned as
NaN. - Performance:
- Advection is dominated by scattered interpolation. Coarser
dtor fewer particles speeds things up. - If your samples lie on a regular grid, consider switching to grid-based interpolators for large problems.
- Advection is dominated by scattered interpolation. Coarser
Troubleshooting
- “Initial and final times must be in
time_steps.”
Use values from the providedtime_stepsarray; the drivers index into it. - Particles stop moving in corners
Likely outside the convex hull ofvelocity_points; expand the data region or shrink the particle grid/time window. - All
NaNFTLE on the border
Expected: centered differences require neighbors; only interior cells get FTLE values.