Carpet plot of an airfoil C-mesh with pressure-coefficient contours
Aerospace
Example from the compendium of canonical charts
Python Code
"""Aerospace — Carpet plot of an airfoil C-mesh with pressure-coefficient contours."""
from pathlib import Path
# ── Palette + theme (matching the Plotly Studio gallery these charts ship in) ──
VIOLET, TEAL, GREEN, PINK, ORANGE = "#845EEE", "#52B3D0", "#55B685", "#DA5597", "#E9A23B"
PRIMARY, SECONDARY = VIOLET, TEAL
COLORWAY = [VIOLET, TEAL, GREEN, PINK, ORANGE]
BG, TEXT, GRID, MUTED = "#ffffff", "#1c2024", "#d9d9e0", "#60646c"
FONT = "Inter, -apple-system, BlinkMacSystemFont, sans-serif"
COLORSCALE = [[0, "rgba(132, 94, 238, 0.05)"], [1, "rgba(132, 94, 238, 0.9)"]]
def apply_theme(fig):
"""Light gallery theme: white background, Inter font, soft gridlines."""
fig.update_layout(
paper_bgcolor=BG, plot_bgcolor=BG, colorway=COLORWAY,
font=dict(family=FONT, color=TEXT, size=12),
legend=dict(font=dict(color=TEXT)),
hoverlabel=dict(bgcolor="#f0f0f3", font=dict(color=TEXT, family=FONT), bordercolor=GRID),
)
fig.update_xaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
fig.update_yaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
def fetch_csv(url, **kwargs):
import io
import pandas as pd
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return pd.read_csv(io.StringIO(r.text), **kwargs)
def fetch_json(url):
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return r.json()
import numpy as np
import plotly.graph_objects as go
URL = "https://raw.githubusercontent.com/bcdunbar/datasets/master/airfoil_data.json"
def generate():
# The dataset is a 7-element list: data[0] holds the curvilinear mesh
# (parametric a/b coordinates + their cartesian x/y positions) and
# data[1] holds the pressure coefficient Cp sampled on that same mesh.
# a/b/x/y/z are passed to the carpet traces as-is (a is 1D, the rest 2D
# of shape len(b) x len(a)) — no flattening.
try:
raw = fetch_json(URL)
mesh, field = raw[0], raw[1]
a, b = mesh["a"], mesh["b"]
x, y = mesh["x"], mesh["y"]
z = field["z"]
print(f"Loaded: a={len(a)}, b={len(b)}, mesh={len(x)}x{len(x[0])}")
except Exception as e:
print(f"Download failed ({e}), using synthetic O-mesh around a body")
a = np.linspace(1.0, 5.0, 31) # radial station (inner dim)
b = np.linspace(0.0, 2 * np.pi, 71) # angular station (outer dim)
R, TH = np.meshgrid(a, b) # (71, 31)
x = (R * np.cos(TH)).tolist()
y = (R * np.sin(TH)).tolist()
z = (-2.0 / R * np.cos(TH) - 0.5).tolist() # dipole-like pressure field
a, b = a.tolist(), b.tolist()
fig = go.Figure()
# Carpet base: the deformed grid itself. a/b ticks are mesh indices, not
# physically meaningful, so hide the labels and let the grid carry the eye.
fig.add_trace(go.Carpet(
a=a,
b=b,
x=x,
y=y,
aaxis=dict(showticklabels="none", gridcolor=GRID, startline=False, endline=False),
baxis=dict(showticklabels="none", gridcolor=GRID, startline=False, endline=False),
name="Mesh",
))
# Pressure-coefficient contours filled onto the same carpet.
# Clip the contour window to the physically interesting band: the
# free stream sits near Cp=0 and the leading-edge suction peak dips to
# roughly -6, which would otherwise wash the whole field into one color.
fig.add_trace(go.Contourcarpet(
a=a,
b=b,
z=z,
colorscale="RdBu",
reversescale=True,
contours=dict(start=-2.0, end=1.0, size=0.1),
line=dict(width=0.4, color="rgba(0,0,0,0.25)"),
colorbar=dict(title="Cp"),
name="Cp",
))
fig.update_layout(
xaxis=dict(title="x", showgrid=False, zeroline=False),
yaxis=dict(title="y", showgrid=False, zeroline=False,
scaleanchor="x", scaleratio=1),
)
apply_theme(fig)
return fig
fig = generate()
fig.show()
Made with Plotly