« home

Otto Cycle

physicsthermodynamicscetztikz

The Otto cycle is an idealized thermodynamic cycle of a typical spark ignition piston engine. It is the thermodynamic cycle most commonly found in automobile engines.


Otto Cycle

  Download

PNGPDFSVG

  Code

  LaTeX

otto-cycle.tex (37 lines)

\documentclass[tikz, svgnames]{standalone}

\usepackage{mathtools}
\usetikzlibrary{decorations.markings}

\def\V{10}
\def\p{7}

\tikzset{decoration={markings, mark=at position 0.5 with {\arrow{stealth}}}}

\begin{document}
\begin{tikzpicture}[thick]
  \draw[->] (0, 0) -- (0, \p) node[right] {$p$};
  \draw[->] (0, 0) -- (\V, 0) node[right] {$V$};

  \draw[dashed] (0, 0.9*\p) node[left] {$p_\text{max}$} -| (0.2*\V, 0) node[below] {$V_\text{min}$};

  \draw[dashed] (0, 0.2*\p) node[left] {$p_\text{min}$} -| (0.9*\V, 0) node[below] {$V_\text{max}$};

  \coordinate[label=above:1] (a) at (0.2*\V, 0.9*\p);
  \coordinate[label=right:2] (b) at (0.9*\V, 0.5*\p);
  \coordinate[label=right:3] (c) at (0.9*\V, 0.2*\p);
  \coordinate[label=left:4] (d) at (0.2*\V, 0.45*\p);

  \foreach \point in {a, b, c, d}
  \fill (\point) circle (3pt);

  \draw[ultra thick, DarkBlue] (a) edge[out=-40, in=180, looseness=0.7, postaction={decorate}]
  node[midway, above=2px] {$\Delta Q = 0$} (b) (b) -- (c)
  node[midway, right, blue] {$\Rightarrow Q_\text{out}$} (c)
  edge[out=180, in=-40, looseness=0.7, postaction={decorate}]
  node[midway, above=2px] {$\Delta Q = 0$} (d) (d) -- (a)
  node[midway, left, red] {$Q_\text{in} \Rightarrow$};

\end{tikzpicture}
\end{document}

  Typst

otto-cycle.typ (151 lines)

#import "@preview/cetz:0.3.4": canvas, draw
#import draw: line, content, circle, bezier, set-style

#set page(width: auto, height: auto, margin: 8pt)

#let (V, p) = (9, 6)
#set-style(line: (mark: (scale: .5)))

#canvas({
  // Draw axes
  line((0, 0), (0, p), mark: (end: "stealth", fill: black), name: "y-axis")
  content((rel: (0.2, 0), to: "y-axis.end"), $p$, name: "p-label")

  line((0, 0), (V, 0), mark: (end: "stealth", fill: black), name: "x-axis")
  content((rel: (0.2, 0), to: "x-axis.end"), $V$, name: "V-label")

  // Define key values
  let (p-min, p-max) = (0.2 * p, 0.9 * p)
  let (V-min, V-max) = (0.2 * V, 0.9 * V)

  // Create reference points for min/max values
  content((0, p-max), name: "p-max-ref", [])
  content((0, p-min), name: "p-min-ref", [])
  content((V-min, 0), name: "V-min-ref", [])
  content((V-max, 0), name: "V-max-ref", [])

  // Horizontal dashed line for p-max
  line(
    "p-max-ref",
    (rel: (V-min, 0), to: "p-max-ref"),
    stroke: (dash: "dashed", thickness: 0.8pt),
    name: "p-max-line",
  )

  // Vertical dashed line for V-min
  line(
    "V-min-ref",
    (rel: (0, p-max), to: "V-min-ref"),
    stroke: (dash: "dashed", thickness: 0.8pt),
    name: "V-min-line",
  )

  // Labels for p-max and V-min
  content((rel: (-0.5, 0), to: "p-max-ref"), $p_"max"$, name: "p-max-label")
  content((rel: (0, -0.5), to: "V-min-ref"), $V_"min"$, name: "V-min-label")

  // Horizontal dashed line for p-min
  line(
    "p-min-ref",
    (rel: (V-max, 0), to: "p-min-ref"),
    stroke: (dash: "dashed", thickness: 0.8pt),
    name: "p-min-line",
  )

  // Vertical dashed line for V-max
  line(
    "V-max-ref",
    (rel: (0, p-min), to: "V-max-ref"),
    stroke: (dash: "dashed", thickness: 0.8pt),
    name: "V-max-line",
  )

  // Labels for p-min and V-max
  content((rel: (-0.5, 0), to: "p-min-ref"), $p_"min"$, name: "p-min-label")
  content((rel: (0, -0.5), to: "V-max-ref"), $V_"max"$, name: "V-max-label")

  // Define cycle points
  circle((V-min, p-max), radius: 3pt, fill: black, name: "point-a")

  circle((V-max, 0.5 * p), radius: 3pt, fill: black, name: "point-b")

  circle((V-max, p-min), radius: 3pt, fill: black, name: "point-c")

  circle((V-min, 0.45 * p), radius: 3pt, fill: black, name: "point-d")

  // Add point labels
  content("point-a", [1], anchor: "south", padding: (bottom: 5pt), name: "label-a")
  content("point-b", [2], anchor: "west", padding: (left: 5pt), name: "label-b")
  content("point-c", [3], anchor: "north-west", padding: (left: 5pt), name: "label-c")
  content("point-d", [4], anchor: "east", padding: (right: 5pt), name: "label-d")

  // Define styles for paths
  let arrow_style = (end: "stealth", fill: black, scale: .5)
  let stroke_style = (paint: rgb("#00008b"), thickness: 1.5pt)

  // Draw cycle paths with arrows and labels
  // a -> b (adiabatic expansion)
  bezier(
    "point-a",
    "point-b",
    (rel: (-5, 1), to: "point-b"),
    stroke: stroke_style,
    mark: arrow_style,
    name: "path-ab",
  )

  // Calculate midpoint for label using relative positioning
  // Create a midpoint reference
  content(
    ((V-min + V-max) / 2, (p-max + 0.5 * p) / 2 - 0.35),
    text(fill: blue.darken(25%), $Delta Q = 0$),
    name: "label-ab",
    anchor: "south",
    padding: (bottom: 5pt),
  )

  // b -> c (heat rejection)
  line("point-b", "point-c", mark: arrow_style, stroke: stroke_style, name: "path-bc")

  content(
    (rel: (0.1, 0), to: "path-bc"),
    text(fill: blue.darken(5%), $arrow.double.r Q_"out"$),
    name: "label-bc",
    anchor: "west",
  )

  // c -> d (adiabatic compression)
  bezier(
    "point-c",
    "point-d",
    (rel: (2.4, -1.3), to: "point-d"),
    stroke: stroke_style,
    mark: arrow_style,
    name: "path-cd",
  )

  content(
    ((V-max + V-min) / 2, (p-min + 0.45 * p) / 2 - 0.35),
    text(fill: blue.darken(15%), $Delta Q = 0$),
    name: "label-cd",
    anchor: "south",
    padding: (bottom: 5pt),
  )

  // d -> a (heat addition)
  line(
    "point-d",
    "point-a",
    mark: arrow_style,
    stroke: stroke_style,
    name: "path-da",
  )

  content(
    (rel: (-0.1, 0), to: "path-da"),
    text(fill: red)[$Q_"in" arrow.double.r$],
    name: "label-da",
    anchor: "east",
  )
})