Skip to content

Instantly share code, notes, and snippets.

@TarickWake
Last active March 19, 2025 13:06
Show Gist options
  • Save TarickWake/add1e2e58eb4e94d4b42dcf459353989 to your computer and use it in GitHub Desktop.
Save TarickWake/add1e2e58eb4e94d4b42dcf459353989 to your computer and use it in GitHub Desktop.
create_log_law_plot

📊 Log-Law Plot Tool for Wall Shear Analysis

GitHub license

A specialized visualization tool for fluid dynamics researchers that creates diagnostic plots to analyze velocity profiles and wall shear stress.

🌊 What is this?

This tool creates specialized log-log plots with diagonal reference lines that help identify shear stress patterns in velocity profiles. The diagonal lines represent constant shear rates (u = α·y), making it easy to visualize where your data follows linear shear relationships.

✨ Features

📈 Creates log-log plots with reference shear lines 🔍 Helps visualize wall shear stress in velocity profiles 🛠️ Works with existing matplotlib axes or creates new ones 📏 Automatically scales based on your data 🎨 Customizable appearance and labels 📋 Installation 📚 Usage Example Here's how to use the log-law plot tool with your velocity profile data:

🔧 Parameters

  • ax: Matplotlib axes to transform (optional)
  • xlim: X-axis limits (distance from wall)
  • ylim: Y-axis limits (velocity)
  • data: List of dictionaries with data to plot
  • A_values: List of gradient values to display as diagonal lines
  • A_labels: Subset of A_values to label
  • show_grid: Whether to display a reference grid
  • show_annotation: Whether to add explanatory annotation
  • log_spacing: Use logarithmic spacing for gradient lines
  • title: Plot title

🖼️ Example Output

from manual_prof_dir_utils import process_profile
from plot_style_ct01 import draw_loglog_slope
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
from matplotlib.ticker import LogLocator, LogFormatter


# Conserver votre code de chargement des données
manual_prof = process_profile("manual_prof.xlsx",
                              px_per_mm=px_per_mm,
                              y_shift=y_shift,
                              fps=fps)

# Définir les limites du graphique
xlim = (1e-5, 1e-2)
ymin = max(0.8 * manual_prof["u"].min(), 1e-3)
ymax = 1.2 * manual_prof["u"].max()
ylim = (ymin, ymax)


# Exemple d'intégration dans votre code:
# Après avoir créé votre graphique initial:

# Importer la fonction

# Option 1: Transformer un graphique matplotlib existant
fig, ax = plt.subplots(figsize=(12, 10))
ax.plot(manual_prof["y"] - y_shift,
        manual_prof["u"],
        marker="o",
        ls="-",
        alpha=0.7,
        color='C0',
        label="Tracking")
ax.plot(u_mean.y - y_shift,
        u_mean,
        marker="o",
        ls="-",
        alpha=0.7,
        color='C1',
        label="PIV")
ax.legend()
# Transformer le graphique en émagramme
fig, ax, ax2 = create_log_law_plot(
    ax=ax,
    xlim=(1e-5, 1e-2),  # Ajustez selon vos données
    ylim=(0.001, 2),  # Ajustez selon vos données
    title=f"Profil de vitesse avec log-law plot pour analyse de ∂u/∂y \n {fig_name}")

# ax2.set_ylim(0.001, 1)  # Ajustez si nécessaire

The resulting plot shows: image Your velocity profile data on a log-log scale Diagonal reference lines representing constant velocity gradients Labels for major gradient values A secondary y-axis showing the gradient values This visualization makes it easy to identify regions with constant shear stress and determine wall shear values.

📄 License

MIT License © [Clément TIFFON]

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib as mpl
from matplotlib.ticker import LogLocator
def create_log_law_plot(
ax=None,
xlim=(1e-5, 1e-2),
ylim=None,
data=None,
A_values=None,
A_labels=None,
show_grid=True,
show_annotation=True,
log_spacing=True,
title="Profil de vitesse avec émagramme pour analyse de ∂u/∂y"):
"""
Crée ou transforme un graphique existant en émagramme pour l'analyse de cisaillement pariétal.
Parameters:
-----------
ax : matplotlib.axes.Axes, optional
Axes existant à transformer. Si None, un nouvel axe est créé.
xlim : tuple, optional
Les limites de l'axe x (distance à la paroi), par défaut (1e-5, 1e-2).
ylim : tuple, optional
Les limites de l'axe y (vitesse). Si None, elles sont calculées à partir des données.
data : list of dict, optional
Liste de dictionnaires contenant les données à tracer. Chaque dict doit contenir:
- 'x': array-like, données x (distance y)
- 'y': array-like, données y (vitesse u)
- 'kwargs': dict, arguments pour plt.plot (marker, color, label, etc.)
A_values : list, optional
Liste des valeurs de gradient à tracer comme lignes diagonales.
A_labels : list, optional
Sous-ensemble de A_values pour lesquelles afficher les étiquettes.
show_grid : bool, optional
Si True, affiche une grille de référence.
show_annotation : bool, optional
Si True, ajoute une annotation explicative.
title : str, optional
Titre du graphique.
Returns:
--------
fig : matplotlib.figure.Figure
La figure contenant le graphique.
ax : matplotlib.axes.Axes
L'axe principal avec l'émagramme.
ax2 : matplotlib.axes.Axes
L'axe secondaire avec les étiquettes de gradient.
"""
# Créer un nouvel axe si nécessaire
if ax is None:
fig, ax = plt.subplots(figsize=(12, 10))
else:
fig = ax.figure
# Valeurs par défaut pour A_values et A_labels
if A_values is None:
if log_spacing:
A_values = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90,
100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 3000,
4000, 5000, 6000, 7000, 8000, 9000, 10000, 2e4, 3e4, 4e4, 5e4,
6e4, 7e4, 8e4, 9e4, 1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5,
9e5, 1e6
]
else:
A_values = [
10, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000
]
if A_labels is None:
if log_spacing:
A_labels = [1, 10, 100, 1000, 10000, 100000, 1000000]
else:
A_labels = [100, 500, 1000, 5000, 10000]
# Configurer les échelles logarithmiques
ax.set_xscale("log")
ax.set_yscale("log")
ax.set_xlim(xlim)
# Tracer les données si fournies
if data is not None:
for dataset in data:
x = dataset.get('x', [])
y = dataset.get('y', [])
kwargs = dataset.get('kwargs', {})
ax.plot(x, y, **kwargs)
# Calculer ylim à partir des données si non spécifié
if ylim is None and data is not None:
all_y_values = []
for dataset in data:
all_y_values.extend(dataset.get('y', []))
if all_y_values:
ymin = max(0.8 * min(all_y_values), 1e-3)
ymax = 1.2 * max(all_y_values)
ylim = (ymin, ymax)
else:
ylim = (1e-2, 1e2) # Valeur par défaut si pas de données
elif ylim is None:
ylim = (1e-2, 1e2) # Valeur par défaut si pas de données
ax.set_ylim(ylim)
# Tracer les lignes u=A*y
for A in A_values:
# Générer les points pour la ligne u=A*y
x_line = np.logspace(np.log10(xlim[0]), np.log10(xlim[1]), 100)
y_line = A * x_line
# Déterminer le style de ligne basé sur la valeur de A
if A in A_labels:
linestyle = '-'
linewidth = 1.0
alpha = 0.5
color = 'black'
else:
linestyle = '--'
linewidth = 0.7
alpha = 0.5
color = 'gray'
# Tracer la ligne
ax.plot(x_line,
y_line,
linestyle=linestyle,
linewidth=linewidth,
alpha=alpha,
color=color,
zorder=1)
# Ajouter une étiquette à la ligne si c'est une ligne principale
if A in A_labels:
# Trouver un point approprié pour placer l'étiquette (au milieu de la ligne visible)
mask = (y_line >= ylim[0]) & (y_line <= ylim[1])
if np.any(mask):
idx = np.where(mask)[0][len(np.where(mask)[0]) // 2]
x_label = x_line[idx]
y_label = y_line[idx]
# Dans un graphique log-log, u=A*y est une ligne droite avec une pente de 1
angle = 45 # degrés
# Créer une transformation pour placer et tourner le texte
trans_angle = plt.gca().transData.transform_angles(
np.array([angle]), np.array([[x_label, y_label]]))[0]
# Ajouter l'étiquette
ax.text(x_label,
y_label,
f"$\\alpha={A}$",
fontsize=8,
ha='center',
va='center',
rotation=angle,
rotation_mode='anchor',
bbox=dict(facecolor='white',
alpha=0.7,
edgecolor='none',
pad=1),
zorder=0)
# Ajouter une grille de référence
if show_grid:
# Grille verticale (valeurs y constantes)
y_grid = np.logspace(np.log10(xlim[0]), np.log10(xlim[1]), 7)
for y in y_grid:
ax.axvline(y,
color='lightgray',
linestyle='-',
linewidth=0.5,
alpha=0.5,
zorder=0)
# Grille horizontale (valeurs u constantes)
u_grid = np.logspace(np.log10(ylim[0]), np.log10(ylim[1]), 7)
for u in u_grid:
ax.axhline(u,
color='lightgray',
linestyle='-',
linewidth=0.5,
alpha=0.5,
zorder=0)
# Ajouter les étiquettes d'axes et le titre
ax.set_xlabel("y (m)", fontsize=12)
ax.set_ylabel("u (m/s)", fontsize=12)
ax.set_title(title, fontsize=14)
# Ajouter une annotation explicative
if show_annotation:
ax.text(
0.02,
0.02,
r"Lignes diagonales: $u = \alpha\,y $, où $\alpha$ est la pente",
transform=ax.transAxes,
fontsize=10,
va='bottom',
bbox=dict(facecolor='white',
alpha=0.7,
edgecolor='gray',
boxstyle='round,pad=0.5'))
# Ajouter un axe secondaire pour montrer explicitement les valeurs de A
ax2 = ax.twinx()
ax2.set_yscale('log')
ax2.set_ylim(ylim)
ax2.set_ylabel("Gradient de vitesse ∂u/∂y", fontsize=12)
# Aligner les ticks avec les valeurs A
tick_positions = [
A * xlim[1] for A in A_labels
if A * xlim[1] <= ylim[1] and A * xlim[1] >= ylim[0]
]
ax2.set_yticks(tick_positions)
ax2.set_yticklabels([f"{int(pos/xlim[1])}" for pos in tick_positions])
# Configurer les locators pour les ticks
ax.xaxis.set_major_locator(LogLocator(base=10.0))
ax.yaxis.set_major_locator(LogLocator(base=10.0))
# Améliorer l'apparence
plt.grid(False) # Désactiver la grille par défaut
plt.tight_layout()
return fig, ax, ax2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment