Laminar Flow Analysis Guide¶
Learning Objectives
By the end of this section you will understand:
The physical setup for laminar flow XPCS (Taylor-Couette geometry)
How to configure homodyne for laminar flow experiments
The anti-degeneracy system and why it matters
Angular dependence diagnostics
Common laminar flow fitting problems and solutions
—
Physical Setup: Taylor-Couette Geometry¶
Laminar flow XPCS is most commonly performed in a Taylor-Couette (concentric cylinder) shear cell. The sample fills the gap between a stationary outer cylinder (stator) and a rotating inner cylinder (rotor).
The key geometric parameters:
Gap distance \(h\): stator-rotor separation (µm). Configured as
gap_distancein YAML (µm, converted to Å internally: 1 µm = 1×10⁴ Å).Flow direction \(\phi_0\): azimuthal angle of the flow velocity relative to the detector coordinate system.
Wavevector \(q\): in the horizontal plane, magnitude in Å⁻¹.
The shear-induced dynamics appear in the XPCS signal because particles are displaced by the shear flow during the correlation time. The displacement at time lag \(\Delta t = t_2 - t_1\) is:
where \(\Gamma\) is the accumulated shear strain. This displacement introduces a sinc² factor in \(C_2\):
The sinc² function creates zeros (nulls) at specific combinations of \(q\), \(h\), \(\Gamma\), and \(\phi\), giving XPCS its sensitivity to shear rate.
—
Configuring for Laminar Flow¶
Minimum required configuration:
data:
file_path: "shear_data.h5"
gap_distance: 500.0 # µm (e.g., 500 µm = 0.5 mm gap)
q_value: 0.054 # Å⁻¹
dt: 0.1 # s (frame interval)
analysis:
mode: "laminar_flow"
optimization:
method: "nlsq"
nlsq:
anti_degeneracy:
per_angle_mode: "auto" # Critical!
parameter_space:
D0:
initial: 1.0
bounds: [0.001, 1.0e5]
alpha:
initial: -0.5
bounds: [-2.0, 1.0]
D_offset:
initial: 0.01
bounds: [0.0, 1.0e3]
gamma_dot_0:
initial: 0.5 # Start near expected shear rate
bounds: [1.0e-6, 1.0e4]
beta:
initial: 0.0
bounds: [-2.0, 2.0]
gamma_dot_t_offset:
initial: 0.001
bounds: [0.0, 1.0e3]
phi_0:
initial: 0.0
bounds: [-180.0, 180.0]
See YAML Configuration Reference for the complete YAML schema.
Generate a laminar flow template:
homodyne-config --mode laminar_flow --output config_flow.yaml
# Edit the output file to set your q_value, gap_distance, and dt
—
Anti-Degeneracy System¶
The per_angle_mode: "auto" setting is the most important configuration
choice for laminar flow analysis. Without it, the optimizer can find degenerate
solutions where:
\(\dot\gamma_0\) becomes very large (unphysical)
Contrast drops to near-zero to compensate
\(\chi^2\) appears low but the result is physically wrong
How ``auto`` mode prevents degeneracy:
Before optimization, quantile-based estimates of contrast and offset are computed for each angle separately
These estimates are averaged across all angles
The averaged contrast and averaged offset are added as 2 free parameters (total: 9 parameters for laminar_flow)
The optimizer adjusts these 2 averaged values, preventing the physical parameters from absorbing them
Empirical validation:
The auto mode has been validated on synthetic data with known ground truth.
It recovers:
\(D_0\) to within 2–5%
\(\dot\gamma_0\) to within 3–8%
\(\phi_0\) to within 1–3°
Without anti-degeneracy, \(D_0\) errors can exceed 50%.
—
Angular Dependence Visualization¶
Visualizing the angular dependence helps verify that the laminar flow model is appropriate and that the fit is capturing the physics:
import numpy as np
import matplotlib.pyplot as plt
from homodyne.data import load_xpcs_data
data = load_xpcs_data("config.yaml")
c2 = data['c2_exp'] # (n_phi, n_t1, n_t2)
phi = data['phi_angles_list']
t1 = data['t1']
t2 = data['t2']
# Choose a fixed lag time
lag = 5 # index into time axis
fig, ax = plt.subplots(figsize=(8, 5))
for i_phi, phi_val in enumerate(phi):
# Extract one-lag diagonal of C2 for this angle
n = min(c2.shape[1], c2.shape[2]) - lag
c2_lag = np.array([c2[i_phi, k, k + lag] for k in range(n)])
ax.plot(np.arange(n), c2_lag, label=f"{phi_val:.0f}°", alpha=0.7)
ax.set_xlabel("Frame index")
ax.set_ylabel(f"C2 at lag={lag}")
ax.set_title("Angular dependence of C2 (expect spread for laminar flow)")
ax.legend(ncol=3, fontsize=8)
plt.tight_layout()
plt.show()
Expected patterns:
No angular dependence (curves overlap) → use static mode instead
Systematic spread with \(\phi\) → laminar flow present; check \(\phi_0\)
Oscillatory pattern (sinc nulls) → strong shear, clear signal
—
Interpreting Shear Parameters¶
gamma_dot_0 (reference shear rate):
Compare to the applied shear rate from your rheometer:
applied_shear_rate = 10.0 # s⁻¹ (from rheometer setting)
fitted_shear_rate = params_dict['gamma_dot_0']
print(f"Applied: {applied_shear_rate:.3f} s⁻¹")
print(f"Fitted: {fitted_shear_rate:.3f} s⁻¹")
print(f"Ratio: {fitted_shear_rate / applied_shear_rate:.2f}")
A ratio near 1.0 validates the measurement. Significant deviations may indicate slip at the walls or non-Newtonian flow profiles.
phi_0 (angular offset):
Should be reproducible across measurements with the same shear cell orientation. If it varies randomly, check:
That the flow direction is consistent between experiments
That the shear cell is properly aligned to the beam
beta (shear rate time dependence):
For steady Couette flow: \(\beta \approx 0\). Significant \(|\beta| > 0.2\) may indicate:
Transient startup behavior in the measured time window
Shear banding or wall slip at some timescales
Data collected during a flow ramp
—
Troubleshooting Laminar Flow Fits¶
Problem: gamma_dot_0 converges to near zero
Possible causes:
The shear rate is too small to produce visible sinc oscillations (check: \(q h \dot\gamma \Delta t_\text{max} \ll 1\))
phi_0 is far from correct — the sinc cos(phi - phi_0) factor cancels the angular dependence at all angles
Fix: provide a tighter initial estimate for phi_0 and gamma_dot_0.
# Estimate expected sinc argument at maximum lag time
q = 0.054 # Å⁻¹
h = 5000.0 # Å (0.5 µm gap)
gamma_dot = 1.0 # s⁻¹ (expected)
t_max = 10.0 # s (max lag)
sinc_arg = 0.5 * q * h * gamma_dot * t_max
print(f"Max sinc argument: {sinc_arg:.2f}")
# Values < 0.5 → weak shear signal; values 1-3 → clear oscillations
Problem: phi_0 oscillates between +90° and -90°
The cos factor in the sinc argument is periodic, so \(\phi_0\) and \(\phi_0 + 180°\) can give similar \(\chi^2\). This is a true degeneracy. Fix: constrain phi_0 to a smaller range based on known geometry:
parameter_space:
phi_0:
initial: 0.0
bounds: [-90.0, 90.0] # Constrained by geometry
Problem: Very slow convergence
Laminar flow has a more complex loss landscape than static mode. Options:
Enable multi-start NLSQ with LHS sampling
Enable CMA-ES for the first pass (see CMA-ES for Multi-Scale Problems)
Increase NLSQ max iterations:
optimization:
nlsq:
max_nfev: 5000 # Increase from default 1000
Problem: Physical parameters unphysical (D0 >> expected)
Enable anti-degeneracy:
optimization:
nlsq:
anti_degeneracy:
per_angle_mode: "auto" # Was "constant" or missing
—
See Also¶
Analysis Modes — Laminar flow mode parameters
Parameter Interpretation Guide — Shear parameter interpretation
Per-Angle Scaling Modes — Anti-degeneracy in depth
CMA-ES for Multi-Scale Problems — CMA-ES for multi-scale problems
NLSQ Fitting Guide — NLSQ fitting workflow