anaesthesia machine checklist protocol
Please make one with flowcharts
mkdir -p /tmp/workspace/anaesthesia-checklist && ls /tmp/workspace/
~/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}")
cd /tmp/workspace/anaesthesia-checklist && python build_pptx.py 2>&1
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') "
Anaesthesia Machine Checklist Protocol
Presentation · PPTX
| Slide | Content |
|---|---|
| 1 - Title | Cover slide with colour legend (Daily / Each Case / Critical) |
| 2 - Overview Flowchart | Top-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 Checks | Items 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 Checks | Items 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 Flowchart | Step-by-step Low-Pressure Leak Test with PASS/FAIL branches + Circle System Functional Test (steps A–G) |
| 6 - Summary Table | All 15 ASA items in one colour-coded table (frequency + responsible party) |
| 7 - Troubleshooting | Intraoperative failure response flowchart: Failure detected → Disconnect → SIRB → Stabilise → Decision diamond → Yes/No branches, with the key Miller's principle quoted |
Oxygen failure protection device 5 marks short note pdf for MD anaesthesia with key points summarised
~/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>•</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)")
cd /tmp/workspace/anaesthesia-checklist && python build_ofpd_pdf.py 2>&1
~/anaesthesia-checklist/build_ofpd_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_ofpd_pdf.py'
~/anaesthesia-checklist/build_ofpd_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_ofpd_pdf.py'
cd /tmp/workspace/anaesthesia-checklist && python build_ofpd_pdf.py 2>&1
OFPD MD Anaesthesia Short Note
PDF Document · PDF
| Type | Mechanism | Threshold | Used in |
|---|---|---|---|
| Binary (threshold) shutoff | All-or-nothing; spring closes valve | 20–30 psig | Older GE/Datex-Ohmeda |
| Proportional (balance regulator / OFPD) | Proportionally reduces N₂O; fully shuts at 0.5 psig | 0.5 psig (N₂O) / 10 psig (others) | All modern machines (ISO std) |
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
Bain's circuit, advantages, disadvantage and modifications and other coaxial circuits
~/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>•</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)")
cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1
~/anaesthesia-checklist/build_bain_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_bain_pdf.py'
cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1
~/anaesthesia-checklist/build_bain_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_bain_pdf.py'
cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1
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:grep -n '_arrow_h' /tmp/workspace/anaesthesia-checklist/build_bain_pdf.py | head -20
col=AMBER which doesn't match the parameter name arrow_col. Fix both calls: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
cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1
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:~/anaesthesia-checklist/build_bain_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/anaesthesia-checklist/build_bain_pdf.py'
cd /tmp/workspace/anaesthesia-checklist && python build_bain_pdf.py 2>&1
# 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
Bain Circuit MD Anaesthesia
PDF Document · PDF
[Reservoir Bag] ←← OUTER TUBE (exhaled gas) ←← [Patient / ETT]
[APL Valve] →→ INNER TUBE (fresh gas) →→ (FGF exits here)
↕ Countercurrent heat exchange
| Mode | FGF Required | Practical (70 kg adult) |
|---|---|---|
| Spontaneous | 200-300 mL/kg/min (~2.5 × MV) | ~14 L/min |
| Controlled (IPPV) | 70 mL/kg/min (~1 × MV) | ~5 L/min |
| Step | Action | Result |
|---|---|---|
| 1 | Occlude patient end | Circuit sealed |
| 2 | Flush O₂ at high flow | Reservoir bag fills |
| 3 | Release patient end | Venturi effect created |
| PASS | Inner tube intact | Bag deflates (Venturi suction) ✓ |
| FAIL | Inner tube leaking | Bag stays inflated - gas escaping into expiratory limb ✗ |
| Modification | Description |
|---|---|
| Paediatric Bain | Miniaturised; lower dead space; for 10-20 kg children |
| COVID-19 modification | Bacterial/viral filter added between circuit and ETT; increases resistance |
| Extended length versions | Different hose lengths for various surgical positions |
| Circuit | Mapleson | Inner Tube | Outer Tube | Best For | Key Point |
|---|---|---|---|---|---|
| Bain (1972) | D | FGF → patient | Exhaled → APL | Controlled ventilation | FGF = 70 mL/kg/min (IPPV) |
| Lack (1976) | A | Exhaled → APL | FGF → patient | Spontaneous ventilation | FGF = 1× MV (most efficient) |
| Humphrey ADE (1983) | A/D/E switchable | Configurable | Configurable | Both; adults + paediatric | Lever switches between Mapleson A, D, E modes |
| Enclosed Magill | A | Exhaled gas | Fresh gas | Spontaneous | Coaxial APL at machine end |
| Miniaturised coaxial | D/F variants | Fresh gas | Exhaled gas | Paediatric (10-20 kg) | Reduced dead space |
paediatric breathing circuits for md anaesthesia exam
~/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>> 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)")
cd /tmp/workspace/anaesthesia-checklist && python build_paed_circuits.py 2>&1
Paediatric Breathing Circuits MD Anaesthesia
PDF Document · PDF
| Parameter | Neonate/Infant | Clinical Impact |
|---|---|---|
| Tidal volume | 15–25 mL (6–8 mL/kg) | Apparatus dead space must be <1 mL/kg |
| Respiratory rate | 30–60/min | High FGF per minute needed |
| Airway resistance | High (narrow airways) | No unidirectional valves for neonates |
| O₂ consumption | 6–8 mL/kg/min (3× adult) | Adequate FGF always essential |
| Heat/humidity loss | Major (large BSA:weight ratio) | HME filters mandatory; consider low FGF |
| Ventilation | Spontaneous ONLY (IPPV unreliable - risk of barotrauma from thumb occlusion) |
| Best for | Neonates < 10 kg; resource-limited settings; head & neck |
| Advantages | No valves → minimal resistance; lightweight; rapid gas change-over |
| Disadvantages | No IPPV; poor scavenging; no heat/humidity; high FGF |
| Ventilation | Both spontaneous AND controlled ✓ |
| Best for | Neonates and infants < 10–20 kg; most popular paediatric circuit worldwide |
| Advantages | Low resistance; minimal dead space; "feel" of ventilation through bag; both modes; rapid gas change |
| Disadvantages | High FGF; poor scavenging; no heat/humidity; less familiar to those trained on circle systems |
| Circuit | Spontaneous | Controlled |
|---|---|---|
| Ayre's T-piece | 2.5× MV | Not reliable |
| Jackson-Rees | 2.5× MV | 2.5× MV |
| Bain | 200–300 mL/kg/min | 70 mL/kg/min |
| Paediatric Circle | ≥ 0.5–1× MV | 200 mL/min (low flow) |
humidification methods in anaesthesia 10 marks question
~/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)")
cd /tmp/workspace/anaesthesia-checklist && python build_humidification_pdf.py 2>&1
# 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
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: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
Humidification Methods Anaesthesia MD
PDF Document · PDF
| Term | Definition | Clinical Value |
|---|---|---|
| Absolute humidity | Mass of water vapour per litre of gas (mg/L) | 44 mg/L at 37°C, 100% RH |
| Relative humidity | Actual water content / maximum possible at that temperature (%) | Target 100% at alveolar level |
| ISB | Point where gas reaches 37°C, 100% RH | Normally at carina; shifts distally with intubation |
| Humidity deficit | 44 - actual inspired water content | > 34 mg/L with dry anaesthetic gases |
| System | Effect | Clinical Result |
|---|---|---|
| Mucociliary | Ciliary dysfunction + mucosal dehydration | Secretion retention → mucous plugging → atelectasis |
| Secretions | Inspissation (drying) | ETT blockage especially small paediatric tubes |
| Thermal | Heat of vaporisation lost (560 cal/g) | 5–10% of total intraoperative heat loss → hypothermia |
| Mucosal | Epithelial damage | Pneumonia risk; increased airway reactivity |
| V/Q | Atelectasis from plugging | Hypoxaemia post-extubation |
| Type | Material | Output | Dead Space |
|---|---|---|---|
| Simple condenser | Metal gauze/foam | 20–30 mg H₂O/L | 5–10 mL |
| Hygroscopic (HCH) | LiCl / CaCl₂ | 25–35 mg H₂O/L | 15–30 mL |
| Hydrophobic HME | Pleated membrane | 22–28 mg H₂O/L; good filtration | 30–50 mL |
| HMEF (HME + Filter) | Hygroscopic + electrostatic | 26–32 mg H₂O/L + viral/bacterial filter | 30–60+ mL |
| Type | Mechanism | Notes |
|---|---|---|
| Passover humidifier | Gas flows over heated water surface | Simple, moderate output |
| Bubble-through humidifier | Gas bubbled through heated water | Higher output |
| Wick humidifier | Gas through saturated wick | Uniform humidification |
| Heated water bath + heated-wire circuit (HH + HWC) | Water heated to 40–42°C; wires in circuit prevent condensation | Gold standard — achieves 100% RH at 37°C |
| Vapour-phase humidifier | Sterile water vaporised by heating element | ICU standard |
| Feature | Passive HME | Active HH + HWC | Circle (Low FGF) |
|---|---|---|---|
| Humidity output | 20–35 mg/L | 35–44 mg/L (100% RH) | 25–30 mg/L |
| Filtration | HMEF: Yes | No | No |
| Thermal injury risk | None | Yes (if >41°C) | None |
| Dead space added | 30–60+ mL | Minimal | None |
| Infection risk | Low | Higher (standing water) | Low |
| Best for | Short/medium procedures; infection control | Neonates; prolonged surgery; lung disease | Adult routine anaesthesia |