Chris Parmer — home

Carpet plot of an airfoil C-mesh with pressure-coefficient contours

Aerospace

Example from the compendium of canonical charts

Aerospace — Carpet plot of an airfoil C-mesh with pressure-coefficient contours

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