Create matplotlib figures that meet journal submission standards: correct fonts,
Skill: Generate Publication-Quality Figures
Purpose
Create matplotlib figures that meet journal submission standards: correct fonts,
compact sizes, consistent styling, readable labels, and high DPI. Based on
lessons learned from the CPA and TPflash papers (Fluid Phase Equilibria 2026).
SI Units (MANDATORY)
All figure axis labels MUST use SI units. See PAPER_WRITING_GUIDELINES.md
"SI Units (MANDATORY)" for the full reference.
Axis label examples (GOOD)
NEVER use
Temperature (K) or $T$ (K)
Temperature (°F)
Pressure (kPa) or $P$ (MPa)
Pressure (psi) or Pressure (atm)
Density (kg/m³) or $\rho$ (kg/m$^3$)
Density (lb/ft³)
Viscosity (mPa·s)
Viscosity (cP) — numerically equal but use SI name
Flow rate (kg/s)
Flow rate (lb/h)
Energy (kJ/mol)
Energy (BTU/lbmol)
"bar" is acceptable for pressure axes in engineering contexts (1 bar = 100 kPa).
When to Use
Creating figures for any scientific paper in the paperlab
Regenerating figures after data or style revisions
Setting up a new 02_generate_figures.py for a paper project
Core Setup (Copy-Paste Starter)
Every figure script should start with this rc configuration:
Key rule: NEVER use contour lines on noisy gridded data — they create
ugly loops. Use pcolormesh instead. If overlaying contours, use very few
levels (3–5) and ensure the data is smooth.
Pattern 3: Scatter with Per-Point Labels (Scaling)
When points cluster at the same x-value, labels will overlap. Use these
techniques:
fig, ax = plt.subplots(figsize=FIG_SINGLE_TALL)
# 1. Define manual offsets per data point to prevent overlap# Format: {system_id: (dx, dy)} in data coordinates or points
offsets = {
"A1": (5, -8), "A2": (5, 5), "B1": (-40, 5),
"B2": (5, 3), "C1": (5, -8), "C2": (5, 5),
}
# 2. Jitter x-coordinates to separate clustered points
np.random.seed(42)
x_jitter = x_values + np.random.uniform(-0.15, 0.15, len(x_values))
ax.scatter(x_jitter, y_values, s=30, color=BLUE, zorder=5)
for i, (sid, xj, yv) inenumerate(zip(system_ids, x_jitter, y_values)):
dx, dy = offsets.get(sid, (5, 3))
ax.annotate(sid, (xj, yv), textcoords="offset points",
xytext=(dx, dy), fontsize=7, color=GREY,
arrowprops=dict(arrowstyle="-", color=GREY, lw=0.3) ifabs(dx) > 10elseNone)
ax.set_xlabel("Component count $N_c$")
ax.set_ylabel("Speedup factor")
ax.grid(True, ls="--")
fig.savefig(FIGURES_DIR / )
plt.close()
Key rules:
ALWAYS check for label overlap visually — automated layouts (adjust_text) often fail
For ≤15 points, define manual offsets dict during review
Use short system IDs (A1, B3) not full names — put the legend in the caption
Pattern 4: Box Plot with Extreme Outliers
When data has a wide range (e.g., 1× to 30×), standard box plots compress
the majority of the data. Solution: log scale.
fig, ax = plt.subplots(figsize=FIG_DOUBLE)
bp = ax.boxplot(data_by_group, labels=short_labels,
patch_artist=True, showfliers=True, widths=0.5,
medianprops=dict(color=ORANGE, lw=1.2),
flierprops=dict(marker="o", ms=3, mfc="none", mec=GREY))
for patch in bp["boxes"]:
patch.set_facecolor(BLUE)
patch.set_alpha(0.35)
ax.set_yscale("log") # Critical for wide-range data
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda v, _: f"{v:.1f}"if v < 10elsef"{v:.0f}"))
ax.set_ylabel("Speedup factor")
ax.axhline(y=1.0, color="grey", ls="--", lw=0.6, label="Parity")
ax.grid(True, axis="y", ls="--")
ax.legend(frameon=False)
fig.savefig(FIGURES_DIR / "fig5_boxplot.png")
plt.close()
Key rule: Use log scale whenever max/min > 10. The linear scale will
squash all boxes into a thin band at the bottom.
This checks DPI, file format, minimum dimensions, and color mode against
the journal profile. Fix all [!!] items before submission.
Alternative: Use figure_style.py Helper
Instead of manual rcParams setup, you can use the tools/figure_style.py
module which wraps SciencePlots with journal presets:
from tools.figure_style import apply_style, save_fig, PALETTE, FIG_SINGLE
apply_style("elsevier") # or "ieee", "nature", "acs"
fig, ax = plt.subplots(figsize=FIG_SINGLE) # 3.5 × 2.8 inches
ax.plot(x, y, color=PALETTE[0])
save_fig(fig, "figures/fig01_results.png", dpi=300)
Common Mistakes Caught from CPA Paper
Rotated bar labels: Never rotate > 30°. Use short IDs instead.
Contour lines on noisy data: Creates ugly loops. Use pcolormesh.
Linear scale box plots with outliers: One 30× outlier squashes all
other boxes to a 1-pixel line. Always use log scale for wide ranges.
Full system names as point labels: "Methane/Ethane/Propane/n-Butane"
overlaps with neighbors. Use "B1" and define in table.
Large figure sizes: 10×8 inch figures waste journal space. Use
3.5×2.8 (single) or 7.0×3.5 (double column).
Inconsistent annotation offsets: When 4 points cluster at the same
x-value, automated label placement fails. Use manual offsets dict.
Missing parity/reference lines: Always add y=1 line on speedup plots,
parity line on comparison plots.
Conceptual / Architectural Diagrams
For framework papers, method papers, and system architecture descriptions,
you need conceptual diagrams (layered architectures, workflow arrows,
feedback loops) — not data plots. Use matplotlib.patches and
matplotlib.text for full control over layout, styling, and publication
quality.
Why matplotlib for conceptual diagrams?
300 DPI + PDF vector output — meets all journal requirements
Exact font control — matches paper body (Times New Roman, 9pt)
Reproducible — script regenerates identical figure after revisions
No external tools — no Visio/PowerPoint/draw.io screenshots
Consistent palette — same colors as data plots in the same paper