anaesthesia machine checklist protocol

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.

Please make one with flowcharts

Reading File
Finding Sources
Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Good - found relevant content in Miller's Anesthesia and Barash. Let me read both sections.
Reading File
Reading File
Reading File
Reading File
I now have comprehensive content. Let me read the PPTX skill guide and create the presentation.
Reading File
Running Command

mkdir -p /tmp/workspace/anaesthesia-checklist && ls /tmp/workspace/

Writing File

~/anaesthesia-checklist/build_pptx.py

"""
Anaesthesia Machine Checklist Protocol - PowerPoint with Flowcharts
Based on ASA Recommendations for Pre-Anesthesia Checkout Procedures (2008)
Source: Miller's Anesthesia 10e & Barash Clinical Anesthesia 9e
"""

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE_TYPE
from pptx.oxml.ns import qn
from pptx.oxml import parse_xml
import lxml.etree as etree
from io import BytesIO

# ── Colour Palette ──────────────────────────────────────────────────────────
DARK_BLUE   = RGBColor(0x1A, 0x3A, 0x5C)   # headings / title bg
MED_BLUE    = RGBColor(0x2E, 0x6D, 0xA8)   # section headers
LIGHT_BLUE  = RGBColor(0xD6, 0xE8, 0xF7)   # step boxes bg
TEAL        = RGBColor(0x00, 0x87, 0x8A)   # daily items
GREEN       = RGBColor(0x2A, 0x7A, 0x4B)   # per-case items
AMBER       = RGBColor(0xE8, 0x8B, 0x00)   # warnings / critical
RED         = RGBColor(0xC0, 0x39, 0x2B)   # FAIL / stop
WHITE       = RGBColor(0xFF, 0xFF, 0xFF)
LIGHT_GRAY  = RGBColor(0xF4, 0xF6, 0xF9)
MID_GRAY    = RGBColor(0x88, 0x96, 0xA7)
DARK_GRAY   = RGBColor(0x33, 0x33, 0x33)
YELLOW_BG   = RGBColor(0xFF, 0xF3, 0xCD)
GREEN_BG    = RGBColor(0xD4, 0xED, 0xDA)
RED_BG      = RGBColor(0xF8, 0xD7, 0xDA)

prs = Presentation()
prs.slide_width  = Inches(13.333)
prs.slide_height = Inches(7.5)
blank = prs.slide_layouts[6]

# ─────────────────────────────────────────────────────────────────────────────
# Helper utilities
# ─────────────────────────────────────────────────────────────────────────────

def add_rect(slide, x, y, w, h, fill_rgb, border_rgb=None, border_pt=1.5, radius=None):
    """Add a filled rectangle (optionally with rounded corners)."""
    shape = slide.shapes.add_shape(
        1,  # MSO_SHAPE_TYPE.RECTANGLE
        Inches(x), Inches(y), Inches(w), Inches(h)
    )
    shape.fill.solid()
    shape.fill.fore_color.rgb = fill_rgb
    if border_rgb:
        shape.line.color.rgb = border_rgb
        shape.line.width = Pt(border_pt)
    else:
        shape.line.fill.background()
    return shape


def add_text_box(slide, text, x, y, w, h, font_size=12, bold=False,
                 color=DARK_GRAY, align=PP_ALIGN.LEFT, wrap=True,
                 v_anchor=MSO_ANCHOR.MIDDLE, italic=False):
    tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
    tf = tb.text_frame
    tf.word_wrap = wrap
    tf.vertical_anchor = v_anchor
    tf.margin_left  = Pt(4)
    tf.margin_right = Pt(4)
    tf.margin_top   = Pt(2)
    tf.margin_bottom= Pt(2)
    p = tf.paragraphs[0]
    p.alignment = align
    r = p.add_run()
    r.text = text
    r.font.size   = Pt(font_size)
    r.font.bold   = bold
    r.font.italic = italic
    r.font.color.rgb = color
    r.font.name = "Calibri"
    return tb


def add_label_box(slide, text, x, y, w, h, fill, border=None,
                  font_size=11, bold=False, color=DARK_GRAY,
                  align=PP_ALIGN.CENTER, wrap=True):
    """A combined filled rect + centred text."""
    add_rect(slide, x, y, w, h, fill, border_rgb=border)
    add_text_box(slide, text, x, y, w, h,
                 font_size=font_size, bold=bold, color=color,
                 align=align, wrap=wrap)


def add_arrow_v(slide, x, y_top, length, color=MID_GRAY):
    """Vertical downward arrow."""
    cx = Inches(x)
    cy = Inches(y_top)
    cw = Inches(0.02)
    ch = Inches(length)
    conn = slide.shapes.add_connector(1, cx, cy, cx, cy + ch)  # straight
    conn.line.color.rgb = color
    conn.line.width = Pt(1.5)


def add_slide_header(slide, title, subtitle=None):
    """Standard slide header bar."""
    add_rect(slide, 0, 0, 13.333, 0.9, DARK_BLUE)
    add_text_box(slide, title, 0.3, 0.05, 10, 0.8,
                 font_size=22, bold=True, color=WHITE, align=PP_ALIGN.LEFT)
    if subtitle:
        add_text_box(slide, subtitle, 0.3, 0.6, 10, 0.35,
                     font_size=11, color=RGBColor(0xAA, 0xCC, 0xEE),
                     align=PP_ALIGN.LEFT)


def add_footer(slide, text="Source: Miller's Anesthesia 10e | ASA Pre-Anesthesia Checkout Recommendations 2008"):
    add_rect(slide, 0, 7.2, 13.333, 0.3, RGBColor(0xEC, 0xF0, 0xF5))
    add_text_box(slide, text, 0.3, 7.2, 12.7, 0.3,
                 font_size=8, color=MID_GRAY, align=PP_ALIGN.LEFT,
                 v_anchor=MSO_ANCHOR.MIDDLE)


# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 1 – Title Slide
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)

# Dark gradient background
add_rect(slide, 0, 0, 13.333, 7.5, DARK_BLUE)
add_rect(slide, 0, 4.5, 13.333, 3.0, MED_BLUE)

# Accent bar
add_rect(slide, 0.4, 1.6, 0.08, 3.5, AMBER)

add_text_box(slide, "ANAESTHESIA MACHINE", 0.7, 1.3, 11, 1.0,
             font_size=36, bold=True, color=WHITE, align=PP_ALIGN.LEFT)
add_text_box(slide, "CHECKLIST PROTOCOL", 0.7, 2.15, 11, 1.0,
             font_size=36, bold=True, color=RGBColor(0xAA, 0xD4, 0xF5),
             align=PP_ALIGN.LEFT)
add_text_box(slide, "Pre-Anaesthesia Checkout (PAC) Flowchart Guide", 0.7, 3.2, 11, 0.5,
             font_size=16, color=RGBColor(0xCC, 0xE0, 0xF0), align=PP_ALIGN.LEFT)
add_text_box(slide,
    "Based on: ASA Recommendations for Pre-Anesthesia Checkout Procedures (2008)\n"
    "Miller's Anesthesia 10e  |  Barash Clinical Anesthesia 9e  |  FDA Checkout Guidelines",
    0.7, 3.8, 11, 0.7, font_size=11, color=RGBColor(0x88, 0xAA, 0xCC),
    align=PP_ALIGN.LEFT)

# Bottom legend
boxes = [
    ("DAILY", TEAL,   "Perform once\nbefore first case"),
    ("EACH CASE", GREEN,  "Repeat before\nevery case"),
    ("CRITICAL", AMBER,  "Most safety-\ncritical items"),
]
for i, (lbl, col, desc) in enumerate(boxes):
    bx = 1.0 + i * 3.8
    add_rect(slide, bx, 5.1, 1.4, 0.5, col)
    add_text_box(slide, lbl, bx, 5.1, 1.4, 0.5,
                 font_size=13, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    add_text_box(slide, desc, bx + 1.5, 5.1, 2.1, 0.5,
                 font_size=10, color=WHITE, align=PP_ALIGN.LEFT)


# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 2 – Overview Flowchart (Pre-Anaesthesia Checkout Overview)
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "Pre-Anaesthesia Checkout (PAC) – Overview Flowchart",
                 "Complete workflow from start of day to anaesthesia induction")
add_footer(slide)

# Central flowchart column (left half)
fc_items = [
    ("START OF DAY", DARK_BLUE,    WHITE,  True),
    ("Daily Checks\n(Items 1, 3, 5–10)", TEAL,  WHITE,  True),
    ("Per-Case Checks\n(Items 2, 4, 11–15)", GREEN, WHITE,  True),
    ("ANESTHESIA TIME-OUT\n(Item 15)", AMBER, WHITE,  True),
    ("COMMENCE ANESTHESIA", MED_BLUE, WHITE, True),
]

col_x = 1.0
box_w = 3.6
box_h = 0.65
gap   = 0.22
start_y = 1.1

for i, (lbl, fill, tcol, bold) in enumerate(fc_items):
    y = start_y + i * (box_h + gap)
    add_rect(slide, col_x, y, box_w, box_h, fill, border_rgb=MED_BLUE, border_pt=1)
    add_text_box(slide, lbl, col_x, y, box_w, box_h,
                 font_size=11, bold=bold, color=tcol, align=PP_ALIGN.CENTER)
    if i < len(fc_items) - 1:
        arr_y = y + box_h
        add_rect(slide, col_x + box_w/2 - 0.01, arr_y, 0.02, gap * 0.8, MID_GRAY)

# Decision diamond after each group
def draw_diamond(slide, cx, cy, w, h, fill, border, text, font_size=9, text_color=WHITE):
    """Approximate diamond with a rotated rectangle textbox + border shape."""
    shape = slide.shapes.add_shape(
        4,  # DIAMOND
        Inches(cx - w/2), Inches(cy - h/2), Inches(w), Inches(h)
    )
    shape.fill.solid()
    shape.fill.fore_color.rgb = fill
    shape.line.color.rgb = border
    shape.line.width = Pt(1.5)
    add_text_box(slide, text, cx - w/2, cy - h/2, w, h,
                 font_size=font_size, bold=True, color=text_color,
                 align=PP_ALIGN.CENTER)

# Decision diamond 1: after daily checks
d1_y = start_y + 1*(box_h + gap) + box_h + 0.05
draw_diamond(slide, col_x + box_w/2, d1_y + 0.3, 2.0, 0.55,
             AMBER, DARK_BLUE, "All daily checks\npassed?")
add_text_box(slide, "NO → Investigate & fix\nbefore proceeding",
             col_x + box_w/2 + 1.1, d1_y + 0.1, 3.0, 0.6,
             font_size=9, color=RED, bold=False)

# Decision diamond 2: after per-case checks
d2_y = start_y + 2*(box_h + gap) + box_h + 0.05
draw_diamond(slide, col_x + box_w/2, d2_y + 0.3, 2.0, 0.55,
             AMBER, DARK_BLUE, "Per-case checks\npassed?")
add_text_box(slide, "NO → Do not proceed\nAlert attending",
             col_x + box_w/2 + 1.1, d2_y + 0.1, 3.0, 0.6,
             font_size=9, color=RED, bold=False)

# Right panel – key requirements
add_rect(slide, 5.2, 1.0, 7.7, 5.9, WHITE, border_rgb=MED_BLUE, border_pt=1)
add_rect(slide, 5.2, 1.0, 7.7, 0.45, MED_BLUE)
add_text_box(slide, "7 BASIC REQUIREMENTS FOR SAFE ANESTHESIA DELIVERY  (ASA 2008)",
             5.3, 1.0, 7.5, 0.45, font_size=10, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

reqs = [
    ("1", "Reliable delivery of O₂ at any appropriate concentration up to 100%"),
    ("2", "Reliable means of positive-pressure ventilation"),
    ("3", "Backup ventilation equipment available and functioning"),
    ("4", "Controlled release of positive pressure from breathing circuit"),
    ("5", "Anaesthetic vapour delivery (if part of the plan)"),
    ("6", "Adequate suction"),
    ("7", "Means to conform to standards for patient monitoring"),
]
for i, (num, txt) in enumerate(reqs):
    ry = 1.55 + i * 0.7
    add_rect(slide, 5.4, ry, 0.38, 0.5, MED_BLUE)
    add_text_box(slide, num, 5.4, ry, 0.38, 0.5,
                 font_size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    add_rect(slide, 5.82, ry, 6.85, 0.5, LIGHT_BLUE if i%2==0 else WHITE)
    add_text_box(slide, txt, 5.85, ry, 6.8, 0.5,
                 font_size=10, color=DARK_GRAY, align=PP_ALIGN.LEFT)


# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 3 – Daily Checks Flowchart (Items 1, 3, 5–10)
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "DAILY CHECKS – Flowchart  (Perform Once Before First Case)",
                 "Items 1, 3, 5, 6, 7, 8, 9, 10 of ASA PAC Recommendations")
add_footer(slide)

daily_steps = [
    ("ITEM 1", "Verify Auxiliary O₂\nCylinder & Self-Inflating\nResuscitation Bag (SIRB)",
     "• Full O₂ cylinder with flowmeter & wrench\n• SIRB present & functional\n• Close valve after check",
     TEAL),
    ("ITEM 3", "Turn on Workstation;\nConfirm AC Power",
     "• Power on all machine modules\n• Verify green AC power indicator\n• Check battery backup status",
     TEAL),
    ("ITEM 5", "Spare O₂ Cylinder\nPressure Check",
     "• Cylinder gauge: FULL (≥ 2000 psi)\n• Open briefly to verify flow\n• Close valve after check",
     TEAL),
    ("ITEM 6", "Verify Pipeline\nGas Pressure",
     "• O₂: 50–55 psi\n• N₂O: 50–55 psi\n• Air: 50–55 psi\n• All connections secure (DISS fittings)",
     TEAL),
    ("ITEM 7", "Vaporiser(s)\nCheck",
     "• Vaporisers properly seated / interlocked\n• Adequate agent level in each\n• Check for leaks around filler cap",
     TEAL),
    ("ITEM 8", "Verify CO₂\nAbsorbent",
     "• Check colour – not exhausted\n• Replace if >50% colour-changed\n• Ensure no bypassing of absorber",
     TEAL),
    ("ITEM 9", "Breathing System /\nCircle System Check",
     "• Inspect hoses for damage\n• Verify unidirectional valves move freely\n• Perform low-pressure leak test (LPLT)",
     TEAL),
    ("ITEM 10", "Oxygen Analyser\nCalibration",
     "• Expose sensor to room air → calibrate to 21%\n• Reinstall; observe FiO₂ ≥ 90% on O₂ flush\n• Set low-O₂ alarm (≥ 18%)",
     TEAL),
]

# Two-column layout
for i, (num, title, detail, fill) in enumerate(daily_steps):
    col = i % 2
    row = i // 2
    bx = 0.35 + col * 6.5
    by = 1.05 + row * 1.5
    # Main box
    add_rect(slide, bx, by, 6.15, 1.35, WHITE, border_rgb=TEAL, border_pt=1.5)
    # Number tag
    add_rect(slide, bx, by, 1.0, 1.35, fill)
    add_text_box(slide, num, bx, by, 1.0, 1.35,
                 font_size=10, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    # Title
    add_text_box(slide, title, bx + 1.05, by + 0.02, 2.1, 1.3,
                 font_size=10, bold=True, color=DARK_BLUE, align=PP_ALIGN.LEFT)
    # Detail
    add_rect(slide, bx + 3.2, by, 2.9, 1.35, LIGHT_BLUE)
    add_text_box(slide, detail, bx + 3.25, by + 0.04, 2.8, 1.27,
                 font_size=8.5, color=DARK_GRAY, align=PP_ALIGN.LEFT)

# Arrow connectors between rows
for row in range(3):
    for col in range(2):
        ax = 0.35 + col * 6.5 + 3.075
        ay = 1.05 + row * 1.5 + 1.35
        add_rect(slide, ax, ay, 0.02, 0.15, MID_GRAY)


# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 4 – Per-Case Checks Flowchart (Items 2, 4, 11–15)
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "PER-CASE CHECKS – Flowchart  (Repeat Before EVERY Case)",
                 "Items 2, 4, 11, 12, 13, 14, 15 of ASA PAC Recommendations")
add_footer(slide)

per_case_steps = [
    ("ITEM 2",  "Verify Patient Suction\nIs Adequate",
     "• Suction functional & at bedside\n• Yankauer tip connected\n• Tubing of appropriate length",
     GREEN),
    ("ITEM 4",  "Verify Monitors &\nCheck Alarms",
     "• Pulse oximeter probe functional\n• Capnography waveform present\n• Alarms set to defaults",
     GREEN),
    ("ITEM 11", "Verify Breathing System\nPatency & Function",
     "• Visual inspection of circuit integrity\n• Confirm fresh gas flow connection\n• APL valve functional",
     GREEN),
    ("ITEM 12", "Check Scavenging\nSystem",
     "• Scavenging connected to breathing system\n• Positive/negative pressure relief open\n• Adequate scavenging flow",
     GREEN),
    ("ITEM 13", "Check Airway\nEquipment",
     "• Correct ETT sizes available\n• Laryngoscope blades light-tested\n• LMA / video laryngoscope ready",
     GREEN),
    ("ITEM 14", "Check & Confirm\nDrugs / IV Access",
     "• Emergency drugs drawn / labelled\n• IV access patent and secured\n• Syringe labels double-checked",
     GREEN),
    ("ITEM 15", "ANESTHESIA TIME-OUT\n(Final Pre-Induction Check)",
     "• Monitors functional?\n• Capnogram present?\n• SpO₂ measured?\n• Flowmeter/vent settings correct?\n• Manual/vent switch → MANUAL?\n• Vaporiser adequately filled?",
     AMBER),
]

# Vertical flowchart left column (items 2,4,11,12,13,14) + right box for item 15
small_h = 0.78
gap2    = 0.12
for i, (num, title, detail, fill) in enumerate(per_case_steps[:6]):
    by = 1.05 + i * (small_h + gap2)
    bx = 0.35
    add_rect(slide, bx, by, 0.9, small_h, fill)
    add_text_box(slide, num, bx, by, 0.9, small_h,
                 font_size=9, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    add_rect(slide, bx + 0.95, by, 2.2, small_h, WHITE, border_rgb=GREEN, border_pt=1)
    add_text_box(slide, title, bx + 1.0, by, 2.1, small_h,
                 font_size=9.5, bold=True, color=DARK_BLUE, align=PP_ALIGN.LEFT)
    add_rect(slide, bx + 3.2, by, 3.1, small_h, LIGHT_BLUE)
    add_text_box(slide, detail, bx + 3.25, by, 3.0, small_h,
                 font_size=8, color=DARK_GRAY, align=PP_ALIGN.LEFT)
    # Arrow
    if i < 5:
        add_rect(slide, bx + 0.44, by + small_h, 0.02, gap2, MID_GRAY)

# Item 15 – big highlight box on right
add_rect(slide, 6.8, 1.0, 6.15, 5.8, WHITE, border_rgb=AMBER, border_pt=2.5)
add_rect(slide, 6.8, 1.0, 6.15, 0.5, AMBER)
add_text_box(slide, "⚑  ITEM 15  |  ANAESTHESIA TIME-OUT", 6.9, 1.0, 6.0, 0.5,
             font_size=13, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
add_text_box(slide, "Final Check Immediately Before Induction  (Provider ONLY)",
             6.9, 1.55, 6.0, 0.4,
             font_size=10.5, bold=False, italic=True, color=DARK_BLUE, align=PP_ALIGN.CENTER)

timeout_items = [
    ("✔", "Monitors functional?", WHITE),
    ("✔", "Capnogram present?", WHITE),
    ("✔", "SpO₂ (pulse oximetry) measured?", WHITE),
    ("✔", "Flowmeter & ventilator settings correct?", WHITE),
    ("✔", "Manual/ventilator switch set to MANUAL?", WHITE),
    ("✔", "Vaporiser(s) adequately filled?", WHITE),
]
for j, (sym, text, bg) in enumerate(timeout_items):
    ty = 2.05 + j * 0.65
    add_rect(slide, 6.9, ty, 0.55, 0.55, AMBER if j == 0 else GREEN)
    add_text_box(slide, sym, 6.9, ty, 0.55, 0.55,
                 font_size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    bg_col = LIGHT_BLUE if j % 2 == 0 else WHITE
    add_rect(slide, 7.5, ty, 5.2, 0.55, bg_col, border_rgb=MID_GRAY, border_pt=0.5)
    add_text_box(slide, text, 7.55, ty, 5.1, 0.55,
                 font_size=11, color=DARK_GRAY, align=PP_ALIGN.LEFT)

add_rect(slide, 6.9, 6.0, 5.85, 0.65, GREEN_BG, border_rgb=GREEN, border_pt=1.5)
add_text_box(slide, "✅  ALL SIX ITEMS CONFIRMED  →  PROCEED TO INDUCTION",
             6.95, 6.0, 5.75, 0.65,
             font_size=11, bold=True, color=GREEN, align=PP_ALIGN.CENTER)

# MS MAIDS mnemonic
add_rect(slide, 6.8, 6.75, 6.15, 0.4, RGBColor(0xE8, 0xF4, 0xFD))
add_text_box(slide, "Mnemonic: MS MAIDS  (Machine · Suction · Monitors · Airway · IV · Drugs · Special equipment)",
             6.85, 6.75, 6.1, 0.4,
             font_size=8.5, color=MED_BLUE, align=PP_ALIGN.CENTER, italic=True)


# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 5 – Low-Pressure Leak Test (LPLT) Flowchart
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "Low-Pressure Circuit Leak Test (LPLT) – Step-by-Step Flowchart",
                 "One of the 3 most critical daily checkout steps  |  Barash Clinical Anesthesia 9e, p.2050")
add_footer(slide)

lplt_steps = [
    (TEAL,    "STEP 1",  "Turn off all vaporisers\nand flowmeters"),
    (TEAL,    "STEP 2",  "Turn oxygen flowmeter on\nto minimum flow (100–250 mL/min)"),
    (TEAL,    "STEP 3",  "Occlude the common\ngas outlet (Y-piece or patient port)"),
    (TEAL,    "STEP 4",  "Observe flowmeter bobbin:\nshould fall to zero (or near zero)"),
    (MED_BLUE,"STEP 5",  "Turn each vaporiser on\none at a time; repeat occlude test"),
    (MED_BLUE,"STEP 6",  "Assess results"),
]

box_w2 = 3.0
box_h2 = 0.7
fc_x   = 0.5

for i, (fill, num, text) in enumerate(lplt_steps):
    fx = fc_x
    fy = 1.1 + i * (box_h2 + 0.25)
    if i == 5:  # decision
        draw_diamond(slide, fx + box_w2/2, fy + box_h2/2, box_w2, box_h2 + 0.1,
                     AMBER, DARK_BLUE, text, font_size=10)
    else:
        add_rect(slide, fx, fy, box_w2, box_h2, fill, border_rgb=DARK_BLUE, border_pt=1)
        add_text_box(slide, num, fx, fy, 0.8, box_h2,
                     font_size=9, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
        add_text_box(slide, text, fx + 0.85, fy, box_w2 - 0.9, box_h2,
                     font_size=9.5, color=WHITE, align=PP_ALIGN.LEFT)
    # arrow
    if i < 5:
        add_rect(slide, fx + box_w2/2, fy + box_h2, 0.02, 0.25, MID_GRAY)

# PASS / FAIL branches
pass_x = 3.8
fail_x = 0.5
branch_y = 1.1 + 5 * (box_h2 + 0.25) + box_h2 + 0.15

add_rect(slide, pass_x, branch_y, 2.8, 0.7, GREEN_BG, border_rgb=GREEN, border_pt=1.5)
add_text_box(slide, "✅  PASS\nFlow falls to ≤ 50 mL/min\n→ Proceed with checkout",
             pass_x + 0.05, branch_y, 2.7, 0.7,
             font_size=9, color=GREEN, align=PP_ALIGN.CENTER, bold=True)

add_rect(slide, fail_x - 0.0, branch_y, 2.8, 0.7, RED_BG, border_rgb=RED, border_pt=1.5)
add_text_box(slide, "❌  FAIL\nFlow does not fall → LEAK\n→ Do NOT use machine; notify",
             fail_x, branch_y, 2.75, 0.7,
             font_size=9, color=RED, align=PP_ALIGN.CENTER, bold=True)

# Right side: circle system explanation
add_rect(slide, 7.2, 1.0, 5.8, 5.9, WHITE, border_rgb=MED_BLUE, border_pt=1)
add_rect(slide, 7.2, 1.0, 5.8, 0.5, MED_BLUE)
add_text_box(slide, "CIRCLE BREATHING SYSTEM FUNCTIONAL TEST", 7.3, 1.0, 5.6, 0.5,
             font_size=11, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

circle_steps = [
    ("A", "Set APL (pop-off) valve to\nminimum (OPEN position)"),
    ("B", "Set fresh gas flow (FGF)\nto 5 L/min O₂"),
    ("C", "Occlude Y-piece with thumb;\nsqueeze reservoir bag"),
    ("D", "Pressure gauge should rise;\nAPL valve should open & vent"),
    ("E", "Release Y-piece;\nobserve bag refills freely"),
    ("F", "Perform 'To-and-Fro' test:\ncheck both one-way valves"),
    ("G", "FGF to minimal; observe\nno unintended pressure build-up"),
]
for k, (letter, desc) in enumerate(circle_steps):
    ky = 1.6 + k * 0.67
    add_rect(slide, 7.35, ky, 0.45, 0.58, GREEN)
    add_text_box(slide, letter, 7.35, ky, 0.45, 0.58,
                 font_size=12, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    add_rect(slide, 7.85, ky, 4.95, 0.58, LIGHT_BLUE if k%2==0 else WHITE)
    add_text_box(slide, desc, 7.9, ky, 4.85, 0.58,
                 font_size=9.5, color=DARK_GRAY, align=PP_ALIGN.LEFT)


# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 6 – Full 15-Item Summary Table
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "ASA Pre-Anaesthesia Checkout – All 15 Items Summary",
                 "Miller's Anesthesia 10e, Table 20.6  |  * = Before each case;  D = Daily")
add_footer(slide)

headers = ["#", "Item", "Frequency", "Responsible Party"]
col_widths = [0.5, 6.5, 1.5, 2.7]
col_x_starts = [0.25]
for w in col_widths[:-1]:
    col_x_starts.append(col_x_starts[-1] + w)

# Header row
for j, (hdr, cw, cx) in enumerate(zip(headers, col_widths, col_x_starts)):
    add_rect(slide, cx, 1.0, cw, 0.4, DARK_BLUE)
    add_text_box(slide, hdr, cx, 1.0, cw, 0.4,
                 font_size=10, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

all_items = [
    ("1",  "Verify auxiliary O₂ cylinder & SIRB present",               "Daily",       "Provider + Tech", TEAL),
    ("2",  "Verify patient suction is adequate",                          "Each case",   "Provider + Tech", GREEN),
    ("3",  "Turn on workstation; confirm AC power",                       "Daily",       "Provider or Tech", TEAL),
    ("4",  "Verify availability of monitors & check alarms",              "Each case",   "Provider or Tech", GREEN),
    ("5",  "Verify spare O₂ cylinder pressure is adequate",               "Daily",       "Provider or Tech", TEAL),
    ("6",  "Verify pipeline gas pressures are ≥45 psi (O₂, N₂O, Air)",   "Daily",       "Provider or Tech", TEAL),
    ("7",  "Verify vaporisers: filled, sealed, interlocked",              "Daily",       "Provider or Tech", TEAL),
    ("8",  "Verify CO₂ absorbent is not exhausted",                       "Daily",       "Provider or Tech", TEAL),
    ("9",  "Calibrate O₂ monitor & check low-O₂ alarm",                  "Daily",       "Provider only",   TEAL),
    ("10", "Verify pressure is adequate on breathing system/vaporiser\n   (Low-Pressure Leak Test)", "Daily", "Provider only", TEAL),
    ("11", "Confirm breathing circuit integrity & patency",               "Each case",   "Provider + Tech", GREEN),
    ("12", "Verify scavenging system is functional",                      "Each case",   "Provider or Tech", GREEN),
    ("13", "Check and prepare airway management equipment",               "Each case",   "Provider only",   GREEN),
    ("14", "Check and confirm medications & IV access",                   "Each case",   "Provider only",   GREEN),
    ("15", "Anaesthesia Time-Out (final pre-induction confirmation)",     "Each case\n(immediately before)", "Provider only", AMBER),
]

row_h = 0.36
for r, (num, item, freq, party, color) in enumerate(all_items):
    ry = 1.4 + r * row_h
    bg = LIGHT_BLUE if r % 2 == 0 else WHITE
    for j, (cw, cx) in enumerate(zip(col_widths, col_x_starts)):
        add_rect(slide, cx, ry, cw, row_h, bg)
    # Number colored
    add_rect(slide, col_x_starts[0], ry, col_widths[0], row_h, color)
    add_text_box(slide, num, col_x_starts[0], ry, col_widths[0], row_h,
                 font_size=9, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
    add_text_box(slide, item, col_x_starts[1], ry, col_widths[1], row_h,
                 font_size=8.5, color=DARK_GRAY, align=PP_ALIGN.LEFT)
    add_text_box(slide, freq, col_x_starts[2], ry, col_widths[2], row_h,
                 font_size=8.5, color=TEAL if "Daily" in freq else GREEN, bold=True,
                 align=PP_ALIGN.CENTER)
    add_text_box(slide, party, col_x_starts[3], ry, col_widths[3], row_h,
                 font_size=8, color=DARK_GRAY, align=PP_ALIGN.CENTER)


# ═════════════════════════════════════════════════════════════════════════════
# SLIDE 7 – Troubleshooting Flowchart
# ═════════════════════════════════════════════════════════════════════════════
slide = prs.slides.add_slide(blank)
add_rect(slide, 0, 0, 13.333, 7.5, LIGHT_GRAY)
add_slide_header(slide, "Intraoperative Anaesthesia Machine Failure – Troubleshooting Flowchart",
                 "General approach when equipment failure is suspected during anaesthesia")
add_footer(slide)

# Problem box at top
add_rect(slide, 3.5, 1.1, 6.3, 0.65, RED, border_rgb=DARK_BLUE, border_pt=2)
add_text_box(slide, "⚠  SUSPECTED ANAESTHESIA MACHINE FAILURE", 3.55, 1.1, 6.2, 0.65,
             font_size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

# Arrow down
add_rect(slide, 6.65, 1.75, 0.02, 0.3, MID_GRAY)

# Immediate action
add_rect(slide, 3.5, 2.05, 6.3, 0.65, AMBER, border_rgb=DARK_BLUE, border_pt=1.5)
add_text_box(slide, "IMMEDIATE ACTION:\nDISCONNECT patient from machine  |  Switch to SIRB (self-inflating bag) + O₂",
             3.55, 2.05, 6.2, 0.65,
             font_size=11, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

# Arrow down
add_rect(slide, 6.65, 2.70, 0.02, 0.28, MID_GRAY)

# Stabilise patient
add_rect(slide, 3.5, 2.98, 6.3, 0.6, MED_BLUE)
add_text_box(slide, "Stabilise Patient:\nManual ventilation  |  Maintain O₂  |  Call for help",
             3.55, 2.98, 6.2, 0.6,
             font_size=11, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

# Arrow down
add_rect(slide, 6.65, 3.58, 0.02, 0.28, MID_GRAY)

# Decision
draw_diamond(slide, 6.667, 4.15, 4.5, 0.7,
             AMBER, DARK_BLUE, "Can failure be\nidentified & corrected\nquickly?", font_size=10)

# YES branch
add_rect(slide, 8.5, 4.55, 0.02, 0.5, MID_GRAY)
add_rect(slide, 8.5, 5.1, 3.6, 0.65, GREEN_BG, border_rgb=GREEN, border_pt=1.5)
add_text_box(slide, "YES → Correct fault\n(e.g., reconnect circuit, replace tubing)\nResume machine ventilation",
             8.55, 5.1, 3.5, 0.65, font_size=9, color=GREEN, align=PP_ALIGN.LEFT, bold=True)

# NO branch
add_rect(slide, 4.8, 4.55, 0.02, 0.5, MID_GRAY)
add_rect(slide, 3.2, 5.1, 3.6, 0.65, RED_BG, border_rgb=RED, border_pt=1.5)
add_text_box(slide, "NO → Do NOT return to machine\nUse alternative ventilation\nNotify senior & biomedical eng.",
             3.25, 5.1, 3.5, 0.65, font_size=9, color=RED, align=PP_ALIGN.LEFT, bold=True)

# Key reminder
add_rect(slide, 2.0, 6.1, 9.3, 0.75, YELLOW_BG, border_rgb=AMBER, border_pt=2)
add_text_box(slide,
    "KEY PRINCIPLE (Miller's Anesthesia 10e): "
    '"When in doubt, ventilate and oxygenate the patient first via another method and troubleshoot later."',
    2.1, 6.1, 9.1, 0.75, font_size=10.5, italic=True, color=DARK_BLUE,
    align=PP_ALIGN.CENTER)


# ═════════════════════════════════════════════════════════════════════════════
# SAVE
# ═════════════════════════════════════════════════════════════════════════════
out_path = "/tmp/workspace/anaesthesia-checklist/Anaesthesia_Machine_Checklist_Protocol.pptx"
buf = BytesIO()
prs.save(buf)
with open(out_path, "wb") as f:
    f.write(buf.getvalue())

print(f"Saved: {out_path}")
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_pptx.py 2>&1

Running Command

python -c " from pptx import Presentation prs = Presentation('/tmp/workspace/anaesthesia-checklist/Anaesthesia_Machine_Checklist_Protocol.pptx') print(f'Slides: {len(prs.slides)}') for i, slide in enumerate(prs.slides, 1): texts = [s.text_frame.text[:60] for s in slide.shapes if s.has_text_frame and s.text_frame.text.strip()] print(f' Slide {i}: {texts[0] if texts else \"(no text)\"}') import os; print(f'File size: {os.path.getsize(\"/tmp/workspace/anaesthesia-checklist/Anaesthesia_Machine_Checklist_Protocol.pptx\"):,} bytes') "

All 7 slides built and verified successfully.Your Anaesthesia Machine Checklist Protocol presentation is ready:

Anaesthesia Machine Checklist Protocol

Presentation · PPTX


What's Inside (7 Slides)

SlideContent
1 - TitleCover slide with colour legend (Daily / Each Case / Critical)
2 - Overview FlowchartTop-level PAC flow: Start of Day → Daily Checks → Per-Case Checks → Time-Out → Induction, with decision diamonds and the 7 ASA Basic Requirements
3 - Daily ChecksItems 1, 3, 5–10 in a two-column card layout with step details (O₂ cylinder, AC power, pipeline pressures, vaporisers, CO₂ absorbent, circle system, O₂ analyser)
4 - Per-Case ChecksItems 2, 4, 11–15 in a vertical flowchart; Item 15 (Anaesthesia Time-Out) highlighted in a large amber panel with all 6 confirmation points and the MS MAIDS mnemonic
5 - LPLT FlowchartStep-by-step Low-Pressure Leak Test with PASS/FAIL branches + Circle System Functional Test (steps A–G)
6 - Summary TableAll 15 ASA items in one colour-coded table (frequency + responsible party)
7 - TroubleshootingIntraoperative failure response flowchart: Failure detected → Disconnect → SIRB → Stabilise → Decision diamond → Yes/No branches, with the key Miller's principle quoted

Sources: Miller's Anesthesia 10e (Table 20.6, pp. 2401–2419) | Barash Clinical Anesthesia 9e (pp. 2049–2051) | ASA Recommendations for Pre-Anesthesia Checkout Procedures (2008) | FDA Anesthesia Apparatus Checkout Recommendations (1993)

Oxygen failure protection device 5 marks short note pdf for MD anaesthesia with key points summarised

The Oxygen Failure Protection Device (OFPD) is a safety mechanism on anaesthesia machines that cuts off or reduces nitrous oxide (and other gases) flow when oxygen supply pressure falls below a threshold (~25–30 psi), preventing delivery of a hypoxic mixture to the patient.
Searching sources...
Finding Sources
Finding Sources
Reading File
Good - found content in both Miller's and Morgan & Mikhail. Let me read both sections simultaneously.
Reading File
Reading File
Excellent content from both textbooks. Now let me also search for Barash content and proportioning system details, then build the PDF.
Finding Sources
Finding Sources
Reading File
Reading File
Excellent - I now have very comprehensive content. Let me also grab the diagram image URL and build the PDF.
Writing File

~/anaesthesia-checklist/build_ofpd_pdf.py

"""
Oxygen Failure Protection Device (OFPD)
5-Mark Short Note PDF for MD Anaesthesia
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon, Circle
from reportlab.graphics import renderPDF
from reportlab.platypus.flowables import Flowable
from reportlab.graphics.shapes import Group, Path
import io, os

# ─── Colour palette ───────────────────────────────────────────────────────────
DARK_BLUE   = colors.HexColor('#1A3A5C')
MED_BLUE    = colors.HexColor('#2E6DA8')
LIGHT_BLUE  = colors.HexColor('#D6E8F7')
TEAL        = colors.HexColor('#00878A')
GREEN       = colors.HexColor('#2A7A4B')
GREEN_BG    = colors.HexColor('#D4EDDA')
AMBER       = colors.HexColor('#E88B00')
AMBER_BG    = colors.HexColor('#FFF3CD')
RED         = colors.HexColor('#C0392B')
RED_BG      = colors.HexColor('#F8D7DA')
LIGHT_GRAY  = colors.HexColor('#F4F6F9')
MID_GRAY    = colors.HexColor('#88A0B4')
DARK_GRAY   = colors.HexColor('#333333')
WHITE       = colors.white

PAGE_W, PAGE_H = A4
MARGIN = 1.8 * cm

# ─── Document setup ──────────────────────────────────────────────────────────
out_path = "/tmp/workspace/anaesthesia-checklist/OFPD_MD_Anaesthesia_Short_Note.pdf"
doc = SimpleDocTemplate(
    out_path,
    pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=1.2*cm, bottomMargin=1.5*cm,
    title="Oxygen Failure Protection Device – MD Anaesthesia Short Note",
)

# ─── Styles ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()

def S(name, **kw):
    return ParagraphStyle(name, parent=styles['Normal'], **kw)

title_style = S('title', fontSize=17, textColor=WHITE, alignment=TA_CENTER,
                fontName='Helvetica-Bold', leading=22, spaceAfter=0)
subtitle_style = S('subtitle', fontSize=9.5, textColor=colors.HexColor('#AACCEE'),
                   alignment=TA_CENTER, fontName='Helvetica', leading=12)
section_hdr = S('sectionhdr', fontSize=11.5, textColor=WHITE,
                fontName='Helvetica-Bold', leading=14, alignment=TA_LEFT)
body = S('body', fontSize=9.5, textColor=DARK_GRAY, leading=14, alignment=TA_JUSTIFY,
         fontName='Helvetica', spaceAfter=3)
body_bold = S('bodybold', fontSize=9.5, textColor=DARK_GRAY, leading=14,
              fontName='Helvetica-Bold', spaceAfter=2)
bullet_style = S('bullet', fontSize=9.5, textColor=DARK_GRAY, leading=14,
                 fontName='Helvetica', leftIndent=12, spaceAfter=2,
                 bulletIndent=0, firstLineIndent=0)
key_point = S('kp', fontSize=9.5, textColor=DARK_GRAY, leading=14,
              fontName='Helvetica', leftIndent=16, spaceAfter=2)
small_gray = S('smgray', fontSize=8, textColor=MID_GRAY, alignment=TA_CENTER,
               fontName='Helvetica-Oblique', leading=10)
ref_style = S('ref', fontSize=8, textColor=MID_GRAY, leading=11, alignment=TA_LEFT,
              fontName='Helvetica-Oblique')

# ─── Custom Flowables ─────────────────────────────────────────────────────────

class ColorBox(Flowable):
    """Full-width coloured header box."""
    def __init__(self, text, sub=None, bg=DARK_BLUE, h=1.55*cm):
        self.text = text
        self.sub  = sub
        self.bg   = bg
        self.h    = h
        self.width = PAGE_W - 2*MARGIN
        Flowable.__init__(self)

    def wrap(self, *args):
        return (self.width, self.h)

    def draw(self):
        c = self.canv
        c.setFillColor(self.bg)
        c.rect(0, 0, self.width, self.h, fill=1, stroke=0)
        # accent bar
        c.setFillColor(AMBER)
        c.rect(0, 0, 4, self.h, fill=1, stroke=0)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 14)
        c.drawString(12, self.h - 22, self.text)
        if self.sub:
            c.setFillColor(colors.HexColor('#AACCEE'))
            c.setFont('Helvetica-Oblique', 8.5)
            c.drawString(12, 5, self.sub)


class SectionHeader(Flowable):
    """Coloured section divider."""
    def __init__(self, text, bg=MED_BLUE, h=0.55*cm):
        self.text = text
        self.bg   = bg
        self.h    = h
        self.width = PAGE_W - 2*MARGIN
        Flowable.__init__(self)

    def wrap(self, *args):
        return (self.width, self.h)

    def draw(self):
        c = self.canv
        c.setFillColor(self.bg)
        c.rect(0, 0, self.width, self.h, fill=1, stroke=0)
        c.setFillColor(AMBER)
        c.rect(0, 0, 3.5, self.h, fill=1, stroke=0)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 10)
        c.drawString(10, (self.h - 10)/2 + 1, self.text)


class KeyPointBox(Flowable):
    """Amber-bordered key-point highlight box."""
    def __init__(self, items, w=None, h=None):
        self.items = items
        self._w = w
        self._h = h
        Flowable.__init__(self)

    def wrap(self, availW, availH):
        self._w = availW
        line_h = 15
        self._h = 8 + len(self.items) * line_h + 8
        return (self._w, self._h)

    def draw(self):
        c = self.canv
        c.setFillColor(AMBER_BG)
        c.setStrokeColor(AMBER)
        c.setLineWidth(1.5)
        c.roundRect(0, 0, self._w, self._h, 4, fill=1, stroke=1)
        c.setFillColor(AMBER)
        c.rect(0, 0, 4, self._h, fill=1, stroke=0)
        c.setFillColor(DARK_GRAY)
        c.setFont('Helvetica', 9)
        for i, item in enumerate(reversed(self.items)):
            y = 8 + i * 15
            c.setFont('Helvetica-Bold', 9)
            c.setFillColor(AMBER)
            c.drawString(10, y, '★')
            c.setFont('Helvetica', 9)
            c.setFillColor(DARK_GRAY)
            c.drawString(22, y, item)


class FlowchartOFPD(Flowable):
    """Simple OFPD mechanism flowchart."""
    def __init__(self, w=None):
        self._fw = w
        Flowable.__init__(self)

    def wrap(self, availW, availH):
        self._fw = availW
        self._fh = 7.5 * cm
        return (self._fw, self._fh)

    def _box(self, c, x, y, w, h, fill, text, font_size=8.5, text_color=WHITE, bold=False):
        c.setFillColor(fill)
        c.setStrokeColor(colors.HexColor('#AAAAAA'))
        c.setLineWidth(0.5)
        c.roundRect(x, y, w, h, 3, fill=1, stroke=1)
        c.setFillColor(text_color)
        fn = 'Helvetica-Bold' if bold else 'Helvetica'
        c.setFont(fn, font_size)
        lines = text.split('\n')
        lh = font_size + 2
        total = len(lines) * lh
        start_y = y + (h - total)/2 + lh - 2
        for ln in lines:
            tw = c.stringWidth(ln, fn, font_size)
            c.drawString(x + (w - tw)/2, start_y, ln)
            start_y -= lh

    def _arrow(self, c, x1, y1, x2, y2):
        c.setStrokeColor(MID_GRAY)
        c.setLineWidth(1.2)
        c.line(x1, y1, x2, y2)
        # arrowhead
        c.setFillColor(MID_GRAY)
        if y2 < y1:   # going down
            c.polygon([x2-4, y2+6, x2+4, y2+6, x2, y2], fill=1, stroke=0)
        elif x2 > x1: # going right
            c.polygon([x2-6, y2-4, x2-6, y2+4, x2, y2], fill=1, stroke=0)

    def _diamond(self, c, cx, cy, w, h, fill, text, font_size=8):
        pts = [cx, cy+h/2, cx+w/2, cy, cx, cy-h/2, cx-w/2, cy]
        c.setFillColor(fill)
        c.setStrokeColor(DARK_BLUE)
        c.setLineWidth(1)
        c.polygon(pts, fill=1, stroke=1)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', font_size)
        lines = text.split('\n')
        lh = font_size + 2
        start_y = cy + (len(lines)-1)*lh/2 - 1
        for ln in lines:
            tw = c.stringWidth(ln, 'Helvetica-Bold', font_size)
            c.drawString(cx - tw/2, start_y, ln)
            start_y -= lh

    def draw(self):
        c = self.canv
        fw = self._fw
        fh = self._fh

        # Background
        c.setFillColor(LIGHT_GRAY)
        c.rect(0, 0, fw, fh, fill=1, stroke=0)

        # Title strip
        c.setFillColor(DARK_BLUE)
        c.rect(0, fh - 0.6*cm, fw, 0.6*cm, fill=1, stroke=0)
        c.setFillColor(WHITE)
        c.setFont('Helvetica-Bold', 9)
        title_txt = "OFPD / Fail-Safe Valve – Mechanism Flowchart"
        c.drawString((fw - c.stringWidth(title_txt, 'Helvetica-Bold', 9))/2,
                     fh - 0.42*cm, title_txt)

        # Node dimensions
        bw = fw * 0.28
        bh = 0.55 * cm
        col1_x = 0.3 * cm
        col2_x = fw/2 - bw/2
        col3_x = fw - bw - 0.3*cm

        # ── Left column: O₂ OK path ──────────────────────
        y_start = fh - 1.3*cm

        self._box(c, col2_x, y_start, bw, bh, DARK_BLUE,
                  'O₂ Pipeline\nSupply (50–55 psi)', font_size=7.5, bold=True)

        # Arrow down
        self._arrow(c, col2_x + bw/2, y_start, col2_x + bw/2, y_start - 0.4*cm)

        # Intermediate pressure
        self._box(c, col2_x, y_start - 0.4*cm - bh, bw, bh, MED_BLUE,
                  'Intermediate-Pressure\nSection (14–55 psi)', font_size=7.5)
        self._arrow(c, col2_x + bw/2, y_start - 0.4*cm - bh,
                    col2_x + bw/2, y_start - 0.8*cm - 2*bh)

        # Piloting pressure line
        pilot_y = y_start - 0.8*cm - 2*bh - bh/2
        c.setStrokeColor(TEAL)
        c.setLineWidth(1)
        c.setDash([3, 3])
        c.line(col2_x + bw/2, pilot_y, col2_x + bw + 0.2*cm, pilot_y)
        c.setDash([])
        c.setFillColor(TEAL)
        c.setFont('Helvetica-Oblique', 7)
        c.drawString(col2_x + bw + 0.25*cm, pilot_y - 3, 'Piloting\npressure')

        # Decision diamond
        d_cx = col2_x + bw/2
        d_cy = y_start - 1.15*cm - 2.5*bh
        self._diamond(c, d_cx, d_cy, bw + 0.3*cm, bh + 0.25*cm,
                      AMBER, 'O₂ pressure\n≥ threshold?', font_size=8)

        # YES branch (right) → valve open
        yes_y = d_cy
        yes_x = col3_x
        c.setStrokeColor(MID_GRAY)
        c.setLineWidth(1.2)
        c.line(d_cx + (bw + 0.3*cm)/2, d_cy, yes_x, d_cy)
        self._arrow(c, yes_x, d_cy, yes_x, d_cy - 0.35*cm)
        c.setFillColor(GREEN)
        c.setFont('Helvetica-Bold', 7.5)
        c.drawString(d_cx + (bw + 0.3*cm)/2 + 2, d_cy + 3, 'YES (≥20–30 psi)')
        self._box(c, yes_x, d_cy - 0.35*cm - bh, bw, bh, GREEN,
                  'Valve OPEN\nN₂O flows freely', font_size=7.5, bold=True)
        self._arrow(c, yes_x + bw/2, d_cy - 0.35*cm - bh,
                    yes_x + bw/2, d_cy - 0.7*cm - 2*bh)
        self._box(c, yes_x, d_cy - 0.7*cm - 2*bh, bw, bh,
                  colors.HexColor('#2A7A4B'),
                  'Normal gas mix\ndelivered', font_size=7.5)

        # NO branch (left) → valve closed
        no_x = col1_x
        c.setStrokeColor(MID_GRAY)
        c.setLineWidth(1.2)
        c.line(d_cx - (bw + 0.3*cm)/2, d_cy, no_x + bw, d_cy)
        self._arrow(c, no_x + bw, d_cy, no_x + bw, d_cy - 0.35*cm)
        c.setFillColor(RED)
        c.setFont('Helvetica-Bold', 7.5)
        c.drawString(no_x, d_cy + 3, 'NO (< threshold)')
        self._box(c, no_x, d_cy - 0.35*cm - bh, bw, bh, RED,
                  'Valve CLOSED\nN₂O cut off', font_size=7.5, bold=True)
        self._arrow(c, no_x + bw/2, d_cy - 0.35*cm - bh,
                    no_x + bw/2, d_cy - 0.7*cm - 2*bh)
        self._box(c, no_x, d_cy - 0.7*cm - 2*bh, bw, bh,
                  colors.HexColor('#8B0000'),
                  'Alarm sounds\nO₂ only flows', font_size=7.5, bold=False, text_color=WHITE)

        # Down arrow from diamond
        self._arrow(c, d_cx, d_cy - (bh + 0.25*cm)/2,
                    d_cx, d_cy - (bh + 0.25*cm)/2 - 0.3*cm)

        # Warning note
        note_y = 0.08 * cm
        c.setFillColor(RED_BG)
        c.setStrokeColor(RED)
        c.setLineWidth(0.8)
        c.roundRect(0.3*cm, note_y, fw - 0.6*cm, 0.55*cm, 3, fill=1, stroke=1)
        c.setFillColor(RED)
        c.setFont('Helvetica-Bold', 8)
        warn = '⚠ Limitation: Does NOT protect if O₂ pipeline is contaminated/crossed with another gas'
        c.drawString((fw - c.stringWidth(warn, 'Helvetica-Bold', 8))/2,
                     note_y + 5, warn)


class ComparisonTable(Flowable):
    """Binary vs Proportional device comparison."""
    def __init__(self, w=None):
        self._fw = w
        Flowable.__init__(self)

    def wrap(self, availW, availH):
        self._fw = availW
        self._fh = 4.8 * cm
        return (self._fw, self._fh)

    def draw(self):
        c = self.canv
        fw = self._fw
        rows = [
            ('Feature', 'Binary (Threshold)\nShutoff Valve', 'Proportional Valve\n(OFPD / Balance Regulator)'),
            ('Also called', '"Fail-safe valve"\n"Nitrous cut-off"',
             'Oxygen Failure Protection Device\n(OFPD), Balance regulator'),
            ('Mechanism', 'Open or closed (all-or-nothing)\nbased on O₂ pressure threshold',
             'Proportionally reduces N₂O\nas O₂ pressure falls'),
            ('O₂ threshold', '20–30 psig (e.g. GE machines)',
             'Completely shuts N₂O at 0.5 psig\n(other gases at 10 psig)'),
            ('Used on', 'Older GE/Datex-Ohmeda machines',
             'Modern machines – ISO standard'),
            ('Limitation', 'All gases off if O₂ drops (sudden)',
             'Does NOT prevent hypoxia if\npipeline crossover/contamination'),
        ]
        col_w = [fw * 0.22, fw * 0.38, fw * 0.38]
        row_h = self._fh / len(rows)
        y_top = self._fh

        for r, row in enumerate(rows):
            y = y_top - (r + 1) * row_h
            for ci, (cell, cw) in enumerate(zip(row, col_w)):
                x = sum(col_w[:ci])
                # Background
                if r == 0:
                    bg = DARK_BLUE
                elif ci == 0:
                    bg = LIGHT_BLUE
                elif ci == 1:
                    bg = colors.HexColor('#FFF3CD') if r % 2 == 0 else WHITE
                else:
                    bg = LIGHT_BLUE if r % 2 == 0 else colors.HexColor('#E8F4FD')
                c.setFillColor(bg)
                c.setStrokeColor(MID_GRAY)
                c.setLineWidth(0.5)
                c.rect(x, y, cw, row_h, fill=1, stroke=1)
                # Text
                fc = WHITE if r == 0 else DARK_GRAY
                fn = 'Helvetica-Bold' if r == 0 or ci == 0 else 'Helvetica'
                fs = 7.5
                c.setFillColor(fc)
                c.setFont(fn, fs)
                lines = cell.split('\n')
                lh = fs + 1.5
                start_y = y + row_h/2 + (len(lines)-1)*lh/2
                for ln in lines:
                    tw = c.stringWidth(ln, fn, fs)
                    c.drawString(x + (cw - tw)/2, start_y, ln)
                    start_y -= lh


# ─── Build content ────────────────────────────────────────────────────────────

def bullet(text, indent=12):
    return Paragraph(f'<bullet>&bull;</bullet> {text}', bullet_style)

def sub_bullet(text):
    s = S('subb', parent=bullet_style, leftIndent=24, fontSize=9)
    return Paragraph(f'  – {text}', s)

story = []

# ── Title block ──────────────────────────────────────────────────────────────
story.append(ColorBox(
    "OXYGEN FAILURE PROTECTION DEVICE (OFPD)",
    sub="MD Anaesthesia  |  5-Mark Short Note  |  Anaesthesia Machine Safety",
    bg=DARK_BLUE, h=1.6*cm
))
story.append(Spacer(1, 4*mm))

# ── Introduction ─────────────────────────────────────────────────────────────
story.append(SectionHeader("1.  DEFINITION & INTRODUCTION", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "The <b>Oxygen Failure Protection Device (OFPD)</b>, also known as the <b>fail-safe valve</b>, "
    "<b>pressure-sensor shutoff valve</b>, or <b>balance regulator</b>, is a safety mechanism "
    "incorporated in the <b>intermediate-pressure section</b> of the anaesthesia workstation. "
    "Its primary function is to <b>reduce or cut off the supply of nitrous oxide</b> (and other "
    "gases except oxygen) when the oxygen supply pressure falls below a critical threshold, "
    "thereby minimising the risk of delivering a <b>hypoxic gas mixture</b> to the patient. "
    "It is an <b>ISO standard</b> on all modern anaesthesia machines.",
    body))
story.append(Spacer(1, 3*mm))

# ── Location ─────────────────────────────────────────────────────────────────
story.append(SectionHeader("2.  LOCATION IN THE ANAESTHESIA MACHINE", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "The OFPD is situated in the <b>intermediate-pressure section</b> of the anaesthesia machine "
    "(pipeline pressure reduced to 45–55 psi). One device is present in the gas line supplying "
    "<b>each flowmeter except oxygen</b>. It receives a <b>'piloting pressure' signal</b> derived "
    "from the oxygen supply line (or secondary regulator).",
    body))
story.append(Spacer(1, 2*mm))
table_loc = Table(
    [
        [Paragraph('<b>Section</b>', body_bold),
         Paragraph('<b>Pressure Range</b>', body_bold),
         Paragraph('<b>Key Components</b>', body_bold)],
        ['High-pressure', '1900–2200 psi (cylinder)', 'Cylinder valves, reducing valves'],
        ['Intermediate-pressure', '45–55 psi (pipeline)', 'OFPD, flush valve, proportioning system'],
        ['Low-pressure', '≤ 16 psi (flow control)', 'Flowmeters, vaporisers, common gas outlet'],
    ],
    colWidths=[(PAGE_W - 2*MARGIN) * x for x in [0.22, 0.25, 0.53]],
)
table_loc.setStyle(TableStyle([
    ('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE),
    ('TEXTCOLOR',  (0, 0), (-1, 0), WHITE),
    ('FONTNAME',   (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',   (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',   (0, 0), (-1, -1), 8.5),
    ('BACKGROUND', (0, 2), (-1, 2), LIGHT_BLUE),
    ('ROWBACKGROUNDS', (0, 1), (-1, -1), [WHITE, LIGHT_BLUE, WHITE]),
    ('ROWHEIGHT',  (0, 0), (-1, -1), 0.55*cm),
    ('VALIGN',     (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',      (0, 0), (-1, -1), 'CENTER'),
    ('GRID',       (0, 0), (-1, -1), 0.5, MID_GRAY),
    ('LEFTPADDING',(0, 0), (-1, -1), 5),
]))
story.append(table_loc)
story.append(Spacer(1, 3*mm))

# ── Types ─────────────────────────────────────────────────────────────────────
story.append(SectionHeader("3.  TYPES OF OFPD", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>A. Binary (Threshold) Shutoff Valve</b>", body_bold))
story.append(bullet("Acts in an <b>all-or-nothing</b> fashion based on a threshold O₂ pressure"))
story.append(bullet("Examples: GE/Datex-Ohmeda <i>pressure-sensor shutoff valve</i>"))
story.append(bullet("Threshold: <b>20–30 psig</b>; valve opens when O₂ pressure exceeds threshold"))
story.append(bullet("Mechanism: oxygen pressure acts on a mobile diaphragm → moves piston "
                    "upward → pin opens the N₂O valve; spring closes it when O₂ pressure is low"))
story.append(bullet("Limitation: <b>abrupt</b> cutoff; N₂O completely stops if O₂ drops even transiently"))
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>B. Proportional Valve (Balance Regulator / OFPD)</b>", body_bold))
story.append(bullet("Modern design – <b>proportionately reduces</b> N₂O pressure as O₂ pressure falls"))
story.append(bullet("Completely shuts off N₂O only below a <b>minimum O₂ pressure</b> (e.g. <b>0.5 psig</b> "
                    "for N₂O; <b>10 psig</b> for other gases)"))
story.append(bullet("ISO standard on all contemporary workstations"))
story.append(bullet("Air flow may bypass the OFPD on some machines, allowing air administration "
                    "even with O₂ failure"))
story.append(Spacer(1, 3*mm))

# ── Flowchart ─────────────────────────────────────────────────────────────────
story.append(SectionHeader("4.  MECHANISM FLOWCHART", bg=TEAL))
story.append(Spacer(1, 2*mm))
story.append(FlowchartOFPD())
story.append(Spacer(1, 3*mm))

# ── Comparison table ──────────────────────────────────────────────────────────
story.append(SectionHeader("5.  COMPARISON: BINARY vs PROPORTIONAL DEVICE", bg=TEAL))
story.append(Spacer(1, 2*mm))
story.append(ComparisonTable())
story.append(Spacer(1, 3*mm))

# ── Proportioning systems ─────────────────────────────────────────────────────
story.append(SectionHeader("6.  RELATIONSHIP WITH PROPORTIONING SYSTEMS", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "The OFPD works in conjunction with — but is <b>distinct from</b> — the proportioning system. "
    "While the OFPD acts upstream (at the piloting pressure level), proportioning systems control "
    "the N₂O:O₂ <b>flow ratio</b> at the flow control valve level to prevent a hypoxic mixture.",
    body))
story.append(Spacer(1, 2*mm))

prop_data = [
    [Paragraph('<b>Device</b>', body_bold), Paragraph('<b>Mechanism</b>', body_bold),
     Paragraph('<b>Minimum FiO₂ ensured</b>', body_bold)],
    ['Dräger S-ORC\n(Sensitive O₂ Ratio Controller)',
     'Pneumatic-mechanical; diaphragm-driven N₂O\nproportioning valve; needs ≥ 200 mL/min O₂',
     '≥ 25% O₂\n(max 75% N₂O)'],
    ['GE/Datex-Ohmeda\nLink-25 System',
     'Mechanical chain-link coupling of O₂\nand N₂O flow knobs',
     '≥ 25% O₂\n(3:1 max N₂O:O₂ ratio)'],
]
prop_table = Table(prop_data,
    colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.28, 0.44, 0.28]])
prop_table.setStyle(TableStyle([
    ('BACKGROUND',  (0, 0), (-1, 0), DARK_BLUE),
    ('TEXTCOLOR',   (0, 0), (-1, 0), WHITE),
    ('FONTNAME',    (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',    (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',    (0, 0), (-1, -1), 8.5),
    ('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, WHITE]),
    ('ROWHEIGHT',   (0, 0), (-1, -1), 0.6*cm),
    ('VALIGN',      (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',       (0, 0), (-1, -1), 'CENTER'),
    ('GRID',        (0, 0), (-1, -1), 0.5, MID_GRAY),
    ('LEFTPADDING', (0, 0), (-1, -1), 5),
]))
story.append(prop_table)
story.append(Spacer(1, 3*mm))

# ── Limitations ───────────────────────────────────────────────────────────────
story.append(SectionHeader("7.  LIMITATIONS – WHY 'FAIL-SAFE' IS A MISNOMER", bg=RED))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "The term <b>'fail-safe'</b> is widely regarded as a <b>misnomer</b> because the OFPD "
    "<i>only</i> responds to <b>oxygen pressure</b>, not to oxygen <b>concentration</b>. "
    "The following situations can cause a hypoxic mixture <b>despite the OFPD being open</b>:",
    body))
story.append(Spacer(1, 2*mm))

lim_data = [
    ['Situation', 'Why OFPD Fails to Protect'],
    ['Pipeline gas crossover / contamination\n(N₂O in O₂ pipeline)',
     'O₂ pipeline pressure is maintained by N₂O → OFPD stays OPEN\nO₂ analyser is the only safeguard'],
    ['Hypoxic gas mixture at adequate pressure\n(e.g. <21% O₂ but pressure ≥ threshold)',
     'OFPD cannot distinguish O₂ from any other gas at the\npiloting line — it only senses pressure'],
    ['Air bypass (some machines)',
     'Air can be delivered even with O₂ failure if its\nline bypasses the OFPD'],
    ['Downstream leaks (low-pressure circuit)',
     'OFPD is upstream; leaks after flowmeters are\nnot detected by this device'],
    ['Flowmeter bobbin float leak\n(cracked or stuck float)',
     'N₂O may continue to flow into the common outlet\neven if OFPD is closed (gas escapes leak)'],
]
lim_table = Table(lim_data,
    colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.42, 0.58]])
lim_table.setStyle(TableStyle([
    ('BACKGROUND',  (0, 0), (-1, 0), RED),
    ('TEXTCOLOR',   (0, 0), (-1, 0), WHITE),
    ('FONTNAME',    (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',    (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',    (0, 0), (-1, -1), 8.5),
    ('ROWBACKGROUNDS', (0, 1), (-1, -1), [RED_BG, WHITE]),
    ('ROWHEIGHT',   (0, 0), (-1, -1), 0.65*cm),
    ('VALIGN',      (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',       (0, 0), (0, -1), 'LEFT'),
    ('ALIGN',       (1, 0), (1, -1), 'LEFT'),
    ('GRID',        (0, 0), (-1, -1), 0.5, MID_GRAY),
    ('LEFTPADDING', (0, 0), (-1, -1), 6),
]))
story.append(lim_table)
story.append(Spacer(1, 3*mm))

# ── Additional safeguards ─────────────────────────────────────────────────────
story.append(SectionHeader("8.  ADDITIONAL SAFEGUARDS (Work with OFPD)", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(bullet("<b>Oxygen supply low-pressure alarm</b> – audible alarm when inlet O₂ "
                    "pressure drops below 20–30 psig"))
story.append(bullet("<b>Oxygen analyser (FiO₂ monitor)</b> – <i>only</i> monitor that detects "
                    "downstream problems incl. pipeline crossover; most important safeguard"))
story.append(bullet("<b>Proportioning systems</b> (SORC / Link-25) – prevent hypoxic N₂O:O₂ ratio "
                    "at flow control valve level (min FiO₂ 25%)"))
story.append(bullet("<b>Oxygen flush valve</b> – delivers 100% O₂ at 35–75 L/min directly, bypassing vaporisers"))
story.append(bullet("<b>Minimum O₂ flow requirement</b> – modern machines mandate a minimum O₂ "
                    "flow (often 200 mL/min) that cannot be turned off"))
story.append(Spacer(1, 3*mm))

# ── Key points summary ────────────────────────────────────────────────────────
story.append(SectionHeader("★  KEY POINTS SUMMARY  (For Exam)", bg=AMBER))
story.append(Spacer(1, 2*mm))
story.append(KeyPointBox([
    "OFPD is in the INTERMEDIATE-pressure section; present in every gas line EXCEPT oxygen",
    "Two types: Binary (threshold shutoff, e.g. 20 psig GE) vs Proportional (balance regulator, modern machines)",
    "Modern OFPD completely shuts N₂O only at ≤ 0.5 psig O₂; proportionally reduces above that",
    "'Fail-safe' is a MISNOMER – does NOT protect against pipeline crossover or contamination",
    "O₂ ANALYSER (FiO₂ monitor) is the ONLY safeguard against pipeline contamination/crossover",
    "OFPD ≠ Proportioning system: OFPD works at piloting pressure; proportioning at flow knob level",
    "Air may bypass OFPD on some machines (air delivery possible even in O₂ failure)",
    "Low-pressure circuit leaks are NOT detected by OFPD (it is an upstream device)",
    "Dräger S-ORC: minimum 200 mL/min O₂ required for N₂O proportioning valve to open",
    "GE Link-25: mechanical chain-link; max N₂O:O₂ ratio = 3:1 (ensures ≥25% FiO₂)",
]))
story.append(Spacer(1, 3*mm))

# ── References ────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=MID_GRAY, spaceAfter=3))
story.append(Paragraph(
    "<b>References:</b> "
    "Miller's Anesthesia 10e (Pardo & Miller), Chapter 20, pp. 2241–2258  |  "
    "Morgan & Mikhail's Clinical Anesthesiology 7e, Chapter 4, pp. 114–115  |  "
    "Barash Clinical Anesthesia 9e, Chapter 25, pp. 1943–1945  |  "
    "ISO 80601-2-13: Medical Electrical Equipment – Anaesthetic Workstations",
    ref_style))

# ── Build PDF ─────────────────────────────────────────────────────────────────
def on_first_page(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(colors.HexColor('#ECF0F5'))
    canvas.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
    canvas.setFont('Helvetica', 7.5)
    canvas.setFillColor(MID_GRAY)
    canvas.drawString(MARGIN, 0.22*cm,
        "Oxygen Failure Protection Device – MD Anaesthesia Short Note")
    canvas.drawRightString(PAGE_W - MARGIN, 0.22*cm, "Page 1")
    canvas.restoreState()

def on_later_pages(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(colors.HexColor('#ECF0F5'))
    canvas.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
    canvas.setFont('Helvetica', 7.5)
    canvas.setFillColor(MID_GRAY)
    canvas.drawString(MARGIN, 0.22*cm,
        "Oxygen Failure Protection Device – MD Anaesthesia Short Note")
    canvas.drawRightString(PAGE_W - MARGIN, 0.22*cm, f"Page {doc.page}")
    canvas.restoreState()

doc.build(story, onFirstPage=on_first_page, onLaterPages=on_later_pages)
size = os.path.getsize(out_path)
print(f"Saved: {out_path}  ({size:,} bytes)")
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_ofpd_pdf.py 2>&1

Editing File

~/anaesthesia-checklist/build_ofpd_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_ofpd_pdf.py'
Also fix the diamond method which uses polygon too:
Editing File

~/anaesthesia-checklist/build_ofpd_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_ofpd_pdf.py'
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_ofpd_pdf.py 2>&1

PDF built successfully. Here is your complete short note:

OFPD MD Anaesthesia Short Note

PDF Document · PDF


Oxygen Failure Protection Device (OFPD) - 5-Mark Short Note


Definition

The OFPD (also called fail-safe valve, pressure-sensor shutoff valve, or balance regulator) is located in the intermediate-pressure section of the anaesthesia machine. It reduces or cuts off N₂O and other gas supply when oxygen pressure falls below a threshold, preventing delivery of a hypoxic mixture. It is an ISO standard on all modern workstations.

Location

Present in the gas supply line of every gas except oxygen, between the intermediate-pressure section (45–55 psi) and the flow control valves. It receives a piloting pressure signal from the O₂ supply line.

Types

TypeMechanismThresholdUsed in
Binary (threshold) shutoffAll-or-nothing; spring closes valve20–30 psigOlder GE/Datex-Ohmeda
Proportional (balance regulator / OFPD)Proportionally reduces N₂O; fully shuts at 0.5 psig0.5 psig (N₂O) / 10 psig (others)All modern machines (ISO std)

Mechanism (Simplified)

O₂ pipeline (50–55 psi)
        ↓
Intermediate-pressure section
        ↓ [piloting pressure line] ────────→ OFPD
                                              ↓
                          O₂ ≥ threshold?
                         /              \
                       YES               NO
                        ↓                ↓
                 Valve OPEN          Valve CLOSED
              N₂O flows freely     N₂O cut off + alarm

Proportioning Systems (Work Alongside OFPD)

  • Dräger S-ORC - pneumatic-mechanical; needs ≥ 200 mL/min O₂; maintains ≥ 25% FiO₂
  • GE Link-25 - mechanical chain-link of O₂ and N₂O flow knobs; max N₂O:O₂ = 3:1

★ KEY POINTS (Exam Focus)

  1. "Fail-safe" is a MISNOMER - responds only to O₂ pressure, not O₂ concentration
  2. Pipeline crossover (N₂O in O₂ line) - O₂ pressure maintained → OFPD stays OPEN → hypoxic mixture delivered
  3. O₂ analyser (FiO₂ monitor) is the ONLY safeguard against pipeline contamination
  4. Location: intermediate-pressure section, upstream of flowmeters
  5. Air may bypass OFPD on some machines - air delivery possible even in O₂ failure
  6. Low-pressure circuit leaks are NOT detected by OFPD
  7. OFPD ≠ Proportioning system - OFPD acts at piloting pressure; proportioning acts at flow knob level
  8. Modern OFPD: complete N₂O shutoff at ≤ 0.5 psig (proportional reduction above that)

Sources: Miller's Anesthesia 10e, Ch.20, p.2241-2258 | Morgan & Mikhail's Clinical Anesthesiology 7e, Ch.4, pp.114-115 | Barash Clinical Anesthesia 9e, Ch.25, pp.1943-1945

Bain's circuit, advantages, disadvantage and modifications and other coaxial circuits

The Bain's circuit is a coaxial modification of the Mapleson D system. Fresh gas flows through an inner tube inside an outer corrugated tube that carries expired gases to the APL valve. It's popular for spontaneous and controlled ventilation, especially in adults.
Searching sources...
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Finding Sources
Finding Sources
I now have comprehensive content from all three major textbooks. Let me also fetch the Bain circuit diagram image and then build the PDF.
Loading Image
Finding Sources
I have all the content needed. Now let me build the comprehensive PDF.
Writing File

~/anaesthesia-checklist/build_bain_pdf.py

"""
Bain's Circuit – MD Anaesthesia Short Note PDF
Advantages, Disadvantages, Modifications, Other Coaxial Circuits
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.platypus.flowables import Flowable
import os
from io import BytesIO

# ── Palette ──────────────────────────────────────────────────────────────────
DARK_BLUE  = colors.HexColor('#1A3A5C')
MED_BLUE   = colors.HexColor('#2E6DA8')
LIGHT_BLUE = colors.HexColor('#D6E8F7')
TEAL       = colors.HexColor('#00878A')
GREEN      = colors.HexColor('#2A7A4B')
GREEN_BG   = colors.HexColor('#D4EDDA')
AMBER      = colors.HexColor('#E88B00')
AMBER_BG   = colors.HexColor('#FFF3CD')
RED        = colors.HexColor('#C0392B')
RED_BG     = colors.HexColor('#F8D7DA')
PURPLE     = colors.HexColor('#6A3D8F')
PURPLE_BG  = colors.HexColor('#EDE7F6')
LIGHT_GRAY = colors.HexColor('#F4F6F9')
MID_GRAY   = colors.HexColor('#88A0B4')
DARK_GRAY  = colors.HexColor('#333333')
WHITE      = colors.white

PAGE_W, PAGE_H = A4
MARGIN = 1.8 * cm

# ── Doc ───────────────────────────────────────────────────────────────────────
out_path = "/tmp/workspace/anaesthesia-checklist/Bain_Circuit_MD_Anaesthesia.pdf"
doc = SimpleDocTemplate(
    out_path, pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=1.0*cm, bottomMargin=1.5*cm,
    title="Bain's Circuit – MD Anaesthesia Short Note",
)

styles = getSampleStyleSheet()

def S(name, **kw):
    return ParagraphStyle(name, parent=styles['Normal'], **kw)

body       = S('body', fontSize=9.5, textColor=DARK_GRAY, leading=14, alignment=TA_JUSTIFY,
               fontName='Helvetica', spaceAfter=2)
body_bold  = S('bb', fontSize=9.5, textColor=DARK_GRAY, leading=14, fontName='Helvetica-Bold')
bullet_s   = S('bul', fontSize=9.5, textColor=DARK_GRAY, leading=14, fontName='Helvetica',
               leftIndent=12, spaceAfter=1)
sub_bul    = S('sbul', fontSize=9, textColor=DARK_GRAY, leading=13, fontName='Helvetica',
               leftIndent=24, spaceAfter=1)
ref_style  = S('ref', fontSize=8, textColor=MID_GRAY, leading=11, fontName='Helvetica-Oblique')
small_c    = S('sc', fontSize=8, textColor=MID_GRAY, alignment=TA_CENTER,
               fontName='Helvetica-Oblique', leading=10)

def B(txt): return Paragraph(f'<bullet>&bull;</bullet> {txt}', bullet_s)
def SB(txt): return Paragraph(f'  \u2013 {txt}', sub_bul)

# ── Custom Flowables ──────────────────────────────────────────────────────────
class TitleBar(Flowable):
    def __init__(self, line1, line2, sub):
        self.line1, self.line2, self.sub = line1, line2, sub
        self.height = 2.0*cm
        Flowable.__init__(self)

    def wrap(self, aW, aH):
        self._w = aW
        return (aW, self.height)

    def draw(self):
        c = self.canv
        w, h = self._w, self.height
        c.setFillColor(DARK_BLUE); c.rect(0, 0, w, h, fill=1, stroke=0)
        c.setFillColor(AMBER);     c.rect(0, 0, 5, h, fill=1, stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold', 15)
        c.drawString(12, h - 22, self.line1)
        c.setFont('Helvetica-Bold', 13)
        c.drawString(12, h - 38, self.line2)
        c.setFillColor(colors.HexColor('#AACCEE'))
        c.setFont('Helvetica-Oblique', 9)
        c.drawString(12, 6, self.sub)


class SectionHdr(Flowable):
    def __init__(self, text, bg=MED_BLUE, h=0.52*cm):
        self.text, self.bg, self.h = text, bg, h
        Flowable.__init__(self)

    def wrap(self, aW, aH):
        self._w = aW
        return (aW, self.h)

    def draw(self):
        c = self.canv
        c.setFillColor(self.bg); c.rect(0, 0, self._w, self.h, fill=1, stroke=0)
        c.setFillColor(AMBER);   c.rect(0, 0, 3.5, self.h, fill=1, stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold', 10)
        c.drawString(10, (self.h - 10)/2 + 1, self.text)


class KeyBox(Flowable):
    def __init__(self, items, bg=AMBER_BG, border=AMBER):
        self.items, self.bg, self.border = items, bg, border
        Flowable.__init__(self)

    def wrap(self, aW, aH):
        self._w = aW
        self._h = 8 + len(self.items)*15 + 8
        return (self._w, self._h)

    def draw(self):
        c = self.canv
        c.setFillColor(self.bg); c.setStrokeColor(self.border); c.setLineWidth(1.5)
        c.roundRect(0, 0, self._w, self._h, 4, fill=1, stroke=1)
        c.setFillColor(self.border); c.rect(0, 0, 4, self._h, fill=1, stroke=0)
        for i, item in enumerate(reversed(self.items)):
            y = 8 + i*15
            c.setFillColor(self.border); c.setFont('Helvetica-Bold', 9)
            c.drawString(10, y, '\u2605')
            c.setFillColor(DARK_GRAY); c.setFont('Helvetica', 9)
            c.drawString(22, y, item)


class BainDiagram(Flowable):
    """ASCII-style schematic of Bain circuit components."""
    def __init__(self):
        Flowable.__init__(self)

    def wrap(self, aW, aH):
        self._w = aW
        self._h = 5.8*cm
        return (self._w, self._h)

    def _box(self, c, x, y, w, h, fill, text, fs=8, tc=WHITE, bold=False):
        c.setFillColor(fill); c.setStrokeColor(MID_GRAY); c.setLineWidth(0.5)
        c.roundRect(x, y, w, h, 3, fill=1, stroke=1)
        fn = 'Helvetica-Bold' if bold else 'Helvetica'
        c.setFillColor(tc); c.setFont(fn, fs)
        lines = text.split('\n')
        lh = fs + 2
        sy = y + h/2 + (len(lines)-1)*lh/2 - 1
        for ln in lines:
            tw = c.stringWidth(ln, fn, fs)
            c.drawString(x + (w-tw)/2, sy, ln)
            sy -= lh

    def _line(self, c, x1, y1, x2, y2, col=MID_GRAY, dash=None):
        c.setStrokeColor(col); c.setLineWidth(1.2)
        if dash: c.setDash(dash)
        c.line(x1, y1, x2, y2)
        if dash: c.setDash([])

    def _arrow_h(self, c, x1, y, x2, col=AMBER):
        self._line(c, x1, y, x2, y, col=col)
        p = c.beginPath()
        if x2 > x1:
            p.moveTo(x2-6, y-3); p.lineTo(x2-6, y+3); p.lineTo(x2, y); p.close()
        else:
            p.moveTo(x2+6, y-3); p.lineTo(x2+6, y+3); p.lineTo(x2, y); p.close()
        c.setFillColor(col); c.drawPath(p, fill=1, stroke=0)

    def draw(self):
        c = self.canv
        fw, fh = self._w, self._h

        # Background
        c.setFillColor(LIGHT_GRAY); c.rect(0, 0, fw, fh, fill=1, stroke=0)

        # Title
        c.setFillColor(DARK_BLUE); c.rect(0, fh-0.55*cm, fw, 0.55*cm, fill=1, stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold', 9)
        t = "BAIN CIRCUIT – Schematic (Coaxial Mapleson D)"
        c.drawString((fw - c.stringWidth(t,'Helvetica-Bold',9))/2, fh-0.38*cm, t)

        # Dimensions
        bh = 0.65*cm
        y_outer = 1.8*cm   # outer tube (expiratory) level
        y_inner = 3.0*cm   # inner tube (fresh gas) level
        y_top   = fh - 0.7*cm

        # Components
        # 1. Reservoir bag
        self._box(c, 0.2*cm, y_outer - 0.2*cm, 1.6*cm, 1.1*cm, DARK_BLUE,
                  'Reservoir\nBag', fs=7.5, bold=True)

        # 2. APL / Pop-off valve (near bag end)
        self._box(c, 0.2*cm, y_top - bh, 1.6*cm, bh, AMBER,
                  'APL\n(Pop-off)', fs=7.5, tc=WHITE, bold=True)
        c.setFillColor(AMBER); c.setFont('Helvetica-Oblique', 7)
        c.drawString(0.22*cm, y_top + 0.05*cm, 'Near reservoir end')

        # 3. Outer corrugated tube (expiratory limb)
        # Draw as two parallel lines
        tube_x1 = 1.85*cm; tube_x2 = fw - 2.2*cm
        c.setStrokeColor(MED_BLUE); c.setLineWidth(10)
        c.line(tube_x1, y_outer + 0.3*cm, tube_x2, y_outer + 0.3*cm)
        c.setFillColor(WHITE); c.setFont('Helvetica', 7.5)
        lbl = 'OUTER CORRUGATED TUBE  (Exhaled gas travels ← toward APL valve)'
        c.drawString(tube_x1 + 0.2*cm, y_outer + 0.05*cm, lbl)

        # Arrow showing exhaled gas direction (right to left)
        self._arrow_h(c, tube_x2 - 0.5*cm, y_outer + 0.3*cm,
                      tube_x1 + 0.8*cm, y_outer + 0.3*cm, col=colors.HexColor('#88AACC'))

        # 4. Inner tube (fresh gas) – dashed line inside outer
        c.setStrokeColor(AMBER); c.setLineWidth(1.5); c.setDash([4,3])
        c.line(tube_x1 + 0.1*cm, y_outer + 0.3*cm, tube_x2, y_outer + 0.3*cm)
        c.setDash([])
        c.setFillColor(AMBER); c.setFont('Helvetica-Bold', 7.5)
        c.drawString(tube_x1 + 0.2*cm, y_inner, 'INNER TUBE (Fresh gas flows → toward patient end)')

        # FGF enters near reservoir
        self._box(c, 0.2*cm, y_inner - bh/2, 1.6*cm, bh, TEAL,
                  'Fresh Gas\nInlet', fs=7.5, bold=True)
        c.setFillColor(TEAL); c.setFont('Helvetica-Oblique', 6.5)
        c.drawString(0.3*cm, y_inner - bh/2 - 0.25*cm, 'Enters near bag end')

        # Arrow FGF toward patient
        self._arrow_h(c, tube_x1 + 0.5*cm, y_inner, tube_x2 - 0.3*cm, y_inner, col=AMBER)

        # 5. Patient end
        self._box(c, fw - 2.1*cm, y_outer - 0.3*cm, 1.8*cm, 1.5*cm, GREEN,
                  'Patient\nEnd\n(FGF exits here)', fs=7, bold=True)
        c.setFillColor(GREEN); c.setFont('Helvetica-Oblique', 7)
        c.drawString(fw - 2.0*cm, y_outer - 0.5*cm, 'Y-piece / ETT')

        # Countercurrent heat exchange annotation
        c.setFillColor(RED); c.setFont('Helvetica-Oblique', 7.5)
        c.drawString(tube_x1 + 1.5*cm, 0.15*cm,
                     '↕ Countercurrent heat exchange: exhaled gases WARM inspired fresh gas')

        # Legend
        lx = 0.3*cm; ly = 0.7*cm
        for col, lbl in [(MED_BLUE, 'Outer tube (expiratory)'),
                         (AMBER,    'Inner tube (FGF)'),
                         (GREEN,    'Patient end')]:
            c.setFillColor(col); c.rect(lx, ly, 0.35*cm, 0.25*cm, fill=1, stroke=0)
            c.setFillColor(DARK_GRAY); c.setFont('Helvetica', 7)
            c.drawString(lx + 0.45*cm, ly + 0.05*cm, lbl)
            lx += 3.5*cm


class FGFFlowchart(Flowable):
    """FGF requirements decision flowchart for Bain circuit."""
    def __init__(self):
        Flowable.__init__(self)

    def wrap(self, aW, aH):
        self._w = aW
        self._h = 4.8*cm
        return (self._w, self._h)

    def _box(self, c, x, y, w, h, fill, text, fs=8.5, tc=WHITE, bold=True):
        c.setFillColor(fill); c.setStrokeColor(MID_GRAY); c.setLineWidth(0.5)
        c.roundRect(x, y, w, h, 3, fill=1, stroke=1)
        fn = 'Helvetica-Bold' if bold else 'Helvetica'
        c.setFillColor(tc); c.setFont(fn, fs)
        lines = text.split('\n'); lh = fs + 2
        sy = y + h/2 + (len(lines)-1)*lh/2 - 1
        for ln in lines:
            tw = c.stringWidth(ln, fn, fs)
            c.drawString(x + (w-tw)/2, sy, ln); sy -= lh

    def _diamond(self, c, cx, cy, w, h, fill, text, fs=8):
        p = c.beginPath()
        p.moveTo(cx, cy+h/2); p.lineTo(cx+w/2, cy)
        p.lineTo(cx, cy-h/2); p.lineTo(cx-w/2, cy); p.close()
        c.setFillColor(fill); c.setStrokeColor(DARK_BLUE); c.setLineWidth(1)
        c.drawPath(p, fill=1, stroke=1)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold', fs)
        lines = text.split('\n'); lh = fs + 1.5
        sy = cy + (len(lines)-1)*lh/2
        for ln in lines:
            tw = c.stringWidth(ln,'Helvetica-Bold',fs)
            c.drawString(cx-tw/2, sy, ln); sy -= lh

    def draw(self):
        c = self.canv
        fw, fh = self._w, self._h

        c.setFillColor(LIGHT_GRAY); c.rect(0, 0, fw, fh, fill=1, stroke=0)
        c.setFillColor(TEAL); c.rect(0, fh-0.52*cm, fw, 0.52*cm, fill=1, stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold', 9)
        t = "FRESH GAS FLOW (FGF) Requirements – Bain Circuit"
        c.drawString((fw-c.stringWidth(t,'Helvetica-Bold',9))/2, fh-0.36*cm, t)

        # Start
        cx = fw/2; bw = 3.8*cm; bh = 0.55*cm
        self._box(c, cx-bw/2, fh-1.2*cm, bw, bh, DARK_BLUE, 'BAIN CIRCUIT IN USE')

        # Arrow
        c.setStrokeColor(MID_GRAY); c.setLineWidth(1.2)
        c.line(cx, fh-1.2*cm, cx, fh-1.65*cm)

        # Decision diamond
        self._diamond(c, cx, fh-2.15*cm, 3.5*cm, 0.9*cm, AMBER,
                      'Mode of\nventilation?')

        # Left branch: spontaneous
        lx = 1.5*cm
        c.setStrokeColor(MID_GRAY); c.setLineWidth(1.2)
        c.line(cx - 1.75*cm, fh-2.15*cm, lx + bw/2, fh-2.15*cm)
        c.line(lx + bw/2, fh-2.15*cm, lx + bw/2, fh-2.7*cm)
        c.setFillColor(MED_BLUE); c.setFont('Helvetica-Bold', 7)
        c.drawString(lx, fh-2.1*cm, 'SPONTANEOUS')
        self._box(c, lx, fh-3.3*cm, bw, 0.55*cm, MED_BLUE,
                  'FGF = 200–300 mL/kg/min\nor 2× minute ventilation')
        self._box(c, lx, fh-4.0*cm, bw, 0.55*cm, colors.HexColor('#1A6A8A'),
                  'Adults: ~150–200 mL/kg/min\n= ~70 kg → ~14 L/min')

        # Right branch: controlled
        rx = fw - 1.5*cm - bw
        c.setStrokeColor(MID_GRAY); c.setLineWidth(1.2)
        c.line(cx + 1.75*cm, fh-2.15*cm, rx + bw/2, fh-2.15*cm)
        c.line(rx + bw/2, fh-2.15*cm, rx + bw/2, fh-2.7*cm)
        c.setFillColor(GREEN); c.setFont('Helvetica-Bold', 7)
        c.drawString(rx, fh-2.1*cm, 'CONTROLLED (IPPV)')
        self._box(c, rx, fh-3.3*cm, bw, 0.55*cm, GREEN,
                  'FGF = 70 mL/kg/min\nor 1× minute ventilation')
        self._box(c, rx, fh-4.0*cm, bw, 0.55*cm, colors.HexColor('#1A5C38'),
                  'Adults: ~70 kg → ~5 L/min\n(Spoerel & Bain 1973)')

        # Note
        c.setFillColor(RED_BG); c.setStrokeColor(RED); c.setLineWidth(0.8)
        c.roundRect(0.3*cm, 0.1*cm, fw-0.6*cm, 0.52*cm, 3, fill=1, stroke=1)
        c.setFillColor(RED); c.setFont('Helvetica-Bold', 8)
        note = ('Pethick Test: Occlude patient end → fill bag with O\u2082 flush → release → '
                'Venturi deflates bag (PASS) | Bag stays inflated = inner tube LEAK')
        c.drawString((fw - c.stringWidth(note,'Helvetica-Bold',8))/2, 0.22*cm, note)


story = []

# ── Title ─────────────────────────────────────────────────────────────────────
story.append(TitleBar(
    "BAIN'S CIRCUIT",
    "Advantages · Disadvantages · Modifications · Other Coaxial Circuits",
    "MD Anaesthesia Short Note  |  Mapleson Breathing Systems  |  Sources: Miller 10e, Barash 9e, Morgan & Mikhail 7e"
))
story.append(Spacer(1, 3*mm))

# ── 1. Introduction ───────────────────────────────────────────────────────────
story.append(SectionHdr("1.  INTRODUCTION & DEFINITION", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "The <b>Bain circuit</b>, described by Bain and Spoerel in 1972, is a <b>coaxial modification "
    "of the Mapleson D system</b>. It consists of an <b>outer corrugated tube</b> (expiratory limb / "
    "reservoir) enclosing a narrow <b>inner tube</b> through which fresh gas flows. The fresh gas "
    "enters the circuit near the <b>reservoir bag end</b> but empties at the <b>patient end</b>. "
    "Exhaled gases travel back along the outer tube toward the <b>APL (adjustable pressure-limiting / "
    "pop-off) valve</b> situated near the reservoir bag. "
    "The circuit can be used for <b>both spontaneous and controlled ventilation</b>.",
    body))
story.append(Spacer(1, 3*mm))

# ── Schematic diagram ─────────────────────────────────────────────────────────
story.append(BainDiagram())
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "<i>Fig. Based on Bain JA, Spoerel WE. A streamlined anaesthetic system. "
    "Can Anaesth Soc J. 1972;19(4):426–435.</i>",
    small_c))
story.append(Spacer(1, 3*mm))

# ── 2. Classification ─────────────────────────────────────────────────────────
story.append(SectionHdr("2.  MAPLESON CLASSIFICATION – WHERE BAIN FITS", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
map_data = [
    [Paragraph('<b>Circuit</b>', body_bold),
     Paragraph('<b>APL Valve</b>', body_bold),
     Paragraph('<b>FGF Inlet</b>', body_bold),
     Paragraph('<b>Best For</b>', body_bold),
     Paragraph('<b>FGF (Spontaneous)</b>', body_bold)],
    ['A (Magill)', 'Near patient', 'Near bag', 'Spontaneous ✓', '= MV (1× MV)'],
    ['B', 'Near patient', 'Near patient', 'Poor efficiency', '> 2× MV'],
    ['C (Waters)', 'Near patient', 'Near patient', 'Resuscitation', '> 2× MV'],
    ['D', 'Near bag', 'Near patient', 'Controlled ✓', '> 2× MV'],
    ['D* BAIN', 'Near bag', 'Inner tube → patient end', 'Both ✓ (coaxial)', '200–300 mL/kg/min'],
    ['E (Ayre T-piece)', 'Open tail', 'Near patient', 'Paediatric ✓', '2.5–3× MV'],
    ['F (Jackson–Rees)', 'Bag + valve', 'Near patient', 'Paediatric ✓', '2.5–3× MV'],
]
map_table = Table(map_data,
    colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.14, 0.14, 0.20, 0.24, 0.28]])
map_table.setStyle(TableStyle([
    ('BACKGROUND',  (0, 0), (-1, 0), DARK_BLUE),
    ('TEXTCOLOR',   (0, 0), (-1, 0), WHITE),
    ('FONTNAME',    (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',    (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',    (0, 0), (-1, -1), 8),
    ('BACKGROUND',  (0, 5), (-1, 5), AMBER_BG),
    ('FONTNAME',    (0, 5), (-1, 5), 'Helvetica-Bold'),
    ('ROWBACKGROUNDS', (0, 1), (-1, -1), [WHITE, LIGHT_BLUE, WHITE, LIGHT_BLUE, AMBER_BG, LIGHT_BLUE, WHITE]),
    ('ROWHEIGHT',   (0, 0), (-1, -1), 0.52*cm),
    ('VALIGN',      (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',       (0, 0), (-1, -1), 'CENTER'),
    ('GRID',        (0, 0), (-1, -1), 0.4, MID_GRAY),
    ('LEFTPADDING', (0, 0), (-1, -1), 4),
]))
story.append(map_table)
story.append(Paragraph("<i>* Highlighted row = Bain Circuit.  MV = minute ventilation.</i>", small_c))
story.append(Spacer(1, 3*mm))

# ── 3. Components ─────────────────────────────────────────────────────────────
story.append(SectionHdr("3.  COMPONENTS OF THE BAIN CIRCUIT", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
comp_data = [
    [Paragraph('<b>Component</b>', body_bold),
     Paragraph('<b>Description</b>', body_bold),
     Paragraph('<b>Clinical Significance</b>', body_bold)],
    ['Inner tube (narrow)', 'Carries FGF from machine inlet\n→ patient end',
     'Kinking/disconnection → hypercapnia\nMust be transparent for inspection'],
    ['Outer corrugated tube', 'Carries exhaled gases → APL valve;\nencloses the inner tube',
     'Provides countercurrent heat & humidity\nexchange to inspired gas'],
    ['Reservoir bag', 'Located at machine end (near APL valve)',
     'Accommodates tidal volume; used for\nmanual ventilation assessment'],
    ['APL / Pop-off valve', 'Located near reservoir bag\n(away from patient)',
     'Easy scavenging; easy access;\ncontrols circuit pressure'],
    ['Patient end (Y-piece)', 'FGF exits here; exhaled gas\nenters outer tube here',
     'Minimal dead space at patient end;\nlightweight connection'],
]
comp_t = Table(comp_data,
    colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.22, 0.40, 0.38]])
comp_t.setStyle(TableStyle([
    ('BACKGROUND',  (0, 0), (-1, 0), TEAL),
    ('TEXTCOLOR',   (0, 0), (-1, 0), WHITE),
    ('FONTNAME',    (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',    (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',    (0, 0), (-1, -1), 8.5),
    ('ROWBACKGROUNDS', (0, 1), (-1, -1), [LIGHT_BLUE, WHITE, LIGHT_BLUE, WHITE, LIGHT_BLUE]),
    ('ROWHEIGHT',   (0, 0), (-1, -1), 0.65*cm),
    ('VALIGN',      (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',       (0, 0), (0, -1), 'CENTER'),
    ('GRID',        (0, 0), (-1, -1), 0.4, MID_GRAY),
    ('LEFTPADDING', (0, 0), (-1, -1), 5),
]))
story.append(comp_t)
story.append(Spacer(1, 3*mm))

# ── 4. FGF requirements ───────────────────────────────────────────────────────
story.append(SectionHdr("4.  FRESH GAS FLOW (FGF) REQUIREMENTS", bg=TEAL))
story.append(Spacer(1, 2*mm))
story.append(FGFFlowchart())
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "For an average adult (70 kg, MV ~7 L/min): <b>Spontaneous</b> – FGF ≈ 14 L/min "
    "(200 mL/kg/min); <b>Controlled (IPPV)</b> – FGF ≈ 70 mL/kg/min (~5 L/min). "
    "The FGF to prevent rebreathing is <b>2.5× MV</b> (Barash 9e).",
    body))
story.append(Spacer(1, 3*mm))

# ── 5. Advantages ─────────────────────────────────────────────────────────────
story.append(SectionHdr("5.  ADVANTAGES", bg=GREEN))
story.append(Spacer(1, 2*mm))
advs = [
    ("<b>Lightweight and simple</b>", "No valves or CO₂ absorber; minimal components"),
    ("<b>Disposable</b>", "Reduces risk of cross-infection; cost-effective"),
    ("<b>Low resistance</b>", "No unidirectional valves → suitable for spontaneous ventilation in adults"),
    ("<b>Countercurrent heat & humidity exchange</b>",
     "Exhaled gases in outer tube partially warm and humidify inspired fresh gas through the inner tube"),
    ("<b>Easy scavenging</b>",
     "APL valve located away from patient (at machine end) → easy to connect scavenging"),
    ("<b>Convenience in remote/difficult locations</b>",
     "Lightweight and flexible; suitable for head & neck surgery, remote anaesthesia, transport"),
    ("<b>No CO₂ absorber required</b>", "Simpler maintenance; CO₂ eliminated by high FGF"),
    ("<b>Both spontaneous & controlled ventilation</b>",
     "Versatile; more efficient than Mapleson D for controlled ventilation"),
    ("<b>Adjustable circuit length</b>",
     "Outer hose available in different lengths for different surgical positions"),
    ("<b>COVID/filter compatibility</b>",
     "Can be modified with bacterial/viral filters (reported use in COVID-19 patients)"),
]
for title, detail in advs:
    story.append(B(f'{title}: {detail}'))
story.append(Spacer(1, 3*mm))

# ── 6. Disadvantages ─────────────────────────────────────────────────────────
story.append(SectionHdr("6.  DISADVANTAGES", bg=RED))
story.append(Spacer(1, 2*mm))
disadvs = [
    ("<b>High FGF required</b>",
     "2.5× MV for spontaneous ventilation → waste of anaesthetic agent, pollution, cost"),
    ("<b>Kinking / disconnection of inner tube</b>",
     "Most important hazard; may go unrecognized → increased resistance, rebreathing, hypercapnia, hypoxaemia"),
    ("<b>No conservation of heat/humidity</b> (relative)",
     "Less effective than circle system at very low FGF"),
    ("<b>Rebreathing risk</b>",
     "Inadequate FGF leads to CO₂ rebreathing; no CO₂ absorber as safety net"),
    ("<b>OR pollution</b>",
     "High FGF with volatile agents → significant theatre pollution unless scavenged"),
    ("<b>Not ideal for paediatrics</b>",
     "High resistance relative to small tidal volumes; Jackson–Rees circuit preferred"),
    ("<b>Difficult to detect inner tube leak</b>",
     "Requires specific Pethick test; routine checks may miss partial disconnection"),
    ("<b>Obstruction risk</b>",
     "Anti-microbial filter between Bain and ETT can increase resistance → mimics severe bronchospasm"),
]
for title, detail in disadvs:
    story.append(B(f'{title}: {detail}'))
story.append(Spacer(1, 3*mm))

# ── 7. Pethick Test ───────────────────────────────────────────────────────────
story.append(SectionHdr("7.  PETHICK TEST – Inner Tube Integrity Check", bg=AMBER))
story.append(Spacer(1, 2*mm))

ptest_data = [
    [Paragraph('<b>Step</b>', body_bold), Paragraph('<b>Action</b>', body_bold),
     Paragraph('<b>Expected Result</b>', body_bold)],
    ['1', 'Occlude patient end of circuit', 'Circuit sealed'],
    ['2', 'Flush O₂ at high flow until reservoir bag fully inflated', 'Bag distended'],
    ['3', 'Open (release) patient end rapidly', 'Venturi effect created'],
    ['4 – PASS', 'Inner tube intact → Venturi creates negative pressure\nat patient end',
     'Reservoir bag DEFLATES\n→ Inner tube INTACT ✓'],
    ['4 – FAIL', 'Inner tube leaking → fresh gas escapes into\nexpiratory limb',
     'Reservoir bag remains INFLATED\n→ LEAK in inner tube ✗'],
]
ptest_t = Table(ptest_data,
    colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.10, 0.50, 0.40]])
ptest_t.setStyle(TableStyle([
    ('BACKGROUND',  (0, 0), (-1, 0), AMBER),
    ('TEXTCOLOR',   (0, 0), (-1, 0), WHITE),
    ('FONTNAME',    (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',    (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',    (0, 0), (-1, -1), 8.5),
    ('BACKGROUND',  (0, 4), (-1, 4), GREEN_BG),
    ('BACKGROUND',  (0, 5), (-1, 5), RED_BG),
    ('ROWBACKGROUNDS', (0, 1), (-1, 3), [WHITE, LIGHT_BLUE, WHITE]),
    ('ROWHEIGHT',   (0, 0), (-1, -1), 0.65*cm),
    ('VALIGN',      (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',       (0, 0), (0, -1), 'CENTER'),
    ('GRID',        (0, 0), (-1, -1), 0.4, MID_GRAY),
    ('LEFTPADDING', (0, 0), (-1, -1), 5),
]))
story.append(ptest_t)
story.append(Spacer(1, 3*mm))

# ── 8. Modifications ─────────────────────────────────────────────────────────
story.append(SectionHdr("8.  MODIFICATIONS OF THE BAIN CIRCUIT", bg=PURPLE))
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>A. Lack Coaxial Circuit (Coaxial Mapleson A)</b>", body_bold))
story.append(B("Described by <b>Philip Lack (1976)</b>"))
story.append(B("Coaxial version of the <b>Mapleson A (Magill) circuit</b>"))
story.append(B("Inner tube carries <b>exhaled gas</b> toward APL valve (opposite of Bain)"))
story.append(B("Outer tube carries <b>fresh gas</b> toward the patient"))
story.append(B("APL valve at machine end → <b>easy scavenging</b> (same advantage as Bain)"))
story.append(B("Ideal for <b>spontaneous ventilation</b>: FGF = minute ventilation (MV) → most efficient"))
story.append(B("Disadvantage: same inner tube kinking/disconnection risk as Bain"))
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>B. Extended versions / Paediatric Bain Modifications</b>", body_bold))
story.append(B("Miniaturised coaxial circuits for paediatric use"))
story.append(B("Lower dead space and resistance optimised for smaller tidal volumes"))
story.append(B("Used for patients 10–20 kg (above which Bain circuit becomes more appropriate)"))
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>C. COVID-19 Modification (2020–2021)</b>", body_bold))
story.append(B("Bacterial/viral filter inserted between Bain circuit and endotracheal tube"))
story.append(B("Used to reduce aerosol exposure risk to theatre staff"))
story.append(B("Caution: filter increases circuit resistance → monitor for bronchospasm-like picture"))
story.append(Spacer(1, 3*mm))

# ── 9. Other Coaxial Circuits ─────────────────────────────────────────────────
story.append(SectionHdr("9.  OTHER COAXIAL CIRCUITS", bg=PURPLE))
story.append(Spacer(1, 2*mm))

coax_data = [
    [Paragraph('<b>Circuit</b>', body_bold),
     Paragraph('<b>Mapleson\nEquivalent</b>', body_bold),
     Paragraph('<b>Inner Tube\nCarries</b>', body_bold),
     Paragraph('<b>Outer Tube\nCarries</b>', body_bold),
     Paragraph('<b>Best For</b>', body_bold),
     Paragraph('<b>Key Feature</b>', body_bold)],
    ['Bain Circuit\n(Bain & Spoerel, 1972)', 'Mapleson D',
     'Fresh gas\n(→ patient)', 'Exhaled gas\n(← to APL)',
     'Controlled ventilation\n(adults)', 'FGF: 70 mL/kg/min (IPPV)'],
    ['Lack Circuit\n(Philip Lack, 1976)', 'Mapleson A',
     'Exhaled gas\n(← to APL)', 'Fresh gas\n(→ patient)',
     'Spontaneous ventilation\n(adults)', 'FGF = 1× MV (most efficient)'],
    ['Humphrey ADE Block\n(Humphrey, 1983)', 'A / D / E\nswitchable',
     'Configurable\n(switch lever)', 'Configurable',
     'Both modes;\npaediatric + adult',
     'Lever switches between\nMapleson A, D, or E mode'],
    ['Enclosed Magill\n(Coaxial Mapleson A)', 'Mapleson A',
     'Exhaled gas', 'Fresh gas',
     'Spontaneous\nventilation',
     'Enclosed scavenging;\nAPL at machine end'],
    ['Miniaturised coaxial\ncircuits (paediatric)', 'D or F variants',
     'Fresh gas', 'Exhaled gas',
     'Paediatric\n(10–20 kg)',
     'Reduced dead space;\nlighter weight'],
]
coax_t = Table(coax_data,
    colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.20, 0.12, 0.14, 0.14, 0.20, 0.20]])
coax_t.setStyle(TableStyle([
    ('BACKGROUND',  (0, 0), (-1, 0), PURPLE),
    ('TEXTCOLOR',   (0, 0), (-1, 0), WHITE),
    ('FONTNAME',    (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',    (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',    (0, 0), (-1, -1), 8),
    ('BACKGROUND',  (0, 1), (-1, 1), AMBER_BG),
    ('FONTNAME',    (0, 1), (-1, 1), 'Helvetica-Bold'),
    ('ROWBACKGROUNDS', (0, 2), (-1, -1), [PURPLE_BG, WHITE, PURPLE_BG, WHITE]),
    ('ROWHEIGHT',   (0, 0), (-1, -1), 0.72*cm),
    ('VALIGN',      (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',       (0, 0), (-1, -1), 'CENTER'),
    ('GRID',        (0, 0), (-1, -1), 0.4, MID_GRAY),
    ('LEFTPADDING', (0, 0), (-1, -1), 4),
]))
story.append(coax_t)
story.append(Paragraph("<i>Highlighted row (amber) = Bain circuit.</i>", small_c))
story.append(Spacer(1, 3*mm))

# ── 10. Humphrey ADE Block (important modification/related circuit) ───────────
story.append(SectionHdr("10.  HUMPHREY ADE BLOCK – Detailed Note", bg=PURPLE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "The <b>Humphrey ADE block</b> is a versatile coaxial circuit described in 1983 that "
    "can function as <b>Mapleson A, D, or E</b> mode by using a simple lever switch. "
    "It is coaxial in design and incorporates all components within a single block unit:",
    body))
story.append(Spacer(1, 2*mm))
story.append(B("<b>Lever UP (position A)</b>: Mapleson A mode → ideal for spontaneous ventilation; FGF = MV"))
story.append(B("<b>Lever DOWN (position D/E)</b>: Mapleson D or E mode → ideal for controlled ventilation"))
story.append(B("Can be used for both <b>adult</b> (with breathing bag) and <b>paediatric</b> patients (T-piece mode)"))
story.append(B("Single unit replaces multiple separate circuits in theatre"))
story.append(B("Allows easy scavenging regardless of mode"))
story.append(Spacer(1, 3*mm))

# ── 11. Comparison: Bain vs Circle vs Lack ───────────────────────────────────
story.append(SectionHdr("11.  QUICK COMPARISON: Bain vs Circle vs Lack", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
comp2_data = [
    [Paragraph('<b>Feature</b>', body_bold),
     Paragraph('<b>Bain (Mapleson D)</b>', body_bold),
     Paragraph('<b>Lack (Mapleson A)</b>', body_bold),
     Paragraph('<b>Circle System</b>', body_bold)],
    ['CO₂ absorber', 'No', 'No', 'Yes (required)'],
    ['FGF (spontaneous)', '200–300 mL/kg/min', '= 1× MV (lowest)', 'Low (200 mL/min)'],
    ['FGF (controlled)', '70 mL/kg/min', '3× MV (inefficient)', 'Very low'],
    ['Rebreathing', 'No (if FGF adequate)', 'No (if FGF adequate)', 'Yes (controlled)'],
    ['Heat/humidity', 'Partial (counter\ncurrent exchange)', 'Minimal', 'Best (via absorber\nand circuit)'],
    ['Scavenging', 'Easy (APL at\nmachine end)', 'Easy (APL at\nmachine end)', 'Standard'],
    ['Hazards', 'Inner tube kink /\ndisconnection', 'Inner tube kink /\ndisconnection', 'Valve failure,\nabsorber exhaustion'],
    ['Best use', 'Controlled ventilation\nhead/neck surgery', 'Spontaneous\nventilation adults', 'All cases;\nlow-flow preferred'],
]
comp2_t = Table(comp2_data,
    colWidths=[(PAGE_W - 2*MARGIN)*x for x in [0.22, 0.26, 0.26, 0.26]])
comp2_t.setStyle(TableStyle([
    ('BACKGROUND',  (0, 0), (-1, 0), DARK_BLUE),
    ('TEXTCOLOR',   (0, 0), (-1, 0), WHITE),
    ('FONTNAME',    (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTNAME',    (0, 1), (-1, -1), 'Helvetica'),
    ('FONTSIZE',    (0, 0), (-1, -1), 8.5),
    ('BACKGROUND',  (1, 1), (1, -1), AMBER_BG),
    ('ROWBACKGROUNDS', (0, 1), (0, -1),
     [LIGHT_BLUE, WHITE, LIGHT_BLUE, WHITE, LIGHT_BLUE, WHITE, LIGHT_BLUE, WHITE]),
    ('ROWHEIGHT',   (0, 0), (-1, -1), 0.58*cm),
    ('VALIGN',      (0, 0), (-1, -1), 'MIDDLE'),
    ('ALIGN',       (0, 0), (0, -1), 'LEFT'),
    ('ALIGN',       (1, 0), (-1, -1), 'CENTER'),
    ('GRID',        (0, 0), (-1, -1), 0.4, MID_GRAY),
    ('LEFTPADDING', (0, 0), (-1, -1), 5),
]))
story.append(comp2_t)
story.append(Paragraph("<i>Amber column = Bain circuit values.</i>", small_c))
story.append(Spacer(1, 3*mm))

# ── 12. Key Points ────────────────────────────────────────────────────────────
story.append(SectionHdr("★  KEY POINTS SUMMARY  (For Exam)", bg=AMBER))
story.append(Spacer(1, 2*mm))
story.append(KeyBox([
    "Bain = Coaxial Mapleson D: inner tube (FGF → patient), outer tube (exhaled gas ← to APL)",
    "FGF: Spontaneous = 200–300 mL/kg/min (2.5× MV); Controlled = 70 mL/kg/min (1× MV)",
    "APL valve at machine end (not patient end) → easy scavenging",
    "Advantages: lightweight, disposable, countercurrent heat exchange, easy scavenging, no CO₂ absorber",
    "Main hazard: kinking/disconnection of INNER TUBE → unrecognised → hypercapnia/hypoxaemia",
    "Pethick test: O₂ flush → occlude patient end → release → bag deflates = PASS; stays inflated = FAIL",
    "Lack circuit = Coaxial Mapleson A; inner tube carries exhaled gas; best for spontaneous (FGF = MV)",
    "Humphrey ADE block: lever switches between Mapleson A, D, and E modes; adult + paediatric",
    "Circle system > Bain for heat/humidity and FGF economy; Bain > Circle for simplicity/portability",
    "Outer corrugated tube must be TRANSPARENT to allow visual inspection of inner tube",
]))
story.append(Spacer(1, 3*mm))

# ── References ─────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=MID_GRAY, spaceAfter=3))
story.append(Paragraph(
    "<b>References:</b> "
    "Miller's Anesthesia 10e (Pardo & Miller), Ch. 20, pp. 2350–2354  |  "
    "Barash Clinical Anesthesia 9e, Ch. 25, pp. 2012–2013  |  "
    "Morgan & Mikhail's Clinical Anesthesiology 7e, Ch. 3, pp. 86–88  |  "
    "Bain JA, Spoerel WE. A streamlined anaesthetic system. Can Anaesth Soc J. 1972;19(4):426–435  |  "
    "Bain JA, Spoerel WE. Flow requirements for a modified Mapleson D system during controlled "
    "ventilation. Can Anaesth Soc J. 1973;20(5):629–636",
    ref_style))

# ── Build ──────────────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(colors.HexColor('#ECF0F5'))
    canvas.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
    canvas.setFont('Helvetica', 7.5)
    canvas.setFillColor(MID_GRAY)
    canvas.drawString(MARGIN, 0.22*cm, "Bain's Circuit – MD Anaesthesia Short Note")
    canvas.drawRightString(PAGE_W - MARGIN, 0.22*cm, f"Page {doc.page}")
    canvas.restoreState()

doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
size = os.path.getsize(out_path)
print(f"Saved: {out_path}  ({size:,} bytes)")
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1

Editing File

~/anaesthesia-checklist/build_bain_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_bain_pdf.py'
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1

Editing File

~/anaesthesia-checklist/build_bain_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_bain_pdf.py'
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1

Reading File
The call is passing 5 positional args (self, c, x1, y, x2) + color as a 6th positional = 7 total including self. The function signature has arrow_col as 5th arg (index 4 after self). The issue is the call passes it as the 5th positional (after x1, y, x2 which are args 2,3,4) - that's actually correct (5 args + self = 6). Let me check the actual signature vs call more carefully:
Running Command

grep -n '_arrow_h' /tmp/workspace/anaesthesia-checklist/build_bain_pdf.py | head -20

Line 229 still uses col=AMBER which doesn't match the parameter name arrow_col. Fix both calls:
Running Command

sed -i 's/self._arrow_h(c, tube_x1 + 0.5\*cm, y_inner, tube_x2 - 0.3\*cm, y_inner, col=AMBER)/self._arrow_h(c, tube_x1 + 0.5*cm, y_inner, tube_x2 - 0.3*cm, y_inner, AMBER)/' /tmp/workspace/anaesthesia-checklist/build_bain_pdf.py # Also check for any FGFFlowchart _arrow_h issues grep -n 'col=' /tmp/workspace/anaesthesia-checklist/build_bain_pdf.py

Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1

Reading File
The call is passing: c, tube_x2 - 0.5*cm, y_outer + 0.3*cm, tube_x1 + 0.8*cm, y_outer + 0.3*cm, colors.HexColor(...) - that's 6 args + self = 7. The issue is there are TWO y arguments (y_outer + 0.3*cm appears as both arg 3 and 5). The signature is (self, c, x1, y, x2, arrow_col) - but I'm passing x1, y1, x2, y2, color which is 5 values. The _arrow_h only takes x1, y, x2 (single y). Need to fix the call to drop the second y:
Editing File

~/anaesthesia-checklist/build_bain_pdf.py

Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_bain_pdf.py'
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1

Running Command

# Fix all remaining _arrow_h calls with 5 non-self args by removing duplicate y arg sed -i 's/self._arrow_h(c, tube_x1 + 0.5\*cm, y_inner, tube_x2 - 0.3\*cm, y_inner, AMBER)/self._arrow_h(c, tube_x1 + 0.5*cm, y_inner, tube_x2 - 0.3*cm, AMBER)/' /tmp/workspace/anaesthesia-checklist/build_bain_pdf.py cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1

Your PDF is ready:

Bain Circuit MD Anaesthesia

PDF Document · PDF


Bain's Circuit - Complete Short Note

Definition

The Bain circuit (Bain & Spoerel, 1972) is a coaxial modification of the Mapleson D system. A narrow inner tube carries fresh gas (FGF) from the machine end toward the patient end, enclosed within an outer corrugated tube that carries exhaled gases back toward the APL valve near the reservoir bag.

Circuit Schematic

[Reservoir Bag] ←← OUTER TUBE (exhaled gas) ←← [Patient / ETT]
[APL Valve]      →→ INNER TUBE (fresh gas)   →→  (FGF exits here)
                    ↕ Countercurrent heat exchange

FGF Requirements

ModeFGF RequiredPractical (70 kg adult)
Spontaneous200-300 mL/kg/min (~2.5 × MV)~14 L/min
Controlled (IPPV)70 mL/kg/min (~1 × MV)~5 L/min

Advantages

  1. Lightweight, simple, disposable - no CO₂ absorber or unidirectional valves
  2. Countercurrent heat & humidity exchange - exhaled gases in outer tube warm the FGF
  3. Easy scavenging - APL valve at machine end, away from the surgical field
  4. Low resistance - suitable for spontaneous ventilation in adults
  5. Versatile - both spontaneous AND controlled ventilation
  6. Remote/difficult locations - head & neck, ENT, dental, transport
  7. No CO₂ absorber needed - simpler setup and maintenance
  8. Adjustable length - outer hose available in multiple lengths

Disadvantages

  1. High FGF required for spontaneous ventilation - agent waste, OR pollution
  2. Kinking/disconnection of inner tube - main hazard; may be unrecognised → hypercapnia, hypoxaemia
  3. Not suitable for paediatrics - Jackson-Rees preferred for small tidal volumes
  4. Rebreathing risk if FGF is inadequate (no CO₂ absorber safety net)
  5. Filter obstruction - antimicrobial filter between circuit and ETT raises resistance, mimics bronchospasm
  6. Less efficient for heat/humidity vs circle system at low flow

Pethick Test (Inner Tube Integrity)

StepActionResult
1Occlude patient endCircuit sealed
2Flush O₂ at high flowReservoir bag fills
3Release patient endVenturi effect created
PASSInner tube intactBag deflates (Venturi suction) ✓
FAILInner tube leakingBag stays inflated - gas escaping into expiratory limb ✗

Modifications of Bain Circuit

ModificationDescription
Paediatric BainMiniaturised; lower dead space; for 10-20 kg children
COVID-19 modificationBacterial/viral filter added between circuit and ETT; increases resistance
Extended length versionsDifferent hose lengths for various surgical positions

Other Coaxial Circuits

CircuitMaplesonInner TubeOuter TubeBest ForKey Point
Bain (1972)DFGF → patientExhaled → APLControlled ventilationFGF = 70 mL/kg/min (IPPV)
Lack (1976)AExhaled → APLFGF → patientSpontaneous ventilationFGF = 1× MV (most efficient)
Humphrey ADE (1983)A/D/E switchableConfigurableConfigurableBoth; adults + paediatricLever switches between Mapleson A, D, E modes
Enclosed MagillAExhaled gasFresh gasSpontaneousCoaxial APL at machine end
Miniaturised coaxialD/F variantsFresh gasExhaled gasPaediatric (10-20 kg)Reduced dead space

Humphrey ADE Block - Special Note

  • Lever UP = Mapleson A mode → spontaneous ventilation; FGF = MV
  • Lever DOWN = Mapleson D/E mode → controlled ventilation
  • Single unit replaces multiple circuits; works for adult and paediatric patients

★ KEY POINTS (Exam)

  1. Coaxial Mapleson D - inner tube = FGF to patient; outer = exhaled to APL valve
  2. FGF: Spontaneous = 2.5× MV (200-300 mL/kg/min); IPPV = 1× MV (70 mL/kg/min)
  3. APL valve at machine end (not patient end) → easy scavenging
  4. Main hazard: inner tube kink/disconnection → hypercapnia/hypoxaemia (may be silent)
  5. Pethick test: O₂ flush → occlude patient end → release → bag deflates = PASS
  6. Outer tube must be transparent for visual inspection of inner tube
  7. Lack circuit = coaxial Mapleson A; best for spontaneous; FGF = MV (most efficient Mapleson for spontaneous)
  8. Humphrey ADE: lever-switchable A/D/E modes in one unit
  9. Circle system > Bain for heat/humidity and FGF economy; Bain > Circle for portability/simplicity
  10. Not ideal for children - Ayre's T-piece / Jackson-Rees preferred

Sources: Miller's Anesthesia 10e pp.2350-2354 | Barash Clinical Anesthesia 9e pp.2012-2013 | Morgan & Mikhail's Clinical Anesthesiology 7e pp.86-88 | Bain & Spoerel, Can Anaesth Soc J 1972 & 1973

paediatric breathing circuits for md anaesthesia exam

Paediatric breathing circuits are designed for low resistance, minimal dead space, and lightweight connections - key differences from adult circuits driven by children's small tidal volumes and high respiratory rates.
Searching sources...
Finding Sources
Finding Sources
Reading File
Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Reading File
Reading File
Excellent - rich content gathered. Now build the comprehensive PDF.
Writing File

~/anaesthesia-checklist/build_paed_circuits.py

"""
Paediatric Breathing Circuits – MD Anaesthesia Exam PDF
With diagrams, flowcharts, comparison tables, FGF requirements, key points
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable
)
from reportlab.platypus.flowables import Flowable
import os
from io import BytesIO

# ── Palette ───────────────────────────────────────────────────────────────────
DARK_BLUE  = colors.HexColor('#1A3A5C')
MED_BLUE   = colors.HexColor('#2E6DA8')
LIGHT_BLUE = colors.HexColor('#D6E8F7')
TEAL       = colors.HexColor('#007A7C')
TEAL_BG    = colors.HexColor('#D0F0F0')
GREEN      = colors.HexColor('#2A7A4B')
GREEN_BG   = colors.HexColor('#D4EDDA')
AMBER      = colors.HexColor('#E88B00')
AMBER_BG   = colors.HexColor('#FFF3CD')
RED        = colors.HexColor('#C0392B')
RED_BG     = colors.HexColor('#F8D7DA')
PURPLE     = colors.HexColor('#6A3D8F')
PURPLE_BG  = colors.HexColor('#EDE7F6')
ORANGE     = colors.HexColor('#D35400')
ORANGE_BG  = colors.HexColor('#FDEBD0')
LIGHT_GRAY = colors.HexColor('#F4F6F9')
MID_GRAY   = colors.HexColor('#88A0B4')
DARK_GRAY  = colors.HexColor('#333333')
WHITE      = colors.white

PAGE_W, PAGE_H = A4
MARGIN = 1.7 * cm

# ── Doc ───────────────────────────────────────────────────────────────────────
out_path = "/tmp/workspace/anaesthesia-checklist/Paediatric_Breathing_Circuits_MD_Anaesthesia.pdf"
doc = SimpleDocTemplate(
    out_path, pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=0.9*cm, bottomMargin=1.5*cm,
    title="Paediatric Breathing Circuits – MD Anaesthesia",
)

styles = getSampleStyleSheet()
def S(name, **kw): return ParagraphStyle(name, parent=styles['Normal'], **kw)
body      = S('body', fontSize=9.5, textColor=DARK_GRAY, leading=14, alignment=TA_JUSTIFY, fontName='Helvetica', spaceAfter=2)
body_bold = S('bb', fontSize=9.5, textColor=DARK_GRAY, leading=14, fontName='Helvetica-Bold')
bullet_s  = S('bul', fontSize=9.5, textColor=DARK_GRAY, leading=14, fontName='Helvetica', leftIndent=12, spaceAfter=1)
sub_bul   = S('sbul', fontSize=9, textColor=DARK_GRAY, leading=13, fontName='Helvetica', leftIndent=24, spaceAfter=1)
ref_style = S('ref', fontSize=7.5, textColor=MID_GRAY, leading=10, fontName='Helvetica-Oblique')
small_c   = S('sc', fontSize=8, textColor=MID_GRAY, alignment=TA_CENTER, fontName='Helvetica-Oblique', leading=10)
def B(t): return Paragraph(f'<bullet>\u2022</bullet> {t}', bullet_s)
def SB(t): return Paragraph(f'  \u2013 {t}', sub_bul)

# ── Custom Flowables ───────────────────────────────────────────────────────────
class TitleBar(Flowable):
    def __init__(self, l1, l2, sub):
        self.l1, self.l2, self.sub = l1, l2, sub
        self.h = 1.9*cm
        Flowable.__init__(self)
    def wrap(self, aW, aH): self._w = aW; return (aW, self.h)
    def draw(self):
        c = self.canv; w, h = self._w, self.h
        c.setFillColor(DARK_BLUE); c.rect(0,0,w,h,fill=1,stroke=0)
        c.setFillColor(AMBER);     c.rect(0,0,5,h,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',15); c.drawString(12, h-22, self.l1)
        c.setFont('Helvetica-Bold',11); c.drawString(12, h-38, self.l2)
        c.setFillColor(colors.HexColor('#AACCEE')); c.setFont('Helvetica-Oblique',8.5)
        c.drawString(12, 6, self.sub)

class SecHdr(Flowable):
    def __init__(self, text, bg=MED_BLUE, h=0.5*cm):
        self.text, self.bg, self.h = text, bg, h; Flowable.__init__(self)
    def wrap(self, aW, aH): self._w = aW; return (aW, self.h)
    def draw(self):
        c = self.canv
        c.setFillColor(self.bg); c.rect(0,0,self._w,self.h,fill=1,stroke=0)
        c.setFillColor(AMBER); c.rect(0,0,3.5,self.h,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',10)
        c.drawString(10,(self.h-10)/2+1, self.text)

class KeyBox(Flowable):
    def __init__(self, items, bg=AMBER_BG, bdr=AMBER):
        self.items, self.bg, self.bdr = items, bg, bdr; Flowable.__init__(self)
    def wrap(self, aW, aH): self._w=aW; self._h=8+len(self.items)*15+8; return(self._w,self._h)
    def draw(self):
        c=self.canv
        c.setFillColor(self.bg); c.setStrokeColor(self.bdr); c.setLineWidth(1.5)
        c.roundRect(0,0,self._w,self._h,4,fill=1,stroke=1)
        c.setFillColor(self.bdr); c.rect(0,0,4,self._h,fill=1,stroke=0)
        for i,item in enumerate(reversed(self.items)):
            y=8+i*15
            c.setFillColor(self.bdr); c.setFont('Helvetica-Bold',9); c.drawString(10,y,'\u2605')
            c.setFillColor(DARK_GRAY); c.setFont('Helvetica',9); c.drawString(22,y,item)

# ────────────────────────────────────────────────────────────────────────────
# CIRCUIT DIAGRAM FLOWABLE
# ────────────────────────────────────────────────────────────────────────────
class CircuitDiagram(Flowable):
    """Draws schematic diagrams of 4 key paediatric circuits side by side."""
    def __init__(self): Flowable.__init__(self)
    def wrap(self, aW, aH): self._w=aW; self._h=7.2*cm; return(self._w,self._h)

    def _box(self,c,x,y,w,h,fill,text,fs=7.5,tc=WHITE,bold=False):
        c.setFillColor(fill); c.setStrokeColor(MID_GRAY); c.setLineWidth(0.5)
        c.roundRect(x,y,w,h,2,fill=1,stroke=1)
        fn='Helvetica-Bold' if bold else 'Helvetica'
        c.setFillColor(tc); c.setFont(fn,fs)
        lines=text.split('\n'); lh=fs+1.5
        sy=y+h/2+(len(lines)-1)*lh/2-1
        for ln in lines:
            tw=c.stringWidth(ln,fn,fs)
            c.drawString(x+(w-tw)/2,sy,ln); sy-=lh

    def _arr(self,c,x1,y1,x2,y2,col=MID_GRAY):
        c.setStrokeColor(col); c.setLineWidth(1)
        c.line(x1,y1,x2,y2)
        p=c.beginPath()
        dx,dy=x2-x1,y2-y1
        import math
        L=math.sqrt(dx*dx+dy*dy) or 1
        ux,uy=dx/L,dy/L; px,py=-uy,ux
        p.moveTo(x2-ux*6+px*3,y2-uy*6+py*3)
        p.lineTo(x2-ux*6-px*3,y2-uy*6-py*3)
        p.lineTo(x2,y2); p.close()
        c.setFillColor(col); c.drawPath(p,fill=1,stroke=0)

    def draw(self):
        c=self.canv; fw,fh=self._w,self._h
        c.setFillColor(LIGHT_GRAY); c.rect(0,0,fw,fh,fill=1,stroke=0)
        c.setFillColor(DARK_BLUE); c.rect(0,fh-0.52*cm,fw,0.52*cm,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',9)
        t="PAEDIATRIC CIRCUIT SCHEMATICS – Mapleson E, F, Coaxial Modifications & Paediatric Circle"
        c.drawString((fw-c.stringWidth(t,'Helvetica-Bold',9))/2,fh-0.37*cm,t)

        col_w=fw/4; bw=2.6*cm; bh=0.55*cm

        # ── Circuit 1: Ayre's T-piece (Mapleson E) ────────────────────────────
        cx1=col_w*0   # left edge of column 1
        c.setFillColor(TEAL); c.rect(cx1,fh-1.05*cm,col_w,0.45*cm,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',8)
        t="AYRE'S T-PIECE (Mapleson E)"
        c.drawString(cx1+(col_w-c.stringWidth(t,'Helvetica-Bold',8))/2,fh-0.73*cm,t)

        # FGF inlet → patient → open expiratory limb
        cx=cx1+col_w/2
        self._box(c,cx-bw/2,fh-2.1*cm,bw,bh,TEAL,'PATIENT\n(ETT/Mask)',fs=7.5,bold=True)
        self._arr(c,cx,fh-2.1*cm,cx,fh-2.65*cm,col=AMBER)
        self._box(c,cx-bw/2,fh-3.2*cm,bw,bh,MED_BLUE,'FGF Inlet\n(at patient end)',fs=7,bold=False)
        # Expiratory limb going right
        self._arr(c,cx+bw/2,fh-1.82*cm,cx+bw/2+0.8*cm,fh-1.82*cm,col=colors.HexColor('#88AACC'))
        c.setFillColor(colors.HexColor('#88AACC')); c.setFont('Helvetica-Oblique',6.5)
        c.drawString(cx+bw/2+0.1*cm,fh-1.72*cm,'Exp. limb')
        c.drawString(cx+bw/2+0.1*cm,fh-1.85*cm,'(open end)')
        # Label
        c.setFillColor(RED_BG); c.roundRect(cx1+0.1*cm,0.1*cm,col_w-0.2*cm,0.8*cm,2,fill=1,stroke=0)
        c.setFillColor(RED); c.setFont('Helvetica-Bold',7)
        c.drawString(cx1+0.2*cm,0.62*cm,'NO reservoir bag')
        c.setFillColor(DARK_GRAY); c.setFont('Helvetica',7)
        c.drawString(cx1+0.2*cm,0.45*cm,'Spontaneous only')
        c.drawString(cx1+0.2*cm,0.28*cm,'FGF \u22652.5\u00d7MV')

        # ── Circuit 2: Jackson-Rees (Mapleson F) ──────────────────────────────
        cx2=col_w*1
        c.setFillColor(GREEN); c.rect(cx2,fh-1.05*cm,col_w,0.45*cm,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',8)
        t="JACKSON-REES (Mapleson F)"
        c.drawString(cx2+(col_w-c.stringWidth(t,'Helvetica-Bold',8))/2,fh-0.73*cm,t)
        cx=cx2+col_w/2
        self._box(c,cx-bw/2,fh-2.1*cm,bw,bh,GREEN,'PATIENT\n(ETT/Mask)',fs=7.5,bold=True)
        self._arr(c,cx,fh-2.1*cm,cx,fh-2.65*cm,col=AMBER)
        self._box(c,cx-bw/2,fh-3.2*cm,bw,bh,MED_BLUE,'FGF Inlet\n(at patient end)',fs=7)
        # Expiratory limb + bag
        self._arr(c,cx+bw/2,fh-1.82*cm,cx+bw/2+0.5*cm,fh-1.82*cm,col=colors.HexColor('#88AACC'))
        self._box(c,cx+bw/2+0.5*cm,fh-2.1*cm,1.0*cm,bh,AMBER,'Bag\n+valve',fs=6.5,bold=True)
        c.setFillColor(GREEN_BG); c.roundRect(cx2+0.1*cm,0.1*cm,col_w-0.2*cm,0.8*cm,2,fill=1,stroke=0)
        c.setFillColor(GREEN); c.setFont('Helvetica-Bold',7)
        c.drawString(cx2+0.2*cm,0.62*cm,'Bag + APL valve at tail')
        c.setFillColor(DARK_GRAY); c.setFont('Helvetica',7)
        c.drawString(cx2+0.2*cm,0.45*cm,'Spontaneous + controlled')
        c.drawString(cx2+0.2*cm,0.28*cm,'FGF \u22652.5\u00d7MV')

        # ── Circuit 3: Bain (Mapleson D coaxial) ─────────────────────────────
        cx3=col_w*2
        c.setFillColor(AMBER); c.rect(cx3,fh-1.05*cm,col_w,0.45*cm,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',8)
        t="BAIN CIRCUIT (Mapleson D)"
        c.drawString(cx3+(col_w-c.stringWidth(t,'Helvetica-Bold',8))/2,fh-0.73*cm,t)
        cx=cx3+col_w/2
        self._box(c,cx-bw/2,fh-2.1*cm,bw,bh,colors.HexColor('#996600'),'PATIENT\n(FGF exits)',fs=7.5,bold=True)
        # Coaxial tube
        c.setStrokeColor(MED_BLUE); c.setLineWidth(6)
        c.line(cx3+0.3*cm,fh-2.5*cm,cx-bw/2,fh-2.5*cm)
        c.setFillColor(WHITE); c.setFont('Helvetica',6.5)
        c.drawString(cx3+0.35*cm,fh-2.44*cm,'Outer (expired)')
        c.setStrokeColor(AMBER); c.setDash([3,2]); c.setLineWidth(1)
        c.line(cx3+0.3*cm,fh-2.5*cm,cx-bw/2,fh-2.5*cm)
        c.setDash([])
        c.setFillColor(AMBER); c.setFont('Helvetica',6.5)
        c.drawString(cx3+0.35*cm,fh-2.65*cm,'Inner (FGF\u2192patient)')
        self._box(c,cx3+0.05*cm,fh-3.2*cm,0.9*cm,bh,DARK_BLUE,'Bag\n+APL',fs=6.5,bold=True)
        c.setFillColor(AMBER_BG); c.roundRect(cx3+0.1*cm,0.1*cm,col_w-0.2*cm,0.8*cm,2,fill=1,stroke=0)
        c.setFillColor(AMBER); c.setFont('Helvetica-Bold',7)
        c.drawString(cx3+0.2*cm,0.62*cm,'APL at machine end')
        c.setFillColor(DARK_GRAY); c.setFont('Helvetica',7)
        c.drawString(cx3+0.2*cm,0.45*cm,'Adults > 20 kg preferred')
        c.drawString(cx3+0.2*cm,0.28*cm,'FGF: 70 mL/kg/min (IPPV)')

        # ── Circuit 4: Paediatric Circle System ──────────────────────────────
        cx4=col_w*3
        c.setFillColor(PURPLE); c.rect(cx4,fh-1.05*cm,col_w,0.45*cm,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',8)
        t="PAEDIATRIC CIRCLE"
        c.drawString(cx4+(col_w-c.stringWidth(t,'Helvetica-Bold',8))/2,fh-0.73*cm,t)
        cx=cx4+col_w/2
        self._box(c,cx-bw/2,fh-2.1*cm,bw,bh,PURPLE,'PATIENT',fs=7.5,bold=True)
        self._arr(c,cx-bw/2,fh-1.82*cm,cx-bw/2-0.5*cm,fh-2.5*cm,col=GREEN)
        c.setFillColor(GREEN); c.setFont('Helvetica',6.5)
        c.drawString(cx4+0.1*cm,fh-2.6*cm,'Insp. valve')
        self._arr(c,cx+bw/2,fh-1.82*cm,cx+bw/2+0.5*cm,fh-2.5*cm,col=colors.HexColor('#88AACC'))
        c.setFillColor(colors.HexColor('#88AACC')); c.setFont('Helvetica',6.5)
        c.drawString(cx+bw/2+0.05*cm,fh-2.6*cm,'Exp. valve')
        self._box(c,cx-bw/2,fh-3.25*cm,bw,bh,colors.HexColor('#4A1E7A'),'CO\u2082 Absorber\n+ Low FGF',fs=7)
        c.setFillColor(PURPLE_BG); c.roundRect(cx4+0.1*cm,0.1*cm,col_w-0.2*cm,0.8*cm,2,fill=1,stroke=0)
        c.setFillColor(PURPLE); c.setFont('Helvetica-Bold',7)
        c.drawString(cx4+0.2*cm,0.62*cm,'Low FGF possible')
        c.setFillColor(DARK_GRAY); c.setFont('Helvetica',7)
        c.drawString(cx4+0.2*cm,0.45*cm,'Heat/humidity conserved')
        c.drawString(cx4+0.2*cm,0.28*cm,'FGF: 200 mL/min (low flow)')


# ────────────────────────────────────────────────────────────────────────────
# FGF FLOWCHART
# ────────────────────────────────────────────────────────────────────────────
class FGFChart(Flowable):
    def __init__(self): Flowable.__init__(self)
    def wrap(self,aW,aH): self._w=aW; self._h=4.5*cm; return(self._w,self._h)

    def _box(self,c,x,y,w,h,fill,text,fs=8,tc=WHITE,bold=True):
        c.setFillColor(fill); c.setStrokeColor(MID_GRAY); c.setLineWidth(0.5)
        c.roundRect(x,y,w,h,3,fill=1,stroke=1)
        fn='Helvetica-Bold' if bold else 'Helvetica'
        c.setFillColor(tc); c.setFont(fn,fs)
        lines=text.split('\n'); lh=fs+2
        sy=y+h/2+(len(lines)-1)*lh/2-1
        for ln in lines:
            tw=c.stringWidth(ln,fn,fs)
            c.drawString(x+(w-tw)/2,sy,ln); sy-=lh

    def _diam(self,c,cx,cy,w,h,fill,text,fs=8):
        p=c.beginPath()
        p.moveTo(cx,cy+h/2); p.lineTo(cx+w/2,cy)
        p.lineTo(cx,cy-h/2); p.lineTo(cx-w/2,cy); p.close()
        c.setFillColor(fill); c.setStrokeColor(DARK_BLUE); c.setLineWidth(1)
        c.drawPath(p,fill=1,stroke=1)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',fs)
        lines=text.split('\n'); lh=fs+1.5
        sy=cy+(len(lines)-1)*lh/2
        for ln in lines:
            tw=c.stringWidth(ln,'Helvetica-Bold',fs)
            c.drawString(cx-tw/2,sy,ln); sy-=lh

    def draw(self):
        c=self.canv; fw,fh=self._w,self._h
        c.setFillColor(LIGHT_GRAY); c.rect(0,0,fw,fh,fill=1,stroke=0)
        c.setFillColor(TEAL); c.rect(0,fh-0.5*cm,fw,0.5*cm,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',9)
        t="CIRCUIT SELECTION FLOWCHART – Based on Age & Weight"
        c.drawString((fw-c.stringWidth(t,'Helvetica-Bold',9))/2,fh-0.35*cm,t)

        cx=fw/2; bw=3.5*cm; bh=0.55*cm
        # Start: patient age
        self._diam(c,cx,fh-1.2*cm,4.5*cm,0.9*cm,AMBER,'Patient age / weight?',fs=9)
        # 3 branches
        # Neonate/infant <10kg
        self._arr_h(c,cx-(4.5*cm)/2,fh-1.2*cm,1.5*cm,fh-1.2*cm,col=TEAL)
        c.setFillColor(TEAL); c.setFont('Helvetica-Bold',7.5)
        c.drawString(0.2*cm,fh-1.1*cm,'Neonate / Infant')
        c.drawString(0.2*cm,fh-1.25*cm,'< 10 kg')
        self._box(c,0.15*cm,fh-2.25*cm,bw,bh,TEAL,'AYRE\'S T-PIECE\nor JACKSON-REES',fs=8,bold=True)

        # Child 10-20kg
        c.setStrokeColor(MID_GRAY); c.setLineWidth(1)
        c.line(cx,fh-1.65*cm,cx,fh-2.1*cm)
        p=c.beginPath(); p.moveTo(cx-3,fh-2.1*cm-6); p.lineTo(cx+3,fh-2.1*cm-6); p.lineTo(cx,fh-2.1*cm); p.close()
        c.setFillColor(MID_GRAY); c.drawPath(p,fill=1,stroke=0)
        c.setFillColor(GREEN); c.setFont('Helvetica-Bold',7.5)
        c.drawString(cx-1.7*cm,fh-1.75*cm,'10–20 kg')
        self._box(c,cx-bw/2,fh-2.75*cm,bw,bh,GREEN,'BAIN / JACKSON-REES\nor Paed. Circle',fs=8)

        # Adult >20kg → adult/paed circle
        self._arr_h(c,cx+(4.5*cm)/2,fh-1.2*cm,fw-1.5*cm,fh-1.2*cm,col=PURPLE)
        c.setFillColor(PURPLE); c.setFont('Helvetica-Bold',7.5)
        c.drawString(fw-3.5*cm,fh-1.1*cm,'> 20 kg')
        self._box(c,fw-3.6*cm,fh-2.25*cm,bw,bh,PURPLE,'PAEDIATRIC CIRCLE\nor Bain Circuit',fs=8)

        # FGF note
        c.setFillColor(AMBER_BG); c.setStrokeColor(AMBER); c.setLineWidth(0.8)
        c.roundRect(0.2*cm,0.1*cm,fw-0.4*cm,0.55*cm,3,fill=1,stroke=1)
        c.setFillColor(DARK_GRAY); c.setFont('Helvetica',8)
        t=('FGF guide: Neonates/T-piece: 2.5\u00d7MV  |  Jackson-Rees: 2.5\u00d7MV  |  '
           'Bain (IPPV): 70 mL/kg/min  |  Circle (low-flow): 200 mL/min')
        c.drawString((fw-c.stringWidth(t,'Helvetica',8))/2, 0.22*cm, t)

    def _arr_h(self,c,x1,y,x2,y2,col=MID_GRAY):
        c.setStrokeColor(col); c.setLineWidth(1.2)
        c.line(x1,y,x2,y2)
        p=c.beginPath()
        if x2>x1:
            p.moveTo(x2-6,y2-3); p.lineTo(x2-6,y2+3); p.lineTo(x2,y2); p.close()
        else:
            p.moveTo(x2+6,y2-3); p.lineTo(x2+6,y2+3); p.lineTo(x2,y2); p.close()
        c.setFillColor(col); c.drawPath(p,fill=1,stroke=0)
    def _arr_v(self,c,x,y1,y2,col=MID_GRAY):
        self._arr_h(c,x,y1,x,y2,col=col)


# ── Build content ─────────────────────────────────────────────────────────────
story = []

story.append(TitleBar(
    "PAEDIATRIC BREATHING CIRCUITS",
    "Classification · Physiology · FGF Requirements · Comparison · Key Points",
    "MD Anaesthesia Exam Note  |  Sources: Miller 10e, Barash 9e, Morgan & Mikhail 7e"))
story.append(Spacer(1,3*mm))

# ── 1. Why paediatric circuits differ ─────────────────────────────────────────
story.append(SecHdr("1.  WHY PAEDIATRIC CIRCUITS DIFFER FROM ADULT CIRCUITS", bg=MED_BLUE))
story.append(Spacer(1,2*mm))
diff_data = [
    [Paragraph('<b>Parameter</b>',body_bold), Paragraph('<b>Neonate / Infant</b>',body_bold),
     Paragraph('<b>Adult</b>',body_bold), Paragraph('<b>Clinical Implication</b>',body_bold)],
    ['Tidal volume', '6–8 mL/kg (~15–25 mL)', '6–8 mL/kg (~500 mL)',
     'Apparatus dead space must be <30% of TV → minimal dead space circuits needed'],
    ['Respiratory rate', '30–60 breaths/min', '12–16 breaths/min',
     'Higher FGF per breath required; rapid change in gas concentration desired'],
    ['Airway resistance', 'High (narrow airways)', 'Lower',
     'Circuit resistance must be minimal; no unidirectional valves for neonates'],
    ['Circuit compliance', 'Small TV absorbed by stiff tubing',
     'Less impact per breath', 'Lightweight low-compliance tubing preferred'],
    ['Heat/humidity loss', 'Major concern (large BSA:weight)',
     'Less critical', 'Circuits should minimise heat loss; HME filters in circuit'],
    ['O₂ consumption', '6–8 mL/kg/min (3× adult)', '3–4 mL/kg/min',
     'Higher FiO₂ demand; importance of adequate FGF at all times'],
]
dt = Table(diff_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.18,0.20,0.16,0.46]])
dt.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),DARK_BLUE), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[LIGHT_BLUE,WHITE,LIGHT_BLUE,WHITE,LIGHT_BLUE,WHITE]),
    ('ROWHEIGHT',(0,0),(-1,-1),0.6*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(2,-1),'CENTER'), ('ALIGN',(3,0),(3,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),4),
]))
story.append(dt)
story.append(Spacer(1,3*mm))

# ── 2. Circuit Diagrams ───────────────────────────────────────────────────────
story.append(SecHdr("2.  CIRCUIT SCHEMATICS", bg=TEAL))
story.append(Spacer(1,2*mm))
story.append(CircuitDiagram())
story.append(Spacer(1,3*mm))

# ── 3. Ayre's T-piece ─────────────────────────────────────────────────────────
story.append(SecHdr("3.  AYRE'S T-PIECE (Mapleson E)", bg=TEAL))
story.append(Spacer(1,2*mm))
story.append(Paragraph(
    "<b>Described by Phillip Ayre (1937)</b> to reduce work of breathing in paediatric patients "
    "undergoing neurosurgical and cleft palate procedures. The T-piece is the "
    "<b>only Mapleson circuit without a breathing bag</b> (Mapleson E).", body))
story.append(Spacer(1,2*mm))
ayre_data = [
    [Paragraph('<b>Feature</b>',body_bold), Paragraph('<b>Details</b>',body_bold)],
    ['Components', '3-way T-connector + FGF inlet + expiratory limb (open end)'],
    ['FGF inlet', 'Near patient connection (T-junction)'],
    ['Expiratory limb', 'Open-ended tube of adequate length to prevent rebreathing\n(volume ≥ patient tidal volume)'],
    ['Reservoir', 'None (Mapleson E is the only one without a bag)'],
    ['APL valve', 'None – open end ± thumb occlusion for IPPV'],
    ['FGF required', '2.5–3× minute ventilation to prevent rebreathing\n(e.g., 3 kg infant: ~500 mL/min × 2.5 = 1250 mL/min)'],
    ['Ventilation mode', 'SPONTANEOUS ONLY (cannot perform controlled ventilation without\noccluding expiratory limb – risk of barotrauma)'],
    ['Best for', 'Neonates < 10 kg; head & neck surgery; resource-limited settings'],
    ['Advantages', '• No valves → minimal resistance\n• Lightweight, simple, disposable\n• Rapid change in FiO₂/anaesthetic concentration\n• No dead space from valves'],
    ['Disadvantages', '• Cannot perform reliable IPPV\n• Scavenging difficult (open end)\n• High FGF required\n• No heat/humidity conservation'],
]
at = Table(ayre_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.22,0.78]])
at.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),TEAL), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTNAME',(0,1),(0,-1),'Helvetica-Bold'), ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[TEAL_BG,WHITE,TEAL_BG,WHITE,TEAL_BG,WHITE,TEAL_BG,WHITE,TEAL_BG,WHITE]),
    ('ROWHEIGHT',(0,0),(-1,-1),0.55*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'CENTER'), ('ALIGN',(1,0),(1,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(at)
story.append(Spacer(1,3*mm))

# ── 4. Jackson-Rees ────────────────────────────────────────────────────────────
story.append(SecHdr("4.  JACKSON-REES CIRCUIT (Mapleson F)", bg=GREEN))
story.append(Spacer(1,2*mm))
story.append(Paragraph(
    "<b>Described by Gordon Jackson Rees (1950)</b> as a modification of Ayre's T-piece. "
    "Adds a <b>double-ended breathing bag with a valve at its tail</b> to the expiratory limb, "
    "enabling controlled ventilation. This is the most commonly used paediatric circuit "
    "for neonates and infants worldwide.", body))
story.append(Spacer(1,2*mm))
jr_data = [
    [Paragraph('<b>Feature</b>',body_bold), Paragraph('<b>Details</b>',body_bold)],
    ['Components', 'T-piece + FGF inlet + open-ended bag with adjustable valve at tail'],
    ['FGF inlet', 'At patient end (near T-junction)'],
    ['Bag', 'Double-ended; open tail with valve – allows monitoring of ventilation\nby "feel of bag"'],
    ['APL valve', 'At tail of bag; open → spontaneous; partially closed → IPPV'],
    ['FGF required', '2.5× MV (spontaneous and controlled – similar efficiency to Mapleson D)'],
    ['Ventilation modes', 'BOTH spontaneous and controlled ventilation possible'],
    ['Best for', 'Neonates and infants < 10–20 kg; preferred over T-piece for versatility'],
    ['Advantages', '• Low resistance (no unidirectional valves)\n• Minimal dead space\n• "Feel" of ventilation through bag allows compliance/resistance monitoring\n• Both modes of ventilation\n• Lightweight and simple\n• Rapid change in anaesthetic concentration'],
    ['Disadvantages', '• High FGF needed (2.5× MV)\n• Scavenging difficult\n• No heat/humidity conservation\n• Less familiar to providers accustomed to circle systems\n• Less gastric insufflation risk than circle but still present'],
    ['vs Paediatric Circle', 'Jackson-Rees: lower resistance, higher FGF, less OR pollution\nCircle: more economical, better heat/humidity, same circuit for all ages'],
]
jt = Table(jr_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.22,0.78]])
jt.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),GREEN), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTNAME',(0,1),(0,-1),'Helvetica-Bold'), ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[GREEN_BG,WHITE]*5+[GREEN_BG]),
    ('ROWHEIGHT',(0,0),(-1,-1),0.62*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'CENTER'), ('ALIGN',(1,0),(1,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(jt)
story.append(Spacer(1,3*mm))

# ── 5. Paediatric Circle System ────────────────────────────────────────────────
story.append(SecHdr("5.  PAEDIATRIC CIRCLE SYSTEM", bg=PURPLE))
story.append(Spacer(1,2*mm))
story.append(Paragraph(
    "The adult circle system modified with <b>smaller volume absorber canisters, "
    "lightweight corrugated tubing, and low dead-space Y-pieces</b>. "
    "Increasingly used even for neonates as modern machines offer precise tidal volume "
    "delivery and the advantage of the <b>same circuit for all age groups</b>.", body))
story.append(Spacer(1,2*mm))
circ_data = [
    [Paragraph('<b>Feature</b>',body_bold), Paragraph('<b>Paediatric Circle</b>',body_bold),
     Paragraph('<b>Adult Circle</b>',body_bold)],
    ['Tubing diameter', '15 mm (paediatric)', '22 mm (adult)'],
    ['FGF (low-flow)', '200 mL/min (same principle)', '200–500 mL/min'],
    ['Dead space', 'Minimised Y-piece (<5 mL)', 'Standard Y-piece (~20 mL)'],
    ['CO₂ absorber', 'Small canister', 'Standard canister'],
    ['Advantages',
     '• Same circuit for all ages (>5–10 kg)\n• Low FGF → economy\n• Heat & humidity conserved\n• Reduced OR pollution\n• Accurate TV delivery',
     '• Same as above; designed for adults'],
    ['Disadvantages',
     '• More resistance than T-piece/JR\n• Larger compression volume\n• Slower gas change-over\n• Risk of CO₂ absorber bypass\n• Valve malfunction risk',
     '• Not suitable for neonates\n• Larger dead space'],
    ['Weight threshold', 'Often used >5–10 kg; some centres use from birth', '>20 kg standard'],
]
ct = Table(circ_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.20,0.46,0.34]])
ct.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),PURPLE), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTNAME',(0,1),(0,-1),'Helvetica-Bold'), ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[PURPLE_BG,WHITE,PURPLE_BG,WHITE,PURPLE_BG,WHITE,PURPLE_BG]),
    ('ROWHEIGHT',(0,0),(-1,-1),0.65*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'CENTER'), ('ALIGN',(1,0),(-1,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(ct)
story.append(Spacer(1,3*mm))

# ── 6. Bain in Paediatrics ─────────────────────────────────────────────────────
story.append(SecHdr("6.  BAIN CIRCUIT IN PAEDIATRICS", bg=AMBER))
story.append(Spacer(1,2*mm))
story.append(Paragraph(
    "The Bain circuit (coaxial Mapleson D) is a popular alternative for children "
    "<b>&gt; 20 kg</b> and is commonly used in paediatric ENT, dental, and head & neck surgery "
    "where the APL valve is away from the surgical field. Not ideal for neonates/infants "
    "due to relatively higher resistance.", body))
story.append(Spacer(1,2*mm))
story.append(B("<b>Weight guideline:</b> Most appropriate for children > 20 kg (some use from 10 kg)"))
story.append(B("<b>FGF controlled (IPPV):</b> 70 mL/kg/min (e.g., 20 kg → 1.4 L/min)"))
story.append(B("<b>FGF spontaneous:</b> 200–300 mL/kg/min (2.5× MV)"))
story.append(B("<b>Advantage:</b> APL valve at machine end → easy scavenging in head & neck surgery"))
story.append(B("<b>Hazard:</b> Inner tube kinking → hypercapnia; must perform Pethick test"))
story.append(Spacer(1,3*mm))

# ── 7. FGF Selection Flowchart ────────────────────────────────────────────────
story.append(SecHdr("7.  CIRCUIT SELECTION FLOWCHART – By Age & Weight", bg=TEAL))
story.append(Spacer(1,2*mm))
story.append(FGFChart())
story.append(Spacer(1,3*mm))

# ── 8. Comprehensive Comparison Table ─────────────────────────────────────────
story.append(SecHdr("8.  COMPREHENSIVE COMPARISON OF PAEDIATRIC BREATHING CIRCUITS", bg=DARK_BLUE))
story.append(Spacer(1,2*mm))
big_data = [
    [Paragraph('<b>Feature</b>',body_bold),
     Paragraph('<b>Ayre\'s T-piece\n(Mapleson E)</b>',body_bold),
     Paragraph('<b>Jackson-Rees\n(Mapleson F)</b>',body_bold),
     Paragraph('<b>Bain Circuit\n(Mapleson D)</b>',body_bold),
     Paragraph('<b>Paediatric\nCircle</b>',body_bold)],
    ['Mapleson class', 'E', 'F', 'D (coaxial)', 'N/A (semi-closed)'],
    ['Reservoir bag', 'None', 'Open-tail bag\n+ valve', 'Yes (at\nmachine end)', 'Yes'],
    ['APL valve', 'None\n(open end)', 'At bag tail', 'At machine end\n(near bag)', 'Standard'],
    ['FGF (spontaneous)', '2.5×MV', '2.5×MV', '200–300 mL/\nkg/min', '≥0.5–1×MV'],
    ['FGF (controlled)', 'Not possible\n(reliably)', '2.5×MV', '70 mL/kg/min', 'Low flow\n200 mL/min'],
    ['IPPV possible?', 'Limited\n(thumb technique)', 'Yes ✓', 'Yes ✓', 'Yes ✓'],
    ['Resistance', 'Very low ✓✓', 'Very low ✓✓', 'Low ✓', 'Moderate'],
    ['Dead space', 'Minimal ✓✓', 'Minimal ✓✓', 'Low ✓', 'Depends on\nY-piece size'],
    ['Heat/humidity', 'Poor', 'Poor', 'Partial\n(countercurrent)', 'Good ✓✓'],
    ['Scavenging', 'Difficult', 'Difficult', 'Easy ✓✓', 'Standard ✓'],
    ['OR pollution risk', 'High', 'High', 'Moderate', 'Low ✓✓'],
    ['Best age/weight', 'Neonates <10 kg', 'Neonates &\ninfants <20 kg', 'Children >20 kg\n(some: >10 kg)', '>5–10 kg\n(modern machines)'],
    ['Rebreathing risk', 'Yes (if FGF low)', 'Yes (if FGF low)', 'Yes (if FGF low)', 'Yes (if CO₂\nabsorbent exhausted)'],
    ['CO₂ absorber', 'No', 'No', 'No', 'Yes (required)'],
]
fcols=[0.18,0.18,0.19,0.20,0.25]
bt = Table(big_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in fcols])
bt.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),DARK_BLUE), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'),
    ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTNAME',(0,1),(0,-1),'Helvetica-Bold'),
    ('FONTSIZE',(0,0),(-1,-1),8),
    ('BACKGROUND',(1,1),(-1,1),TEAL_BG),
    ('BACKGROUND',(1,2),(-1,2),GREEN_BG),
    ('BACKGROUND',(1,3),(-1,3),AMBER_BG),
    ('BACKGROUND',(1,4),(-1,4),PURPLE_BG),
    ('ROWBACKGROUNDS',(0,5),(-1,-1),[WHITE,LIGHT_BLUE]*10),
    ('ROWHEIGHT',(0,0),(-1,-1),0.52*cm),
    ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'LEFT'), ('ALIGN',(1,0),(-1,-1),'CENTER'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),4),
]))
story.append(bt)
story.append(Spacer(1,3*mm))

# ── 9. FGF Formulae ────────────────────────────────────────────────────────────
story.append(SecHdr("9.  FRESH GAS FLOW FORMULAE – Quick Reference", bg=MED_BLUE))
story.append(Spacer(1,2*mm))
fgf_data = [
    [Paragraph('<b>Circuit</b>',body_bold), Paragraph('<b>Mode</b>',body_bold),
     Paragraph('<b>FGF Formula</b>',body_bold), Paragraph('<b>Example: 5 kg infant</b>',body_bold)],
    ["Ayre's T-piece", 'Spontaneous', '2.5–3× MV or\n200–300 mL/kg/min', 'MV~750 mL → FGF ≥1875 mL/min'],
    ['Jackson-Rees', 'Spontaneous', '2.5× MV', 'MV~750 mL → FGF ~1875 mL/min'],
    ['Jackson-Rees', 'Controlled (IPPV)', '2.5× MV\n(same as spontaneous)', 'FGF ~1875 mL/min'],
    ['Bain Circuit', 'Controlled (IPPV)', '70 mL/kg/min\n(≈1× MV)', '20 kg → 1400 mL/min'],
    ['Bain Circuit', 'Spontaneous', '200–300 mL/kg/min\n(≈2.5× MV)', '20 kg → 4000–6000 mL/min'],
    ['Paediatric Circle', 'Low-flow', '200 mL/min (maintenance)\n500 mL/min (wash-in)', 'Same for all weights >5 kg'],
    ['Paediatric Circle', 'Standard flow', '~1–2× MV during\nwash-in', '5 kg: MV~750→ FGF~1500 mL/min'],
]
ft = Table(fgf_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.18,0.14,0.34,0.34]])
ft.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),MED_BLUE), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[LIGHT_BLUE,WHITE]*4),
    ('ROWHEIGHT',(0,0),(-1,-1),0.58*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(-1,-1),'CENTER'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),4),
]))
story.append(ft)
story.append(Spacer(1,3*mm))

# ── 10. Special Considerations ──────────────────────────────────────────────────
story.append(SecHdr("10.  SPECIAL CONSIDERATIONS IN NEONATES & INFANTS", bg=RED))
story.append(Spacer(1,2*mm))
story.append(Paragraph("<b>Heat & Humidity:</b>", body_bold))
story.append(B("Neonates lose heat and humidity rapidly through the ETT"))
story.append(B("Use <b>low FGF</b> + neonatal <b>heat-moisture exchangers (HME)</b> in the circuit"))
story.append(B("Heated vaporisers previously used – now largely replaced by HME filters"))
story.append(B("HME dead space must be < 30% of tidal volume to avoid significant rebreathing"))
story.append(Spacer(1,2*mm))
story.append(Paragraph("<b>Oxygen in Neonates:</b>", body_bold))
story.append(B("Premature neonates: avoid prolonged 100% O₂ → risk of <b>retinopathy of prematurity (ROP)</b>"))
story.append(B("Air flowmeter is essential to allow O₂:air blending"))
story.append(B("Nitrous oxide avoided in bowel obstruction → use air + O₂"))
story.append(Spacer(1,2*mm))
story.append(Paragraph("<b>CO₂ Monitoring:</b>", body_bold))
story.append(B("Capnography trend useful even if absolute ETCO₂ ≠ PaCO₂ in neonates"))
story.append(B("Rebreathing indicated by rising baseline on capnography"))
story.append(Spacer(1,2*mm))
story.append(Paragraph("<b>Apparatus Dead Space:</b>", body_bold))
story.append(B("Must keep apparatus dead space <b>< 1 mL/kg</b> (ideally < 30% of TV)"))
story.append(B("Each connector, filter, HME adds to dead space – choose neonatal sizes"))
story.append(B("In a 1 kg neonate (TV ~7 mL): dead space must be < 2.1 mL"))
story.append(Spacer(1,3*mm))

# ── 11. Key Points ──────────────────────────────────────────────────────────────
story.append(SecHdr("\u2605  KEY POINTS SUMMARY  (MD Anaesthesia Exam)", bg=AMBER))
story.append(Spacer(1,2*mm))
story.append(KeyBox([
    "Ayre's T-piece = Mapleson E; NO reservoir bag; spontaneous ventilation only; <10 kg neonates",
    "Jackson-Rees = Mapleson F = modified Ayre's T-piece with open-tail bag + valve; both modes possible",
    "Jackson-Rees FGF: 2.5× MV (same for spontaneous AND controlled – unlike adult Mapleson D vs A difference)",
    "Bain circuit: coaxial Mapleson D; ideal >20 kg; FGF = 70 mL/kg/min (IPPV) / 200-300 mL/kg/min (spont.)",
    "Apparatus dead space must be <1 mL/kg (<30% tidal volume) – critical in neonates and infants",
    "Paediatric circle: lower FGF, better heat/humidity, same circuit for all ages – now preferred in many centres",
    "Neonates: avoid prolonged 100% O₂ (ROP risk); air flowmeter is ESSENTIAL on paediatric anaesthesia machine",
    "HME dead space can cause significant rebreathing in neonates – choose neonatal-size HME only",
    "Jackson-Rees: lower resistance & gastric insufflation risk vs circle; but higher FGF and poor scavenging",
    "Key exam: 'Most efficient paediatric circuit for spontaneous ventilation' = Mapleson A (adult) but in paeds = Mapleson F (JR)",
]))
story.append(Spacer(1,3*mm))

# ── References ──────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%",thickness=0.5,color=MID_GRAY,spaceAfter=3))
story.append(Paragraph(
    "<b>References:</b> "
    "Miller's Anesthesia 10e, Ch. 20, pp. 2350–2355  |  "
    "Barash Clinical Anesthesia 9e, Ch. 25, pp. 2012–2013 & Ch. 1, p. 350; Ch. 45, pp. 3618–3619  |  "
    "Morgan & Mikhail's Clinical Anesthesiology 7e, Ch. 3, pp. 86–88  |  "
    "Ayre P. Technique for pulmonary operations. Lancet 1937;1:1326  |  "
    "Jackson Rees G. Paediatric anaesthesia. Br J Anaesth 1950;22:48–49",
    ref_style))

# ── Build ────────────────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(colors.HexColor('#ECF0F5'))
    canvas.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
    canvas.setFont('Helvetica', 7.5); canvas.setFillColor(MID_GRAY)
    canvas.drawString(MARGIN, 0.22*cm, "Paediatric Breathing Circuits – MD Anaesthesia")
    canvas.drawRightString(PAGE_W-MARGIN, 0.22*cm, f"Page {doc.page}")
    canvas.restoreState()

doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"Saved: {out_path}  ({os.path.getsize(out_path):,} bytes)")
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_paed_circuits.py 2>&1

Your PDF is ready:

Paediatric Breathing Circuits MD Anaesthesia

PDF Document · PDF


Paediatric Breathing Circuits - Complete MD Anaesthesia Note


Why Paediatric Circuits Differ

ParameterNeonate/InfantClinical Impact
Tidal volume15–25 mL (6–8 mL/kg)Apparatus dead space must be <1 mL/kg
Respiratory rate30–60/minHigh FGF per minute needed
Airway resistanceHigh (narrow airways)No unidirectional valves for neonates
O₂ consumption6–8 mL/kg/min (3× adult)Adequate FGF always essential
Heat/humidity lossMajor (large BSA:weight ratio)HME filters mandatory; consider low FGF

1. Ayre's T-Piece (Mapleson E) — 1937

Structure: 3-way T-connector + FGF inlet (at patient end) + open-ended expiratory limb. No reservoir bag.
FGF: 2.5–3× minute ventilation (e.g., 3 kg infant: ~1.25–1.5 L/min)
VentilationSpontaneous ONLY (IPPV unreliable - risk of barotrauma from thumb occlusion)
Best forNeonates < 10 kg; resource-limited settings; head & neck
AdvantagesNo valves → minimal resistance; lightweight; rapid gas change-over
DisadvantagesNo IPPV; poor scavenging; no heat/humidity; high FGF

2. Jackson-Rees Circuit (Mapleson F) — 1950

Structure: Modified Ayre's T-piece + open-tail breathing bag with valve at expiratory limb end.
FGF: 2.5× MV for both spontaneous AND controlled ventilation
VentilationBoth spontaneous AND controlled ✓
Best forNeonates and infants < 10–20 kg; most popular paediatric circuit worldwide
AdvantagesLow resistance; minimal dead space; "feel" of ventilation through bag; both modes; rapid gas change
DisadvantagesHigh FGF; poor scavenging; no heat/humidity; less familiar to those trained on circle systems

3. Bain Circuit (Mapleson D - Coaxial) in Paediatrics

  • Best for children > 20 kg (some use from 10 kg)
  • FGF controlled (IPPV): 70 mL/kg/min (e.g., 20 kg → 1.4 L/min)
  • FGF spontaneous: 200–300 mL/kg/min
  • APL valve at machine end → easy scavenging - preferred for ENT/head & neck surgery
  • Hazard: inner tube kinking → hypercapnia; Pethick test mandatory

4. Paediatric Circle System

  • Modified adult circle with 15 mm tubing, small absorber canister, low dead-space Y-piece
  • FGF: 200 mL/min (low-flow maintenance); 500 mL/min wash-in
  • Increasingly used from 5–10 kg with modern precision ventilators
  • Advantages: same circuit for all ages, low FGF, heat/humidity conserved, low OR pollution
  • Disadvantages: more resistance than T-piece/Jackson-Rees, larger compression volume, slower gas change

FGF Quick Reference

CircuitSpontaneousControlled
Ayre's T-piece2.5× MVNot reliable
Jackson-Rees2.5× MV2.5× MV
Bain200–300 mL/kg/min70 mL/kg/min
Paediatric Circle≥ 0.5–1× MV200 mL/min (low flow)

Special Considerations - Neonates

  • Dead space limit: < 1 mL/kg (< 30% tidal volume) — HME must be neonatal-size
  • Heat/humidity: Low FGF + neonatal HME filters; heated humidifiers now rarely used
  • Oxygen: Air flowmeter essential; avoid prolonged 100% O₂ in premature neonates (ROP risk)
  • Capnography: ETCO₂ trend is reliable even if absolute values differ from PaCO₂

★ Key Points for Exam

  1. Ayre's T-piece = Mapleson E - no bag, spontaneous only, neonates < 10 kg
  2. Jackson-Rees = Mapleson F - T-piece + open-tail bag; both modes; FGF = 2.5× MV for both
  3. Jackson-Rees FGF is 2.5× MV for BOTH modes (unlike Mapleson A/D difference in adults)
  4. Apparatus dead space < 1 mL/kg - critical rule in paediatric anaesthesia
  5. Bain > 20 kg; JR/T-piece < 20 kg; Circle from 5 kg (approximate weight thresholds)
  6. Paediatric circle = same FGF economy as adults; increasing trend even in neonates
  7. Avoid prolonged 100% O₂ in premature neonates → ROP; must have air flowmeter
  8. HME dead space can cause significant rebreathing in neonates if adult-size HME used
  9. Poor scavenging is a disadvantage of T-piece and Jackson-Rees
  10. Jackson-Rees vs Circle: JR = lower resistance + gastric insufflation risk; Circle = lower FGF + better heat/humidity

Sources: Miller's Anesthesia 10e | Barash Clinical Anesthesia 9e | Morgan & Mikhail's 7e | Ayre 1937 | Jackson Rees 1950

humidification methods in anaesthesia 10 marks question

Humidification in anaesthesia is critical because bypassing the upper airway with an ETT eliminates the normal warming and humidification of inspired gases, leading to mucociliary dysfunction, airway desiccation, heat loss, and secretion retention.
Searching sources...
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
Finding Sources
I have comprehensive content from the textbooks. Now building the 10-mark PDF with full diagrams, tables, and flowcharts.
Writing File

~/anaesthesia-checklist/build_humidification_pdf.py

"""
Humidification Methods in Anaesthesia – MD Anaesthesia 10-Mark PDF
Comprehensive: physiology, definitions, methods, devices, comparisons, complications, key points
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable
)
from reportlab.platypus.flowables import Flowable
import os, math

# ── Palette ─────────────────────────────────────────────────────────────────
DARK_BLUE  = colors.HexColor('#1A3A5C')
MED_BLUE   = colors.HexColor('#2E6DA8')
LIGHT_BLUE = colors.HexColor('#D6E8F7')
TEAL       = colors.HexColor('#007A7C')
TEAL_BG    = colors.HexColor('#D0F0F0')
GREEN      = colors.HexColor('#2A7A4B')
GREEN_BG   = colors.HexColor('#D4EDDA')
AMBER      = colors.HexColor('#E88B00')
AMBER_BG   = colors.HexColor('#FFF3CD')
RED        = colors.HexColor('#C0392B')
RED_BG     = colors.HexColor('#F8D7DA')
PURPLE     = colors.HexColor('#6A3D8F')
PURPLE_BG  = colors.HexColor('#EDE7F6')
ORANGE     = colors.HexColor('#D35400')
ORANGE_BG  = colors.HexColor('#FDEBD0')
LIGHT_GRAY = colors.HexColor('#F4F6F9')
MID_GRAY   = colors.HexColor('#88A0B4')
DARK_GRAY  = colors.HexColor('#333333')
WHITE      = colors.white

PAGE_W, PAGE_H = A4
MARGIN = 1.7 * cm

out_path = "/tmp/workspace/anaesthesia-checklist/Humidification_Methods_Anaesthesia_MD.pdf"
doc = SimpleDocTemplate(
    out_path, pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=0.9*cm, bottomMargin=1.5*cm,
    title="Humidification Methods in Anaesthesia – MD Anaesthesia 10-Mark",
)

styles = getSampleStyleSheet()
def S(name, **kw): return ParagraphStyle(name, parent=styles['Normal'], **kw)
body      = S('body', fontSize=9.5, textColor=DARK_GRAY, leading=14, alignment=TA_JUSTIFY, fontName='Helvetica', spaceAfter=2)
body_bold = S('bb', fontSize=9.5, textColor=DARK_GRAY, leading=14, fontName='Helvetica-Bold')
bullet_s  = S('bul', fontSize=9.5, textColor=DARK_GRAY, leading=14, fontName='Helvetica', leftIndent=12, spaceAfter=1)
sub_bul   = S('sbul', fontSize=9, textColor=DARK_GRAY, leading=13, fontName='Helvetica', leftIndent=24, spaceAfter=1)
ref_style = S('ref', fontSize=7.5, textColor=MID_GRAY, leading=10, fontName='Helvetica-Oblique')
small_c   = S('sc', fontSize=8, textColor=MID_GRAY, alignment=TA_CENTER, fontName='Helvetica-Oblique', leading=10)
def B(t): return Paragraph(f'<bullet>\u2022</bullet> {t}', bullet_s)
def SB(t): return Paragraph(f'  \u2013 {t}', sub_bul)

# ── Custom Flowables ──────────────────────────────────────────────────────────
class TitleBar(Flowable):
    def __init__(self, l1, l2, sub):
        self.l1, self.l2, self.sub = l1, l2, sub; self.h = 2.0*cm; Flowable.__init__(self)
    def wrap(self, aW, aH): self._w = aW; return (aW, self.h)
    def draw(self):
        c = self.canv; w, h = self._w, self.h
        c.setFillColor(DARK_BLUE); c.rect(0,0,w,h,fill=1,stroke=0)
        c.setFillColor(AMBER);     c.rect(0,0,5,h,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',16); c.drawString(12, h-24, self.l1)
        c.setFont('Helvetica-Bold',11); c.drawString(12, h-40, self.l2)
        c.setFillColor(colors.HexColor('#AACCEE')); c.setFont('Helvetica-Oblique',8.5)
        c.drawString(12, 6, self.sub)

class SecHdr(Flowable):
    def __init__(self, text, bg=MED_BLUE, h=0.52*cm):
        self.text, self.bg, self.h = text, bg, h; Flowable.__init__(self)
    def wrap(self, aW, aH): self._w = aW; return (aW, self.h)
    def draw(self):
        c = self.canv
        c.setFillColor(self.bg); c.rect(0,0,self._w,self.h,fill=1,stroke=0)
        c.setFillColor(AMBER); c.rect(0,0,3.5,self.h,fill=1,stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',10)
        c.drawString(10,(self.h-10)/2+1, self.text)

class KeyBox(Flowable):
    def __init__(self, items, bg=AMBER_BG, bdr=AMBER):
        self.items, self.bg, self.bdr = items, bg, bdr; Flowable.__init__(self)
    def wrap(self, aW, aH):
        self._w = aW; self._h = 8 + len(self.items)*15 + 8; return (self._w, self._h)
    def draw(self):
        c = self.canv
        c.setFillColor(self.bg); c.setStrokeColor(self.bdr); c.setLineWidth(1.5)
        c.roundRect(0,0,self._w,self._h,4,fill=1,stroke=1)
        c.setFillColor(self.bdr); c.rect(0,0,4,self._h,fill=1,stroke=0)
        for i, item in enumerate(reversed(self.items)):
            y = 8 + i*15
            c.setFillColor(self.bdr); c.setFont('Helvetica-Bold',9); c.drawString(10,y,'\u2605')
            c.setFillColor(DARK_GRAY); c.setFont('Helvetica',9); c.drawString(22,y,item)


# ── HUMIDITY PHYSIOLOGY DIAGRAM ──────────────────────────────────────────────
class HumidityPhysioDiagram(Flowable):
    """Airway humidification physiology – ISB concept diagram."""
    def __init__(self): Flowable.__init__(self)
    def wrap(self, aW, aH): self._w = aW; self._h = 5.5*cm; return (self._w, self._h)

    def _box(self, c, x, y, w, h, fill, text, fs=8, tc=WHITE, bold=True):
        c.setFillColor(fill); c.setStrokeColor(MID_GRAY); c.setLineWidth(0.5)
        c.roundRect(x, y, w, h, 3, fill=1, stroke=1)
        fn = 'Helvetica-Bold' if bold else 'Helvetica'
        c.setFillColor(tc); c.setFont(fn, fs)
        lines = text.split('\n'); lh = fs + 2
        sy = y + h/2 + (len(lines)-1)*lh/2 - 1
        for ln in lines:
            tw = c.stringWidth(ln, fn, fs)
            c.drawString(x + (w-tw)/2, sy, ln); sy -= lh

    def _arr(self, c, x1, y1, x2, y2, col=MID_GRAY):
        c.setStrokeColor(col); c.setLineWidth(1.2); c.line(x1, y1, x2, y2)
        dx, dy = x2-x1, y2-y1; L = math.sqrt(dx*dx+dy*dy) or 1
        ux, uy = dx/L, dy/L; px, py = -uy, ux
        p = c.beginPath()
        p.moveTo(x2-ux*6+px*3, y2-uy*6+py*3)
        p.lineTo(x2-ux*6-px*3, y2-uy*6-py*3)
        p.lineTo(x2, y2); p.close()
        c.setFillColor(col); c.drawPath(p, fill=1, stroke=0)

    def draw(self):
        c = self.canv; fw, fh = self._w, self._h
        c.setFillColor(LIGHT_GRAY); c.rect(0,0,fw,fh,fill=1,stroke=0)
        c.setFillColor(DARK_BLUE); c.rect(0, fh-0.52*cm, fw, 0.52*cm, fill=1, stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',9)
        t = "AIRWAY HUMIDIFICATION PHYSIOLOGY  |  Normal vs. Intubated Patient"
        c.drawString((fw-c.stringWidth(t,'Helvetica-Bold',9))/2, fh-0.37*cm, t)

        # Left half: Normal (no ETT)
        lx = 0.2*cm; hw = fw/2 - 0.4*cm
        c.setFillColor(GREEN_BG); c.roundRect(lx, 0.1*cm, hw, fh-0.7*cm, 4, fill=1, stroke=0)
        c.setFillColor(GREEN); c.setFont('Helvetica-Bold',9)
        c.drawString(lx+0.2*cm, fh-0.85*cm, 'NORMAL (Nose/Mouth Breathing)')

        chain = [
            (ORANGE, 'Inspired gas\n(21\u00b0C, ~50% RH\n~10 mg H\u2082O/L)'),
            (AMBER,  'Nasal turbinates\n+ nasopharynx\n(Warm + humidify)'),
            (TEAL,   'ISB* reached\nat carina level\n(37\u00b0C, 100% RH\n44 mg H\u2082O/L)'),
            (GREEN,  'Alveoli\n(37\u00b0C, 100%\n44 mg H\u2082O/L)'),
        ]
        bw = hw - 0.5*cm; bh = 0.65*cm; gap = 0.25*cm
        total = len(chain)*(bh+gap) - gap
        start_y = (fh - 0.7*cm - total)/2 + 0.1*cm
        for i, (fc, txt) in enumerate(chain):
            by = start_y + i*(bh+gap)
            self._box(c, lx+0.25*cm, by, bw, bh, fc, txt, fs=7.5, bold=True)
            if i < len(chain)-1:
                self._arr(c, lx+0.25*cm+bw/2, by+bh, lx+0.25*cm+bw/2, by+bh+gap, col=GREEN)

        # Right half: Intubated
        rx = fw/2 + 0.2*cm
        c.setFillColor(RED_BG); c.roundRect(rx, 0.1*cm, hw, fh-0.7*cm, 4, fill=1, stroke=0)
        c.setFillColor(RED); c.setFont('Helvetica-Bold',9)
        c.drawString(rx+0.2*cm, fh-0.85*cm, 'INTUBATED (ETT Bypasses URT)')

        chain2 = [
            (ORANGE, 'Inspired gas\n(21\u00b0C, <10 mg\nH\u2082O/L  \u2013 DRY)'),
            (RED,    'ETT/LMA bypasses\nnose + pharynx\n(NO warming/\nhumidification)'),
            (colors.HexColor('#8B0000'), 'ISB* shifts DISTAL\nto lower trachea/\nbronchi\n(Cold dry gas\nreaches alveoli)'),
            (RED,    'CONSEQUENCES:\nMucosa dehydration\nCiliary dysfunction\nInspissated secretions'),
        ]
        for i, (fc, txt) in enumerate(chain2):
            by = start_y + i*(bh+gap)
            self._box(c, rx+0.25*cm, by, bw, bh, fc, txt, fs=7.5, bold=(i<2))
            if i < len(chain2)-1:
                self._arr(c, rx+0.25*cm+bw/2, by+bh, rx+0.25*cm+bw/2, by+bh+gap, col=RED)

        # ISB footnote
        c.setFillColor(DARK_GRAY); c.setFont('Helvetica-Oblique', 7.5)
        c.drawString(lx+0.1*cm, 0.18*cm, '*ISB = Isothermic Saturation Boundary: point where inspired gas reaches 37\u00b0C and 100% RH. Normally at the carina; moves distally with intubation.')


# ── HME MECHANISM DIAGRAM ────────────────────────────────────────────────────
class HMEDiagram(Flowable):
    """HME mechanism: passive condenser humidifier."""
    def __init__(self): Flowable.__init__(self)
    def wrap(self, aW, aH): self._w = aW; self._h = 4.2*cm; return (self._w, self._h)

    def _box(self, c, x, y, w, h, fill, text, fs=8, tc=WHITE, bold=True):
        c.setFillColor(fill); c.setStrokeColor(MID_GRAY); c.setLineWidth(0.5)
        c.roundRect(x, y, w, h, 3, fill=1, stroke=1)
        fn = 'Helvetica-Bold' if bold else 'Helvetica'
        c.setFillColor(tc); c.setFont(fn, fs)
        lines = text.split('\n'); lh = fs + 2
        sy = y + h/2 + (len(lines)-1)*lh/2 - 1
        for ln in lines:
            tw = c.stringWidth(ln, fn, fs)
            c.drawString(x + (w-tw)/2, sy, ln); sy -= lh

    def _arr_h(self, c, x1, y, x2, col=AMBER):
        c.setStrokeColor(col); c.setLineWidth(1.5); c.line(x1, y, x2, y)
        p = c.beginPath()
        if x2 > x1:
            p.moveTo(x2-7, y-4); p.lineTo(x2-7, y+4); p.lineTo(x2, y); p.close()
        else:
            p.moveTo(x2+7, y-4); p.lineTo(x2+7, y+4); p.lineTo(x2, y); p.close()
        c.setFillColor(col); c.drawPath(p, fill=1, stroke=0)

    def draw(self):
        c = self.canv; fw, fh = self._w, self._h
        c.setFillColor(LIGHT_GRAY); c.rect(0,0,fw,fh,fill=1,stroke=0)
        c.setFillColor(TEAL); c.rect(0, fh-0.52*cm, fw, 0.52*cm, fill=1, stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',9)
        t = "PASSIVE HME – Mechanism of Action  (\"Artificial Nose\")"
        c.drawString((fw-c.stringWidth(t,'Helvetica-Bold',9))/2, fh-0.37*cm, t)

        bh = 0.9*cm; bw = 2.5*cm; mid_y = fh/2 - 0.15*cm

        # ETT
        self._box(c, 0.2*cm, mid_y-bh/2, bw, bh, DARK_BLUE, 'ETT\n(Patient)', fs=8)
        # Arrow: exhaled → HME
        self._arr_h(c, 0.2*cm+bw, mid_y, fw/2-2.0*cm, mid_y, col=colors.HexColor('#3399CC'))
        c.setFillColor(colors.HexColor('#3399CC')); c.setFont('Helvetica-Oblique',7.5)
        c.drawString(0.2*cm+bw+0.2*cm, mid_y+0.15*cm, 'EXHALED\n(Warm, moist 37\u00b0C)')

        # HME box
        hme_x = fw/2 - 1.5*cm
        c.setFillColor(AMBER); c.setStrokeColor(DARK_BLUE); c.setLineWidth(1.5)
        c.roundRect(hme_x, mid_y-0.7*cm, 3.0*cm, 1.4*cm, 4, fill=1, stroke=1)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',9)
        t2 = 'HME UNIT'
        c.drawString(hme_x+(3.0*cm-c.stringWidth(t2,'Helvetica-Bold',9))/2, mid_y+0.3*cm, t2)
        c.setFont('Helvetica',7.5)
        lines = ['Hygroscopic material', '(foam/cellulose/ceramic)', 'Traps heat & moisture']
        for i,ln in enumerate(lines):
            tw = c.stringWidth(ln,'Helvetica',7.5)
            c.drawString(hme_x+(3.0*cm-tw)/2, mid_y+0.1*cm-i*0.18*cm, ln)

        # Arrow: inhaled ← HME
        self._arr_h(c, fw-0.2*cm-bw, mid_y, hme_x+3.0*cm+0.2*cm, mid_y, col=ORANGE)
        c.setFillColor(ORANGE); c.setFont('Helvetica-Oblique',7.5)
        c.drawString(hme_x+3.0*cm+0.25*cm, mid_y+0.15*cm, 'INSPIRED\n(Warmed, humidified)')

        # Anaesthesia circuit
        self._box(c, fw-0.2*cm-bw, mid_y-bh/2, bw, bh, MED_BLUE, 'Breathing\nCircuit', fs=8)

        # Annotation boxes below
        ann_y = 0.1*cm
        ann_data = [
            ('Exhalation phase', TEAL_BG, TEAL, 'Warm moist exhaled gas condenses\non hygroscopic material → stores heat & H₂O'),
            ('Inhalation phase', ORANGE_BG, ORANGE, 'Dry inspired gas picks up stored\nheat & moisture from hygroscopic material'),
        ]
        for i, (lbl, bg, border, desc) in enumerate(ann_data):
            ax = 0.2*cm + i*(fw/2)
            c.setFillColor(bg); c.setStrokeColor(border); c.setLineWidth(0.8)
            c.roundRect(ax, ann_y, fw/2-0.4*cm, 0.9*cm, 3, fill=1, stroke=1)
            c.setFillColor(border); c.setFont('Helvetica-Bold', 8)
            c.drawString(ax+0.2*cm, ann_y+0.62*cm, lbl)
            c.setFillColor(DARK_GRAY); c.setFont('Helvetica', 7.5)
            c.drawString(ax+0.2*cm, ann_y+0.38*cm, desc.split('\n')[0])
            if '\n' in desc:
                c.drawString(ax+0.2*cm, ann_y+0.18*cm, desc.split('\n')[1])


# ── ACTIVE HUMIDIFIER DIAGRAM ─────────────────────────────────────────────────
class ActiveHumidDiagram(Flowable):
    """Active humidifier types in one row."""
    def __init__(self): Flowable.__init__(self)
    def wrap(self, aW, aH): self._w = aW; self._h = 4.0*cm; return (self._w, self._h)

    def _box(self, c, x, y, w, h, fill, text, fs=8, tc=WHITE, bold=True):
        c.setFillColor(fill); c.setStrokeColor(MID_GRAY); c.setLineWidth(0.5)
        c.roundRect(x, y, w, h, 3, fill=1, stroke=1)
        fn = 'Helvetica-Bold' if bold else 'Helvetica'
        c.setFillColor(tc); c.setFont(fn, fs)
        lines = text.split('\n'); lh = fs+1.8
        sy = y+h/2+(len(lines)-1)*lh/2-1
        for ln in lines:
            tw = c.stringWidth(ln, fn, fs)
            c.drawString(x+(w-tw)/2, sy, ln); sy -= lh

    def draw(self):
        c = self.canv; fw, fh = self._w, self._h
        c.setFillColor(LIGHT_GRAY); c.rect(0,0,fw,fh,fill=1,stroke=0)
        c.setFillColor(MED_BLUE); c.rect(0, fh-0.52*cm, fw, 0.52*cm, fill=1, stroke=0)
        c.setFillColor(WHITE); c.setFont('Helvetica-Bold',9)
        t = "ACTIVE HUMIDIFIER TYPES"
        c.drawString((fw-c.stringWidth(t,'Helvetica-Bold',9))/2, fh-0.37*cm, t)

        types = [
            (TEAL,   'PASSOVER\nHUMIDIFIER',
             'Gas flows over\nwater surface\n(simple, low output)'),
            (GREEN,  'BUBBLE-THROUGH\nHUMIDIFIER',
             'Gas bubbled through\nwater column\n(higher output)'),
            (MED_BLUE,'WICK\nHUMIDIFIER',
             'Gas passes through\nsaturated wick\n(uniform humidification)'),
            (PURPLE, 'HEATED-WIRE\nCIRCUIT',
             'Electrical wires in\ntube wall heat gas\n(prevents condensation)'),
            (ORANGE, 'HEATED WATER\nBATH (HH)',
             'Water heated to\n40–42\u00b0C; thermostat\ncontrolled; most effective'),
        ]
        cw = fw/len(types)
        for i, (fc, title, desc) in enumerate(types):
            x = i*cw + 0.15*cm
            self._box(c, x, fh-1.7*cm, cw-0.3*cm, 1.1*cm, fc, title, fs=8, bold=True)
            c.setFillColor(DARK_GRAY); c.setFont('Helvetica',7.5)
            dlines = desc.split('\n')
            for j, dl in enumerate(dlines):
                tw = c.stringWidth(dl,'Helvetica',7.5)
                c.drawString(x+(cw-0.3*cm-tw)/2, fh-1.85*cm-j*0.17*cm, dl)

        # Hazards bar at bottom
        c.setFillColor(RED_BG); c.setStrokeColor(RED); c.setLineWidth(0.8)
        c.roundRect(0.2*cm, 0.1*cm, fw-0.4*cm, 0.75*cm, 3, fill=1, stroke=1)
        c.setFillColor(RED); c.setFont('Helvetica-Bold', 8)
        c.drawString(0.4*cm, 0.64*cm, 'HAZARDS OF ACTIVE HUMIDIFIERS:')
        c.setFillColor(DARK_GRAY); c.setFont('Helvetica',8)
        haz = ('Thermal lung injury (keep inspired gas \u226441\u00b0C)  |  Nosocomial infection  |  '
               'Excess condensate \u2192 circuit obstruction  |  Flowmeter interference  |  '
               'Circuit disconnection  |  Do NOT filter (unlike HME)')
        c.drawString(0.4*cm, 0.42*cm, haz[:90])
        c.drawString(0.4*cm, 0.22*cm, haz[90:])


story = []

# ── Title ─────────────────────────────────────────────────────────────────────
story.append(TitleBar(
    "HUMIDIFICATION METHODS IN ANAESTHESIA",
    "Physiology · Passive & Active Methods · Devices · Complications · Clinical Applications",
    "MD Anaesthesia  |  10-Mark Long Answer  |  Sources: Miller 10e, Morgan & Mikhail 7e, Barash 9e"
))
story.append(Spacer(1, 3*mm))

# ── 1. Introduction ────────────────────────────────────────────────────────────
story.append(SecHdr("1.  INTRODUCTION & IMPORTANCE", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "Normally, the <b>upper respiratory tract (nose, nasopharynx, pharynx)</b> warms inspired "
    "gases from room temperature (~21°C) to body temperature (37°C) and saturates them with "
    "water vapour to reach <b>100% relative humidity (44 mg H₂O/L)</b> by the level of the "
    "<b>isothermic saturation boundary (ISB)</b>, normally located at the carina. "
    "Anaesthesia – particularly with <b>endotracheal intubation</b> and high fresh gas flows – "
    "bypasses this system, delivering cold, dry gas (< 10 mg H₂O/L) directly to the lower airways. "
    "This has clinically important physiological consequences that make humidification during "
    "anaesthesia an essential consideration.", body))
story.append(Spacer(1, 3*mm))

# ── 2. Definitions ─────────────────────────────────────────────────────────────
story.append(SecHdr("2.  DEFINITIONS", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
def_data = [
    [Paragraph('<b>Term</b>', body_bold), Paragraph('<b>Definition</b>', body_bold), Paragraph('<b>Value (clinical)</b>', body_bold)],
    ['Absolute humidity',
     'Mass of water vapour in a given volume of gas (mg/L or g/m³)',
     '44 mg/L at 37°C, 100% RH\n18 mg/L at 21°C, 100% RH'],
    ['Relative humidity (RH)',
     'Ratio of actual water content to maximum possible water content\nat a given temperature (expressed as %)',
     'Target: 100% RH at 37°C\n(alveolar level)'],
    ['Isothermic Saturation\nBoundary (ISB)',
     'Point in the airway where inspired gas reaches 37°C and 100% RH;\ngas exchange occurs at/below this point',
     'Normal: carina level\nIntubated: shifts distal to\nlower trachea/mainstem bronchi'],
    ['Humidity deficit',
     'Difference between actual water content of inspired gas\nand 44 mg/L (fully saturated at body temperature)',
     'Dry anaesthetic gas:\n44 – <10 = >34 mg H₂O/L deficit'],
    ['Dew point',
     'Temperature at which water vapour begins to condense out\nof a gas mixture',
     'Relevant to heated circuit\nwire humidifiers'],
]
dt = Table(def_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.24, 0.48, 0.28]])
dt.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),DARK_BLUE), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTNAME',(0,1),(0,-1),'Helvetica-Bold'), ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[LIGHT_BLUE,WHITE,LIGHT_BLUE,WHITE,LIGHT_BLUE]),
    ('ROWHEIGHT',(0,0),(-1,-1),0.65*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'CENTER'), ('ALIGN',(1,0),(-1,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(dt)
story.append(Spacer(1, 3*mm))

# ── 3. Physiology Diagram ──────────────────────────────────────────────────────
story.append(SecHdr("3.  PHYSIOLOGY – NORMAL vs. INTUBATED PATIENT", bg=TEAL))
story.append(Spacer(1, 2*mm))
story.append(HumidityPhysioDiagram())
story.append(Spacer(1, 3*mm))

# ── 4. Consequences of inadequate humidification ────────────────────────────────
story.append(SecHdr("4.  CONSEQUENCES OF INADEQUATE HUMIDIFICATION", bg=RED))
story.append(Spacer(1, 2*mm))
cons_data = [
    [Paragraph('<b>System</b>', body_bold), Paragraph('<b>Effect</b>', body_bold), Paragraph('<b>Clinical Result</b>', body_bold)],
    ['Mucociliary', 'Ciliary dysfunction; mucosal dehydration',
     'Impaired secretion clearance → mucous plugging → atelectasis'],
    ['Secretions', 'Inspissation (drying) of secretions',
     'Plugging of ETT especially small paediatric tubes; difficult suction'],
    ['Thermal', 'Heat of vaporisation lost (560 cal/g of water vaporised)',
     '5–10% of total intraoperative heat loss → hypothermia (worse in neonates)'],
    ['Mucosal', 'Epithelial damage; loss of surfactant function',
     'Pneumonia risk; increased airway reactivity; bronchospasm'],
    ['V/Q mismatch', 'Atelectasis from mucous plugging and surfactant loss',
     'Hypoxaemia; increased work of breathing post-extubation'],
    ['Airway resistance', 'Swelling, secretion retention',
     'Increased work of breathing; prolonged ventilation weaning'],
    ['Paediatric risk', 'Neonates especially vulnerable (large BSA:weight)',
     'ETT obstruction; hypothermia; significant morbidity in prolonged surgery'],
]
ct = Table(cons_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.16, 0.40, 0.44]])
ct.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),RED), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTNAME',(0,1),(0,-1),'Helvetica-Bold'), ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[RED_BG,WHITE]*4),
    ('ROWHEIGHT',(0,0),(-1,-1),0.6*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'CENTER'), ('ALIGN',(1,0),(-1,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(ct)
story.append(Spacer(1, 3*mm))

# ── 5. Classification ──────────────────────────────────────────────────────────
story.append(SecHdr("5.  CLASSIFICATION OF HUMIDIFICATION METHODS", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "Humidification methods in anaesthesia can be classified as <b>passive</b> (condenser humidifiers) "
    "or <b>active</b> (heated humidifiers and heated-wire breathing circuits). Additionally, the "
    "<b>circle system with CO₂ absorber</b> at low fresh gas flows provides inherent humidification.", body))
story.append(Spacer(1, 2*mm))

class_data = [
    [Paragraph('<b>Category</b>', body_bold), Paragraph('<b>Type</b>', body_bold), Paragraph('<b>Mechanism</b>', body_bold), Paragraph('<b>Examples</b>', body_bold)],
    ['PASSIVE\n(Condenser)', 'Heat-Moisture Exchanger\n(HME / "Artificial Nose")',
     'Hygroscopic material retains\nheat & moisture from exhaled\ngas; returns on inspiration',
     'Pall HME, Humid-Vent,\nDAR filter-HME,\nHMEF (+ filter)'],
    ['PASSIVE\n(Condenser)', 'Hygroscopic Condenser\nHumidifier (HCH)',
     'Hygroscopic material\n(LiCl, CaCl₂) absorbs more\nwater; better efficiency',
     'Hygrobac, Pall BB50T,\nThermovent series'],
    ['ACTIVE', 'Passover / Bubble-through\nHumidifier',
     'Gas passes over or through\nheated water reservoir;\nthermostat controlled',
     'Fisher & Paykel MR410,\nVentstream, Humid-Aire'],
    ['ACTIVE', 'Wick Humidifier',
     'Gas flows past saturated\nfibrous wick; high surface\narea → effective',
     'Respiratory Care Inc.\nwet wick systems'],
    ['ACTIVE', 'Heated-Wire Breathing\nCircuit (HH + HWC)',
     'Heated wires inside circuit\nwalls prevent condensation;\nmaintain gas temperature',
     'Fisher & Paykel Optiflow;\nHamilton Arabella circuit'],
    ['ACTIVE', 'Vapour-Phase\nHumidifier',
     'Sterile water vaporised by\nheating element; mixed\nwith inspired gas',
     'MR890 series (ICU\nstandard)'],
    ['CIRCUIT-\nBASED', 'Low-Flow Circle System\n(CO₂ absorber)',
     'Exhaled moisture recirculated\nin circle; CO₂ absorber\ngenerates heat + water',
     'Standard circle system\nat < 1 L/min FGF'],
    ['CIRCUIT-\nBASED', 'Low-Flow Bain /\nCoaxial circuits',
     'Countercurrent exchange:\nexhaled gas in outer tube\nwarms inner FGF tube',
     'Bain circuit; Lack\ncircuit (partial effect)'],
]
class_t = Table(class_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.13, 0.24, 0.34, 0.29]])
class_t.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),DARK_BLUE), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTSIZE',(0,0),(-1,-1),8),
    ('BACKGROUND',(0,1),(0,2),TEAL_BG), ('BACKGROUND',(0,3),(0,6),ORANGE_BG),
    ('BACKGROUND',(0,7),(0,8),PURPLE_BG),
    ('ROWBACKGROUNDS',(1,1),(-1,2),[TEAL_BG,WHITE]),
    ('ROWHEIGHT',(0,0),(-1,-1),0.65*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'CENTER'), ('ALIGN',(1,0),(-1,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(class_t)
story.append(Spacer(1, 3*mm))

# ── 6. HME – Detailed ─────────────────────────────────────────────────────────
story.append(SecHdr("6.  PASSIVE HME (HEAT-MOISTURE EXCHANGER) – DETAILED", bg=TEAL))
story.append(Spacer(1, 2*mm))
story.append(HMEDiagram())
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>A. Types of HME by material:</b>", body_bold))
story.append(Spacer(1, 1*mm))
hme_types_data = [
    [Paragraph('<b>Type</b>', body_bold), Paragraph('<b>Material</b>', body_bold),
     Paragraph('<b>Efficiency</b>', body_bold), Paragraph('<b>Dead space</b>', body_bold)],
    ['Simple condenser\nhumidifier', 'Metal gauze / foam\n(non-hygroscopic)',
     'Low: 20–30 mg H₂O/L\noutput', '5–10 mL'],
    ['Hygroscopic condenser\nhumidifier (HCH)', 'LiCl or CaCl₂ impregnated\nmaterial (hygroscopic)',
     'Good: 25–35 mg H₂O/L\noutput', '15–30 mL'],
    ['Hydrophobic HME', 'Pleated hydrophobic\nmembrane', 'Moderate: 22–28 mg H₂O/L;\nexcellent filtration',
     '30–50 mL'],
    ['HMEF (HME + Filter)', 'Hygroscopic + electrostatic\nbacterial/viral filter combined',
     'Best filtration + moderate\nhumidification (26–32 mg H₂O/L)',
     '30–60+ mL (concern\nin paediatrics)'],
]
hme_t = Table(hme_types_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in [0.22, 0.32, 0.28, 0.18]])
hme_t.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),TEAL), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTSIZE',(0,0),(-1,-1),8.5),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[TEAL_BG,WHITE,TEAL_BG,WHITE]),
    ('ROWHEIGHT',(0,0),(-1,-1),0.62*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(-1,-1),'CENTER'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(hme_t)
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>B. Advantages of HME:</b>", body_bold))
story.append(B("Simple, cheap, disposable – no water reservoir or electrical supply needed"))
story.append(B("Acts as an 'artificial nose' – minimal maintenance"))
story.append(B("HMEF types provide bacterial + viral filtration (barrier to cross-contamination)"))
story.append(B("No risk of thermal injury"))
story.append(B("Reduces circuit contamination – protects anaesthesia machine"))
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>C. Disadvantages / Contraindications of HME:</b>", body_bold))
story.append(B("<b>Increases apparatus dead space</b> (>60 mL) → significant rebreathing in paediatric patients"))
story.append(B("Increases breathing circuit resistance → increased work of breathing (spontaneous ventilation)"))
story.append(B("Excessive saturation with secretions or water → <b>circuit obstruction</b>"))
story.append(B("Cannot be used with nebulised drugs (clogs the HME material)"))
story.append(B("Less effective than active heated humidifiers"))
story.append(B("Output declines over time (typically change every 24 hours)"))
story.append(B("<b>Contraindicated:</b> bloody secretions, thick secretions, very low tidal volume (<150 mL)"))
story.append(Spacer(1, 3*mm))

# ── 7. Active Humidifiers ─────────────────────────────────────────────────────
story.append(SecHdr("7.  ACTIVE HUMIDIFIERS – TYPES & DETAILS", bg=MED_BLUE))
story.append(Spacer(1, 2*mm))
story.append(ActiveHumidDiagram())
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "Active humidifiers add heat and water to inspired gases using an external energy source. "
    "The <b>heated water bath humidifier (HH)</b> combined with a <b>heated-wire breathing circuit (HWC)</b> "
    "is the current gold standard for long-term mechanical ventilation and neonatal anaesthesia. "
    "Gas temperature should be continuously monitored and maintained between <b>36–41°C</b>; "
    "exceeding 41°C risks <b>thermal airway injury</b> (mucosal burns).", body))
story.append(Spacer(1, 2*mm))

story.append(Paragraph("<b>Key principles of heated humidifiers:</b>", body_bold))
story.append(B("Higher temperature → greater water vapour capacity of gas → better humidification"))
story.append(B("Thermostatically controlled to deliver gas at 36–41°C (should not exceed 41°C)"))
story.append(B("Heated-wire circuits prevent condensation ('rainout') in the inspiratory limb"))
story.append(B("Most effective method: <b>100% relative humidity at 37°C achievable</b>"))
story.append(B("Particularly valuable for children (prevent hypothermia + ETT plugging)"))
story.append(B("Used routinely in ICU; increasingly in operating theatres for prolonged surgery"))
story.append(B("<b>Active humidifiers do NOT provide filtration</b> (unlike HMEF)"))
story.append(Spacer(1, 3*mm))

# ── 8. Circle System – Inherent Humidification ──────────────────────────────────
story.append(SecHdr("8.  CIRCLE SYSTEM – INHERENT HUMIDIFICATION AT LOW FGF", bg=GREEN))
story.append(Spacer(1, 2*mm))
story.append(Paragraph(
    "The circle system with CO₂ absorber at <b>low fresh gas flow (< 1 L/min)</b> provides "
    "significant inherent humidification through two mechanisms:", body))
story.append(Spacer(1, 1*mm))
story.append(B("<b>Recirculation of exhaled moisture:</b> Exhaled gas (containing 44 mg H₂O/L) is partially recirculated back to the patient; at low FGF, a large proportion of expired gas is rebreathed after CO₂ removal"))
story.append(B("<b>CO₂ absorber exothermic reaction:</b> CO₂ + Ca(OH)₂ → CaCO₃ + H₂O + heat; the CO₂ absorber produces water and heat, which warms and humidifies the recirculated gas"))
story.append(B("At FGF < 1 L/min, inspired gas humidity reaches 25–30 mg H₂O/L – approaching 70% RH"))
story.append(B("Bain/coaxial circuits: partial countercurrent heat exchange (exhaled gas in outer tube warms FGF in inner tube)"))
story.append(Spacer(1, 3*mm))

# ── 9. Comparison table ───────────────────────────────────────────────────────
story.append(SecHdr("9.  COMPARISON: PASSIVE vs. ACTIVE HUMIDIFICATION", bg=DARK_BLUE))
story.append(Spacer(1, 2*mm))
comp_data = [
    [Paragraph('<b>Feature</b>', body_bold),
     Paragraph('<b>Passive HME</b>', body_bold),
     Paragraph('<b>Active Heated Humidifier\n(HH + HWC)</b>', body_bold),
     Paragraph('<b>Circle System\n(Low FGF)</b>', body_bold)],
    ['Mechanism', 'Condenses exhaled moisture;\nhygroscopic material',
     'External heating element;\nwater reservoir',
     'Recirculation of exhaled gas;\nCO₂ absorber reaction'],
    ['Humidity output', '20–35 mg H₂O/L\n(~50–80% RH)', '35–44 mg H₂O/L\n(100% RH achievable)',
     '25–30 mg H₂O/L at\n< 1 L/min FGF'],
    ['Temperature control', 'None (passive)', 'Yes (thermostat 36–41°C)', 'Partial (absorber warmth)'],
    ['Filtration', 'HMEF: Yes ✓\nBasic HME: No', 'No ✗', 'No ✗'],
    ['Dead space added', '30–60+ mL (concern\nfor paediatrics)', 'Minimal (water trap\nonly)', 'No additional DS'],
    ['Energy required', 'None', 'Electrical power', 'None (additional)'],
    ['Infection risk', 'Low (HMEF filters)', 'Higher (standing water,\nbacterial colonisation)',
     'Low (closed system)'],
    ['Thermal injury risk', 'None', 'Yes (if >41°C)', 'None'],
    ['Best for', 'Short-medium procedures;\nICU patients with HMEF;\nbreathing circuit protection',
     'Neonates; prolonged surgery;\ncystic fibrosis; ICU long-term',
     'Adult routine anaesthesia;\nall cases with circle system'],
    ['Contraindications', 'Paediatrics <20 kg (dead space);\nbloody/thick secretions;\nnebuliser use',
     'Must monitor temperature;\nnot used with open circuits',
     'N/A (inherent to circuit)'],
]
cw_comp = [0.18, 0.27, 0.28, 0.27]
comp_t = Table(comp_data, colWidths=[(PAGE_W-2*MARGIN)*x for x in cw_comp])
comp_t.setStyle(TableStyle([
    ('BACKGROUND',(0,0),(-1,0),DARK_BLUE), ('TEXTCOLOR',(0,0),(-1,0),WHITE),
    ('FONTNAME',(0,0),(-1,0),'Helvetica-Bold'), ('FONTNAME',(0,1),(-1,-1),'Helvetica'),
    ('FONTNAME',(0,1),(0,-1),'Helvetica-Bold'), ('FONTSIZE',(0,0),(-1,-1),8),
    ('ROWBACKGROUNDS',(0,1),(-1,-1),[LIGHT_BLUE,WHITE]*6),
    ('ROWHEIGHT',(0,0),(-1,-1),0.62*cm), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('ALIGN',(0,0),(0,-1),'CENTER'), ('ALIGN',(1,0),(-1,-1),'LEFT'),
    ('GRID',(0,0),(-1,-1),0.4,MID_GRAY), ('LEFTPADDING',(0,0),(-1,-1),5),
]))
story.append(comp_t)
story.append(Spacer(1, 3*mm))

# ── 10. Clinical Applications & Guidelines ─────────────────────────────────────
story.append(SecHdr("10.  CLINICAL APPLICATIONS & GUIDELINES", bg=PURPLE))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>A. Short Procedures (< 1 hour):</b>", body_bold))
story.append(B("Humidification less critical; 5–10% of intraoperative heat loss from airway humidification"))
story.append(B("HME adequate if used; circle system at low FGF provides sufficient humidification"))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>B. Prolonged Surgery (> 1–2 hours):</b>", body_bold))
story.append(B("Active humidification strongly recommended"))
story.append(B("Low-flow circle system + HMEF filter is practical and effective"))
story.append(B("Heated-wire circuit + heated humidifier for ICU-type ventilation intraoperatively"))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>C. Paediatrics / Neonates:</b>", body_bold))
story.append(B("<b>Active humidification is essential</b> – prevent both hypothermia and ETT plugging from dried secretions"))
story.append(B("Use <b>neonatal HME</b> (dead space < 2 mL) if HME chosen – adult HME dead space causes rebreathing"))
story.append(B("Heated-wire circuits + heated humidifier for all neonatal procedures"))
story.append(B("Low FGF circle system preferred over open Mapleson circuits for better humidification"))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>D. Patients with Pre-existing Lung Disease (e.g., Cystic Fibrosis, Bronchiectasis):</b>", body_bold))
story.append(B("Active heated humidification is essential to prevent inspissation of secretions"))
story.append(B("Target near 100% RH at 37°C – heated water bath + HWC"))
story.append(B("HME inadequate alone in these patients"))
story.append(Spacer(1, 2*mm))
story.append(Paragraph("<b>E. Infection Control / COVID-19:</b>", body_bold))
story.append(B("<b>HMEF</b> (combined HME + filter) used to protect circuit from respiratory pathogens"))
story.append(B("Provides barrier against bacteria and viruses; used in COVID-positive patients"))
story.append(B("Should not be used with nebulised drugs as it blocks the filter"))
story.append(Spacer(1, 3*mm))

# ── 11. Key Points ─────────────────────────────────────────────────────────────
story.append(SecHdr("\u2605  KEY POINTS SUMMARY  (10-Mark Exam)", bg=AMBER))
story.append(Spacer(1, 2*mm))
story.append(KeyBox([
    "Absolute humidity at 37°C, 100% RH = 44 mg H₂O/L. Anaesthetic gas delivers < 10 mg/L – 'humidity deficit'",
    "ISB (isothermic saturation boundary) = normally at carina; shifts distally with ETT/intubation",
    "Consequences of inadequate humidification: mucociliary failure, inspissated secretions, hypothermia, atelectasis, V/Q mismatch",
    "Passive HME = 'Artificial nose'; hygroscopic material; 20–35 mg H₂O/L output; no power needed",
    "HMEF = HME + filter; best for infection control; do NOT use with nebulisers",
    "HME dead space (>60 mL) is significant in paediatrics – use neonatal HME (<2 mL dead space)",
    "Active heated humidifier = gold standard; achieves 100% RH at 37°C; heated-wire circuit prevents condensate",
    "Active humidifier hazards: thermal lung injury (keep ≤41°C), nosocomial infection, circuit obstruction",
    "Circle system at low FGF (<1 L/min) provides 25–30 mg H₂O/L inherent humidification (CO₂ absorber reaction + recirculation)",
    "Humidification is most important for: neonates/infants, prolonged surgery, patients with severe lung disease (CF, bronchiectasis)",
]))
story.append(Spacer(1, 3*mm))

# ── References ─────────────────────────────────────────────────────────────────
story.append(HRFlowable(width="100%", thickness=0.5, color=MID_GRAY, spaceAfter=3))
story.append(Paragraph(
    "<b>References:</b> "
    "Morgan & Mikhail's Clinical Anesthesiology 7e, Ch.4, pp.128–130  |  "
    "Miller's Anesthesia 10e (Pardo & Miller), Ch.20, pp.2340–2346  |  "
    "Barash Clinical Anesthesia 9e, Ch.25, pp.1966–1968  |  "
    "Wilkes AR. Heat and moisture exchangers and breathing system filters: their use in anaesthesia and intensive care. "
    "Anaesthesia 2011;66(Suppl 1):31–39  |  "
    "ISO 9360-1:2000: Anaesthetic and respiratory equipment – Heat and moisture exchangers",
    ref_style))

# ── Build ─────────────────────────────────────────────────────────────────────
def on_page(canvas, doc):
    canvas.saveState()
    canvas.setFillColor(colors.HexColor('#ECF0F5'))
    canvas.rect(0, 0, PAGE_W, 0.7*cm, fill=1, stroke=0)
    canvas.setFont('Helvetica', 7.5); canvas.setFillColor(MID_GRAY)
    canvas.drawString(MARGIN, 0.22*cm, "Humidification Methods in Anaesthesia – MD Anaesthesia 10-Mark")
    canvas.drawRightString(PAGE_W-MARGIN, 0.22*cm, f"Page {doc.page}")
    canvas.restoreState()

doc.build(story, onFirstPage=on_page, onLaterPages=on_page)
print(f"Saved: {out_path}  ({os.path.getsize(out_path):,} bytes)")
Running Command

cd /tmp/workspace/anaesthesia-checklist && python build_humidification_pdf.py 2>&1

Running Command

# Fix: _arr_h signature has 'col' as 5th positional arg, but calls pass x1,y,x2,y2=col as 4 positional args then col= # The signature is _arr_h(self, c, x1, y, x2, col=AMBER) - so passing col as 5th positional is fine IF y2 is not passed # Error: calls like _arr_h(c, x, y, x2, y, col=) pass y twice # Let me check all _arr_h calls in the file grep -n '_arr_h' /tmp/workspace/anaesthesia-checklist/build_humidification_pdf.py

The calls pass 5 positional args: c, x1, y, x2, y2(=mid_y again) - so col parameter gets mid_y positionally and also col= as keyword. Fix by removing the duplicate mid_y:
Running Command

sed -i 's/self._arr_h(c, 0.2\*cm+bw, mid_y, fw\/2-2.0\*cm, mid_y, col=colors.HexColor/self._arr_h(c, 0.2*cm+bw, mid_y, fw\/2-2.0*cm, colors.HexColor/' /tmp/workspace/anaesthesia-checklist/build_humidification_pdf.py sed -i 's/self._arr_h(c, fw-0.2\*cm-bw, mid_y, hme_x+3.0\*cm+0.2\*cm, mid_y, col=ORANGE)/self._arr_h(c, fw-0.2*cm-bw, mid_y, hme_x+3.0*cm+0.2*cm, ORANGE)/' /tmp/workspace/anaesthesia-checklist/build_humidification_pdf.py cd /tmp/workspace/anaesthesia-checklist && python build_humidification_pdf.py 2>&1

Your 10-mark PDF is ready:

Humidification Methods Anaesthesia MD

PDF Document · PDF


Humidification Methods in Anaesthesia — 10-Mark Answer


1. Introduction

Normally the upper respiratory tract warms inspired gas from room temperature (21°C) to body temperature (37°C) and saturates it with water vapour to 44 mg H₂O/L (100% RH) by the level of the Isothermic Saturation Boundary (ISB) — normally at the carina. Tracheal intubation + high fresh gas flows bypass this system, delivering cold, dry gas (< 10 mg H₂O/L) to the lower airways, creating a humidity deficit of > 34 mg H₂O/L.

2. Key Definitions

TermDefinitionClinical Value
Absolute humidityMass of water vapour per litre of gas (mg/L)44 mg/L at 37°C, 100% RH
Relative humidityActual water content / maximum possible at that temperature (%)Target 100% at alveolar level
ISBPoint where gas reaches 37°C, 100% RHNormally at carina; shifts distally with intubation
Humidity deficit44 - actual inspired water content> 34 mg/L with dry anaesthetic gases

3. Consequences of Inadequate Humidification

SystemEffectClinical Result
MucociliaryCiliary dysfunction + mucosal dehydrationSecretion retention → mucous plugging → atelectasis
SecretionsInspissation (drying)ETT blockage especially small paediatric tubes
ThermalHeat of vaporisation lost (560 cal/g)5–10% of total intraoperative heat loss → hypothermia
MucosalEpithelial damagePneumonia risk; increased airway reactivity
V/QAtelectasis from pluggingHypoxaemia post-extubation

4. Classification of Methods

A. PASSIVE — Heat-Moisture Exchangers (HME / "Artificial Nose")

Mechanism: Hygroscopic material in the HME traps heat and moisture from exhaled gas (37°C, 100% RH) and releases it to the next inspired breath.
TypeMaterialOutputDead Space
Simple condenserMetal gauze/foam20–30 mg H₂O/L5–10 mL
Hygroscopic (HCH)LiCl / CaCl₂25–35 mg H₂O/L15–30 mL
Hydrophobic HMEPleated membrane22–28 mg H₂O/L; good filtration30–50 mL
HMEF (HME + Filter)Hygroscopic + electrostatic26–32 mg H₂O/L + viral/bacterial filter30–60+ mL
Advantages: Simple, cheap, disposable, no power, circuit protection, HMEF provides filtration
Disadvantages/Contraindications:
  • Dead space > 60 mL → significant rebreathing in paediatrics
  • Increases circuit resistance → work of breathing
  • Secretion saturation → circuit obstruction
  • Cannot use with nebulisers
  • Contraindicated: bloody/thick secretions, TV < 150 mL, paediatric patients (use neonatal HME)

B. ACTIVE — Heated Humidifiers

TypeMechanismNotes
Passover humidifierGas flows over heated water surfaceSimple, moderate output
Bubble-through humidifierGas bubbled through heated waterHigher output
Wick humidifierGas through saturated wickUniform humidification
Heated water bath + heated-wire circuit (HH + HWC)Water heated to 40–42°C; wires in circuit prevent condensationGold standard — achieves 100% RH at 37°C
Vapour-phase humidifierSterile water vaporised by heating elementICU standard
Key principle: Higher temperature → greater water vapour capacity → better humidification. Gas temperature must be monitored and must not exceed 41°C (risk of thermal airway mucosal burns).
Hazards: Thermal lung injury · Nosocomial infection · Circuit obstruction from condensate · Flowmeter interference · Circuit disconnection · No filtration (unlike HMEF)

C. CIRCUIT-BASED — Inherent Humidification

Circle system at low FGF (< 1 L/min):
  • Recirculation of exhaled moisture (44 mg H₂O/L in exhaled gas)
  • CO₂ absorber exothermic reaction: CO₂ + Ca(OH)₂ → CaCO₃ + H₂O + heat
  • Achieves 25–30 mg H₂O/L (~70% RH) at FGF < 1 L/min
Bain coaxial circuit: Countercurrent heat exchange — exhaled gas in outer tube warms FGF in inner tube (partial effect only)

5. Comparison: Passive vs Active vs Circle

FeaturePassive HMEActive HH + HWCCircle (Low FGF)
Humidity output20–35 mg/L35–44 mg/L (100% RH)25–30 mg/L
FiltrationHMEF: YesNoNo
Thermal injury riskNoneYes (if >41°C)None
Dead space added30–60+ mLMinimalNone
Infection riskLowHigher (standing water)Low
Best forShort/medium procedures; infection controlNeonates; prolonged surgery; lung diseaseAdult routine anaesthesia

★ Key Points (10-Mark Exam)

  1. Absolute humidity at 37°C = 44 mg H₂O/L; anaesthetic gas delivers < 10 mg/L
  2. ISB = isothermic saturation boundary = normally at carina; shifts distally with ETT
  3. Consequences: mucociliary failure → inspissated secretions → atelectasis + hypothermia
  4. HME = "artificial nose" — passive, hygroscopic; 20–35 mg H₂O/L
  5. HMEF = HME + filter — bacterial/viral protection; do NOT use with nebulisers
  6. HME dead space > 60 mL — dangerous in paediatrics; use neonatal HME (< 2 mL)
  7. Active humidifier = gold standard — 100% RH at 37°C; must monitor temperature (≤ 41°C)
  8. Active humidifier hazards: thermal injury, nosocomial infection, circuit obstruction
  9. Circle + low FGF — CO₂ absorber produces H₂O + heat → inherent humidification
  10. Humidification most important in: neonates, prolonged surgery, cystic fibrosis, bronchiectasis

Sources: Morgan & Mikhail's Clinical Anesthesiology 7e pp.128–130 | Miller's Anesthesia 10e | Barash 9e | Wilkes AR, Anaesthesia 2011 | ISO 9360-1:2000
This is a shared conversation. Sign in to Orris to start your own chat.