Correction of Respiratory Acidosis Step by step with BiPaP ,Titration,maintaince and further guidance with values of ABGs n settings for BiPaP according
"respiratory acidosis" AND "noninvasive ventilation" AND "BiPAP"
"noninvasive ventilation" AND "hypercapnic respiratory failure" AND guidelines
BiPAP settings titration respiratory acidosis IPAP EPAP starting values ABG targets 2024
| Parameter | Normal | Acute Resp. Acidosis | Chronic Resp. Acidosis |
|---|---|---|---|
| pH | 7.35–7.45 | < 7.35 | < 7.35 (partially compensated) |
| PaCO₂ | 35–45 mmHg | > 45 mmHg (↑) | > 45 mmHg (↑) |
| HCO₃⁻ | 22–26 mEq/L | ↑ 1 mEq/L per 10 mmHg ↑PaCO₂ | ↑ 4 mEq/L per 10 mmHg ↑PaCO₂ |
| PaO₂ | 80–100 mmHg | Often ↓ (hypoxemia coexists) | Often ↓ |
| Criterion | Value |
|---|---|
| pH | 7.25–7.35 (the sweet spot — acidotic but not yet critically so) |
| PaCO₂ | > 45 mmHg with clinical distress |
| Respiratory rate | > 25 breaths/min |
| SpO₂ | < 90% on supplemental O₂ |
| Parameter | Starting Value | Notes |
|---|---|---|
| Mode | S/T (Spontaneous-Timed) | Preferred for hypercapnia |
| IPAP | 10–15 cmH₂O | Start at 10–12; titrate up |
| EPAP | 4–6 cmH₂O | Lower EPAP in pure hypercapnia (COPD) |
| Pressure Support (PS = IPAP−EPAP) | ≥ 6–8 cmH₂O initially, target > 10 | Drives tidal volume and CO₂ clearance |
| Backup RR | 12–16 breaths/min | Prevent apnea; set close to patient's own RR |
| FiO₂ | 0.50–1.0 | Start at 1.0, wean to SpO₂ 88–92% |
| Rise time | Short (1–2) | Shorter = faster pressurization → better comfort in obstructive disease |
| I:E ratio | 1:2 to 1:3 (COPD) / 1:1 (NMD) | Longer expiratory time → prevents dynamic hyperinflation in COPD |
| Ti (inspiratory time) | Short | Reduces risk of auto-PEEP in COPD |
For pure hypoxemia (Type I): IPAP 12–14 / EPAP 8 cmH₂O, FiO₂ 1.0
Step 1: Apply mask, start IPAP 10 / EPAP 4–5 cmH₂O, FiO₂ 1.0
Step 2: Observe for 5–10 min — patient comfort, synchrony, RR, SpO₂
Step 3: Increase IPAP by 2 cmH₂O increments if:
✓ WOB still increased
✓ RR still > 25
✓ Tidal volume inadequate (target 6–8 mL/kg IBW)
Step 4: Increase EPAP by 1–2 cmH₂O if:
✓ SpO₂ still < 88–92% despite adequate IPAP
✓ Auto-PEEP suspected (note: also re-raise IPAP to maintain PS)
Step 5: Check ABG at 60–90 minutes
| ABG Parameter | Target |
|---|---|
| pH | > 7.35 |
| PaCO₂ | Gradual reduction (do NOT drop > 10 mmHg/hr — risk of post-hypercapnic alkalosis + seizures) |
| PaO₂ | 55–80 mmHg (or SpO₂ 88–92% in COPD — avoid hyperoxia) |
| HCO₃⁻ | Will remain elevated until kidneys excrete the buffer load (days) |
| Limit | Reason |
|---|---|
| Max IPAP ≤ 20–25 cmH₂O | Above this → gastric insufflation, mask leak, discomfort |
| BTS guideline: up to 30 cmH₂O with expert review | For refractory hypercapnia |
| High-intensity BiPAP: PS > 15 cmH₂O | Recommended for stable hypercapnic COPD (home NIV) |
| Time | Action |
|---|---|
| Baseline | ABG before starting BiPAP |
| 1 hour | First follow-up ABG — most critical check |
| 4 hours | Second ABG if any setting changes made |
| Every 4–6 hours | Ongoing if clinically stable |
| After each setting change | Repeat ABG 1 hour after any adjustment |
Step 1: Reduce IPAP by 2 cmH₂O every 2–4 hours
Step 2: Reduce FiO₂ toward 0.21–0.35 as tolerated
Step 3: Trial breaks (1–2 hours off BiPAP with supplemental O₂ via nasal cannula)
Step 4: Check ABG 30–60 min after removal of BiPAP
Step 5: Wean EPAP last — reduce to 4 cmH₂O before discontinuing
BIPAP FOR RESPIRATORY ACIDOSIS — QUICK GUIDE
INDICATION: pH 7.25–7.35, PaCO₂ > 45, ↑WOB, RR > 25
START: IPAP 10–12 / EPAP 4–5 / FiO₂ 1.0
Mode: S/T, RR backup 12–16/min
I:E 1:2–1:3 (COPD), Short Ti, Short rise time
TITRATE: ↑ IPAP by 2 cmH₂O q5–10 min for ↑WOB / ↑RR / ↑PaCO₂
↑ EPAP by 1–2 cmH₂O for refractory hypoxemia
Max IPAP: 20–25 cmH₂O (up to 30 with expert review)
Target PS (IPAP-EPAP) > 10–15 cmH₂O
ABG GOAL: pH > 7.35, PaCO₂ ↓ gradually (<10 mmHg/hr)
PaO₂ 55–80 (SpO₂ 88–92% in COPD)
CHECK ABG: 1 hr, 4 hrs, then q4–6 hrs / after each change
FAILURE: pH < 7.25 at 1 hr, ↓GCS, ↑RR → INTUBATE
Create a PDF protocol for acute respiratory acidosis management in your institution
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace not found"
mkdir -p /home/daytona/workspace/resp-acidosis-protocol && python3 -c "from reportlab.lib.pagesizes import A4; print('reportlab ok')" && python3 -c "from reportlab.platypus import SimpleDocTemplate; print('platypus ok')"
~/resp-acidosis-protocol/build_protocol.py
#!/usr/bin/env python3
"""
Generate: Acute Respiratory Acidosis & BiPAP Management Protocol PDF
"""
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_RIGHT, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, KeepTogether, PageBreak
)
from reportlab.platypus.flowables import BalancedColumns
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import Drawing, Rect, String, Line, Arrow
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import os
# ─── Output path ────────────────────────────────────────────────────────────
OUT = "/home/daytona/workspace/resp-acidosis-protocol/Acute_Respiratory_Acidosis_Protocol.pdf"
# ─── Colour palette ──────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1A3557")
TEAL = colors.HexColor("#1B6CA8")
LIGHT_BLUE= colors.HexColor("#D6E8F7")
LIGHT_TEAL= colors.HexColor("#E8F4FD")
RED = colors.HexColor("#C0392B")
LIGHT_RED = colors.HexColor("#FADBD8")
GREEN = colors.HexColor("#1E8449")
LIGHT_GRN = colors.HexColor("#D5F5E3")
AMBER = colors.HexColor("#D68910")
LIGHT_AMB = colors.HexColor("#FEF9E7")
GREY = colors.HexColor("#566573")
LIGHT_GRY = colors.HexColor("#F2F3F4")
WHITE = colors.white
BLACK = colors.black
ORANGE = colors.HexColor("#E67E22")
LIGHT_ORG = colors.HexColor("#FDEBD0")
# ─── Page geometry ───────────────────────────────────────────────────────────
PAGE_W, PAGE_H = A4 # 595.3 x 841.9 pt
MARGIN_L = 1.8*cm
MARGIN_R = 1.8*cm
MARGIN_T = 2.2*cm
MARGIN_B = 2.0*cm
CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R
# ─── Styles ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
STYLE = {
"title": S("ptitle", fontSize=22, fontName="Helvetica-Bold",
textColor=WHITE, alignment=TA_CENTER, leading=26,
spaceAfter=2),
"subtitle": S("psubt", fontSize=11, fontName="Helvetica",
textColor=LIGHT_BLUE, alignment=TA_CENTER, leading=14),
"meta": S("pmeta", fontSize=8.5, fontName="Helvetica",
textColor=LIGHT_GRY, alignment=TA_CENTER, leading=11),
"section": S("psect", fontSize=12, fontName="Helvetica-Bold",
textColor=WHITE, alignment=TA_LEFT, leading=15,
leftIndent=4),
"h2": S("ph2", fontSize=10.5, fontName="Helvetica-Bold",
textColor=NAVY, leading=13, spaceAfter=3, spaceBefore=6),
"body": S("pbody", fontSize=9, fontName="Helvetica",
textColor=BLACK, leading=12, spaceAfter=2),
"body_sm": S("pbsm", fontSize=8, fontName="Helvetica",
textColor=BLACK, leading=11),
"bold": S("pbold", fontSize=9, fontName="Helvetica-Bold",
textColor=NAVY, leading=12),
"bullet": S("pbull", fontSize=9, fontName="Helvetica",
textColor=BLACK, leading=12, leftIndent=12,
bulletIndent=4, spaceAfter=1),
"bullet_red": S("pbull_r", fontSize=9, fontName="Helvetica",
textColor=RED, leading=12, leftIndent=12,
bulletIndent=4, spaceAfter=1),
"bullet_grn": S("pbull_g", fontSize=9, fontName="Helvetica",
textColor=GREEN, leading=12, leftIndent=12,
bulletIndent=4, spaceAfter=1),
"caption": S("pcap", fontSize=7.5, fontName="Helvetica-Oblique",
textColor=GREY, alignment=TA_CENTER, leading=10),
"footer": S("pfooter", fontSize=7, fontName="Helvetica",
textColor=GREY, alignment=TA_CENTER, leading=9),
"th": S("pth", fontSize=8.5, fontName="Helvetica-Bold",
textColor=WHITE, alignment=TA_CENTER, leading=11),
"td": S("ptd", fontSize=8.5, fontName="Helvetica",
textColor=BLACK, alignment=TA_LEFT, leading=11),
"td_c": S("ptdc", fontSize=8.5, fontName="Helvetica",
textColor=BLACK, alignment=TA_CENTER, leading=11),
"td_bold": S("ptdb", fontSize=8.5, fontName="Helvetica-Bold",
textColor=NAVY, alignment=TA_LEFT, leading=11),
"warning": S("pwarn", fontSize=9, fontName="Helvetica-Bold",
textColor=RED, alignment=TA_CENTER, leading=12),
"note": S("pnote", fontSize=8, fontName="Helvetica-Oblique",
textColor=GREY, leading=10, spaceAfter=2),
"box_title": S("pboxt", fontSize=10, fontName="Helvetica-Bold",
textColor=NAVY, leading=13, spaceAfter=2),
"ref": S("pref", fontSize=7.5, fontName="Helvetica",
textColor=GREY, leading=10),
}
# ─── Helpers ─────────────────────────────────────────────────────────────────
def section_header(text, color=NAVY):
"""Coloured section bar."""
data = [[Paragraph(text, STYLE["section"])]]
t = Table(data, colWidths=[CONTENT_W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), color),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
("ROUNDEDCORNERS", [4]),
]))
return t
def simple_table(headers, rows, col_widths, hdr_color=NAVY,
alt_color=LIGHT_TEAL, row_color=WHITE):
hdr_cells = [Paragraph(h, STYLE["th"]) for h in headers]
data = [hdr_cells]
for i, row in enumerate(rows):
cells = []
for j, cell in enumerate(row):
if isinstance(cell, str):
st = STYLE["td_bold"] if j == 0 else STYLE["td_c"] if j > 0 else STYLE["td"]
cells.append(Paragraph(cell, st))
else:
cells.append(cell)
data.append(cells)
t = Table(data, colWidths=col_widths, repeatRows=1)
style_cmds = [
("BACKGROUND", (0,0),(-1,0), hdr_color),
("TEXTCOLOR", (0,0),(-1,0), WHITE),
("FONTNAME", (0,0),(-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0),(-1,0), 8.5),
("ALIGN", (0,0),(-1,0), "CENTER"),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("GRID", (0,0),(-1,-1), 0.4, colors.HexColor("#BDC3C7")),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
]
for i in range(1, len(data)):
bg = alt_color if i % 2 == 0 else row_color
style_cmds.append(("BACKGROUND", (0,i),(-1,i), bg))
t.setStyle(TableStyle(style_cmds))
return t
def coloured_box(content_flowables, bg=LIGHT_TEAL, border=TEAL, radius=4):
"""Wrap flowables in a coloured box using a single-cell table."""
inner = []
for f in content_flowables:
inner.append(f)
data = [[inner]]
t = Table([[inner]], colWidths=[CONTENT_W - 4])
t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), bg),
("BOX", (0,0),(-1,-1), 0.8, border),
("TOPPADDING", (0,0),(-1,-1), 8),
("BOTTOMPADDING", (0,0),(-1,-1), 8),
("LEFTPADDING", (0,0),(-1,-1), 10),
("RIGHTPADDING", (0,0),(-1,-1), 10),
("VALIGN", (0,0),(-1,-1), "TOP"),
]))
return t
def HR(color=TEAL, thickness=1):
return HRFlowable(width="100%", thickness=thickness,
color=color, spaceAfter=4, spaceBefore=4)
def SP(h=4):
return Spacer(1, h)
def B(text, style="bullet"):
return Paragraph(f"• {text}", STYLE[style])
def two_col(left_items, right_items, col_w=None):
"""Two-column layout."""
if col_w is None:
col_w = [CONTENT_W/2 - 4, CONTENT_W/2 - 4]
data = [[left_items, right_items]]
t = Table(data, colWidths=col_w)
t.setStyle(TableStyle([
("VALIGN", (0,0),(-1,-1), "TOP"),
("LEFTPADDING", (0,0),(-1,-1), 2),
("RIGHTPADDING", (0,0),(-1,-1), 2),
("TOPPADDING", (0,0),(-1,-1), 0),
("BOTTOMPADDING",(0,0),(-1,-1), 0),
]))
return t
# ─── Header / Footer ─────────────────────────────────────────────────────────
def on_page(canvas, doc):
canvas.saveState()
w, h = A4
# Header bar
canvas.setFillColor(NAVY)
canvas.rect(0, h - 1.2*cm, w, 1.2*cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica-Bold", 8)
canvas.drawString(MARGIN_L, h - 0.85*cm,
"ACUTE RESPIRATORY ACIDOSIS — BiPAP MANAGEMENT PROTOCOL")
canvas.setFont("Helvetica", 7.5)
canvas.drawRightString(w - MARGIN_R, h - 0.85*cm,
f"Page {doc.page}")
# Footer
canvas.setFillColor(NAVY)
canvas.rect(0, 0, w, 0.9*cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 7)
canvas.drawCentredString(w/2, 0.32*cm,
"Internal Clinical Protocol | Respiratory/Critical Care | "
"Review Date: May 2027 | Version 1.0")
canvas.restoreState()
def on_first_page(canvas, doc):
# No running header on cover
canvas.saveState()
w, h = A4
canvas.setFillColor(NAVY)
canvas.rect(0, 0, w, 0.9*cm, fill=1, stroke=0)
canvas.setFillColor(WHITE)
canvas.setFont("Helvetica", 7)
canvas.drawCentredString(w/2, 0.32*cm,
"Internal Clinical Protocol | Respiratory/Critical Care | "
"Review Date: May 2027 | Version 1.0")
canvas.restoreState()
# ─── Build document ──────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUT, pagesize=A4,
leftMargin=MARGIN_L, rightMargin=MARGIN_R,
topMargin=MARGIN_T + 0.6*cm, bottomMargin=MARGIN_B + 0.4*cm,
title="Acute Respiratory Acidosis & BiPAP Protocol",
author="Respiratory/Critical Care Department",
subject="Clinical Management Protocol"
)
story = []
# ══════════════════════════════════════════════════════════════════════════════
# COVER
# ══════════════════════════════════════════════════════════════════════════════
cover_title = Table(
[[Paragraph("ACUTE RESPIRATORY ACIDOSIS", STYLE["title"])],
[Paragraph("BiPAP Management Protocol", STYLE["subtitle"])],
[SP(4)],
[Paragraph(
"Respiratory Medicine · Critical Care · Emergency Department",
STYLE["meta"])]],
colWidths=[CONTENT_W]
)
cover_title.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,-1), NAVY),
("TOPPADDING", (0,0),(-1,-1), 12),
("BOTTOMPADDING", (0,0),(-1,-1), 12),
("LEFTPADDING", (0,0),(-1,-1), 16),
("RIGHTPADDING", (0,0),(-1,-1), 16),
("ROUNDEDCORNERS",[6]),
]))
story += [cover_title, SP(8)]
# Version info box
meta_rows = [
["Document No.", "RCC-PROT-001", "Version", "1.0"],
["Effective Date", "May 2026", "Review Date", "May 2027"],
["Approved By", "Clinical Director", "Department", "Resp / Critical Care"],
]
meta_t = Table(
[[Paragraph(c, STYLE["body_sm"]) for c in row] for row in meta_rows],
colWidths=[3.2*cm, 4.8*cm, 3.2*cm, 4.8*cm]
)
meta_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(0,-1), LIGHT_BLUE),
("BACKGROUND", (2,0),(2,-1), LIGHT_BLUE),
("FONTNAME", (0,0),(0,-1), "Helvetica-Bold"),
("FONTNAME", (2,0),(2,-1), "Helvetica-Bold"),
("GRID", (0,0),(-1,-1), 0.4, colors.HexColor("#BDC3C7")),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
]))
story += [meta_t, SP(10)]
# ── Purpose box ─────────────────────────────────────────────────────────────
purpose_items = [
Paragraph("PURPOSE & SCOPE", STYLE["box_title"]),
SP(3),
Paragraph(
"This protocol provides a standardised, evidence-based framework for the "
"recognition, initial management, BiPAP titration, monitoring, weaning, and "
"escalation of care for adult patients presenting with <b>acute or acute-on-chronic "
"respiratory acidosis</b>. It applies to Emergency Department, Medical Ward, High "
"Dependency Unit, and ICU settings.",
STYLE["body"]),
SP(4),
Paragraph("<b>Target Users:</b> Physicians, Residents, Nurses, Respiratory Therapists",
STYLE["body"]),
Paragraph("<b>Population:</b> Adult patients (≥ 18 years) with pH < 7.35 and PaCO₂ > 45 mmHg",
STYLE["body"]),
]
story += [coloured_box(purpose_items, bg=LIGHT_TEAL, border=TEAL), SP(10)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1 — DIAGNOSTIC CRITERIA
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("1. DIAGNOSTIC CRITERIA — RESPIRATORY ACIDOSIS"), SP(6)]
story += [Paragraph("1.1 ABG Interpretation", STYLE["h2"])]
abg_headers = ["Parameter", "Normal Range", "Respiratory Acidosis", "Clinical Significance"]
abg_rows = [
["pH", "7.35 – 7.45", "< 7.35", "Primary acidaemia"],
["PaCO₂", "35 – 45 mmHg", "> 45 mmHg (↑)", "Primary defect — hypoventilation"],
["HCO₃⁻", "22 – 26 mEq/L","↑ (compensation)", "Acute: +1 / Chronic: +4 per 10 mmHg ↑CO₂"],
["PaO₂", "80–100 mmHg", "Often ↓", "Hypoxaemia frequently coexists"],
["SpO₂", "95–100%", "Often < 90%", "Target 88–92% in COPD during O₂ therapy"],
["HCO₃⁻ max","—", "Rarely > 38 mEq/L", "Upper limit of renal compensation"],
]
story += [simple_table(abg_headers, abg_rows,
[3.2*cm, 3.5*cm, 4.2*cm, 5.1*cm]), SP(6)]
story += [Paragraph("1.2 Compensation Formulae", STYLE["h2"])]
comp_data = [
[Paragraph("Type", STYLE["th"]),
Paragraph("Duration", STYLE["th"]),
Paragraph("Expected HCO₃⁻ Rise", STYLE["th"]),
Paragraph("Expected pH", STYLE["th"])],
[Paragraph("Acute", STYLE["td_bold"]),
Paragraph("< 24 hours", STYLE["td_c"]),
Paragraph("+1 mEq/L per 10 mmHg ↑ PaCO₂", STYLE["td"]),
Paragraph("Falls ~0.08 per 10 mmHg ↑ PaCO₂", STYLE["td"])],
[Paragraph("Chronic", STYLE["td_bold"]),
Paragraph("> 24 hours", STYLE["td_c"]),
Paragraph("+4 mEq/L per 10 mmHg ↑ PaCO₂", STYLE["td"]),
Paragraph("Falls ~0.03 per 10 mmHg ↑ PaCO₂", STYLE["td"])],
]
comp_t = Table(comp_data, colWidths=[3.5*cm, 3.0*cm, 5.5*cm, 4.0*cm])
comp_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("BACKGROUND", (0,2),(-1,2), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.4, colors.HexColor("#BDC3C7")),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
]))
story += [comp_t, SP(6)]
story += [Paragraph("1.3 Common Causes", STYLE["h2"])]
causes_left = [
Paragraph("<b>Airway / Pulmonary</b>", STYLE["bold"]),
B("COPD acute exacerbation"),
B("Severe asthma (status asthmaticus)"),
B("Pneumonia with respiratory fatigue"),
B("Pulmonary oedema"),
B("Pneumothorax / haemothorax"),
SP(4),
Paragraph("<b>Neuromuscular</b>", STYLE["bold"]),
B("Myasthenia gravis crisis"),
B("Guillain-Barré syndrome"),
B("Spinal cord injury (C3–C5)"),
B("Amyotrophic lateral sclerosis"),
]
causes_right = [
Paragraph("<b>CNS / Ventilatory Drive</b>", STYLE["bold"]),
B("Opioid / sedative overdose"),
B("Brainstem stroke"),
B("Head trauma"),
B("Obesity-hypoventilation syndrome (OHS)"),
SP(4),
Paragraph("<b>Mechanical / Iatrogenic</b>", STYLE["bold"]),
B("Ventilator malfunction / misadjustment"),
B("Bronchospasm post-intubation"),
B("Permissive hypercapnia (ARDS management)"),
B("Post-operative splinting"),
]
story += [two_col(causes_left, causes_right), SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2 — INITIAL ASSESSMENT
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("2. INITIAL CLINICAL ASSESSMENT"), SP(6)]
assess_items = [
Paragraph("2.1 Rapid 5-Point Assessment (perform simultaneously with ABG)", STYLE["h2"]),
]
assess_tbl_data = [
[Paragraph("Step", STYLE["th"]), Paragraph("Assessment", STYLE["th"]),
Paragraph("Key Findings", STYLE["th"]), Paragraph("Action", STYLE["th"])],
[Paragraph("1", STYLE["td_c"]), Paragraph("Airway patency", STYLE["td_bold"]),
Paragraph("Obstruction, secretions, stridor", STYLE["td"]),
Paragraph("Suction, jaw thrust, OPA/NPA if needed", STYLE["td"])],
[Paragraph("2", STYLE["td_c"]), Paragraph("Breathing pattern", STYLE["td_bold"]),
Paragraph("RR, depth, accessory muscles, paradox", STYLE["td"]),
Paragraph("SpO₂, ETCO₂ if available", STYLE["td"])],
[Paragraph("3", STYLE["td_c"]), Paragraph("Consciousness (GCS)", STYLE["td_bold"]),
Paragraph("GCS < 8 = intubation threshold", STYLE["td"]),
Paragraph("GCS ≤ 8 → call anaesthesia/ICU stat", STYLE["td"])],
[Paragraph("4", STYLE["td_c"]), Paragraph("Haemodynamics", STYLE["td_bold"]),
Paragraph("BP, HR, perfusion, CO₂ on VBG", STYLE["td"]),
Paragraph("Shock → stabilise before NIV", STYLE["td"])],
[Paragraph("5", STYLE["td_c"]), Paragraph("ABG + labs", STYLE["td_bold"]),
Paragraph("pH, PaCO₂, PaO₂, HCO₃⁻, lactate, K⁺", STYLE["td"]),
Paragraph("Guides NIV vs. intubation decision", STYLE["td"])],
]
assess_t = Table(assess_tbl_data, colWidths=[1.4*cm, 3.8*cm, 5.0*cm, 5.8*cm])
assess_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("BACKGROUND", (0,2),(-1,2), LIGHT_TEAL),
("BACKGROUND", (0,4),(-1,4), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.4, colors.HexColor("#BDC3C7")),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
("ALIGN", (0,0),(0,-1), "CENTER"),
]))
story += [assess_t, SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3 — DECISION PATHWAY
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("3. DECISION PATHWAY — BiPAP vs. INTUBATION"), SP(6)]
# NIV indications
niv_ind = [
Paragraph("✔ INDICATIONS FOR BiPAP (NIV)", STYLE["h2"]),
B("pH 7.25 – 7.35 with PaCO₂ > 45 mmHg", "bullet"),
B("Respiratory rate > 25 breaths/min", "bullet"),
B("Moderate–severe dyspnoea with increased WOB", "bullet"),
B("SpO₂ < 90% despite supplemental oxygen", "bullet"),
B("COPD exacerbation with acute hypercapnia", "bullet"),
B("Cardiogenic pulmonary oedema + hypercapnia", "bullet"),
B("OHS / neuromuscular disease exacerbation", "bullet"),
B("Patient awake, cooperative, haemodynamically stable", "bullet"),
]
niv_ci = [
Paragraph("✘ CONTRAINDICATIONS (→ Intubate)", STYLE["h2"]),
B("pH < 7.25 and rapidly deteriorating", "bullet_red"),
B("GCS ≤ 8 / inability to protect airway", "bullet_red"),
B("Respiratory arrest or gasping", "bullet_red"),
B("Haemodynamic instability / refractory shock", "bullet_red"),
B("Copious secretions / high aspiration risk", "bullet_red"),
B("Uncooperative / agitated patient", "bullet_red"),
B("Facial trauma or deformity preventing mask seal", "bullet_red"),
B("Recent upper GI surgery (relative)", "bullet_red"),
]
story += [two_col(
coloured_box(niv_ind, bg=LIGHT_GRN, border=GREEN),
coloured_box(niv_ci, bg=LIGHT_RED, border=RED)
), SP(6)]
# NIV failure predictors
fail_items = [
Paragraph("⚠ PREDICTORS OF NIV FAILURE (escalate to ICU early if present)", STYLE["warning"]),
SP(3),
two_col(
[B("GCS < 11 at initiation", "bullet"),
B("Sustained pH < 7.25 after 60 min of NIV", "bullet"),
B("RR > 35 breaths/min not responding to NIV", "bullet")],
[B("Worsening encephalopathy on NIV", "bullet"),
B("Inability to reduce PaCO₂ after 2 hours", "bullet"),
B("NNT for intubation = 1 if all 3 criteria present", "bullet")]
)
]
story += [coloured_box(fail_items, bg=LIGHT_RED, border=RED), SP(10)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4 — BiPAP SETUP
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("4. BiPAP SETUP & INITIAL SETTINGS"), SP(6)]
story += [Paragraph("4.1 Interface Selection", STYLE["h2"])]
iface_data = [
[Paragraph("Interface", STYLE["th"]), Paragraph("First Choice", STYLE["th"]),
Paragraph("Indication", STYLE["th"]), Paragraph("Notes", STYLE["th"])],
[Paragraph("Full-face (oronasal) mask", STYLE["td_bold"]),
Paragraph("✔ YES", STYLE["td_c"]),
Paragraph("All acute hypercapnic RF", STYLE["td"]),
Paragraph("Lower leak rate; recommended as first choice", STYLE["td"])],
[Paragraph("Nasal mask", STYLE["td_bold"]),
Paragraph("Second line", STYLE["td_c"]),
Paragraph("If oronasal not tolerated", STYLE["td"]),
Paragraph("Higher leak; patient must keep mouth closed", STYLE["td"])],
[Paragraph("Helmet interface", STYLE["td_bold"]),
Paragraph("Third line", STYLE["td_c"]),
Paragraph("Prolonged NIV, skin breakdown", STYLE["td"]),
Paragraph("Specialised; check dead space ventilation", STYLE["td"])],
]
iface_t = Table(iface_data, colWidths=[4.5*cm, 2.4*cm, 4.5*cm, 4.6*cm])
iface_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("BACKGROUND", (0,1),(-1,1), LIGHT_GRN),
("BACKGROUND", (0,3),(-1,3), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.4, colors.HexColor("#BDC3C7")),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
]))
story += [iface_t, SP(6)]
story += [Paragraph("4.2 Mode Selection", STYLE["h2"])]
mode_items = [
B("<b>S/T (Spontaneous-Timed)</b> — PREFERRED for all acute hypercapnic RF. Delivers pressure support when patient triggers; backup rate provides ventilation if apnoea occurs.", "bullet"),
B("S (Spontaneous) — for patients with reliable respiratory drive and mild disease only.", "bullet"),
B("T (Timed) — for neuromuscular disease with unreliable drive.", "bullet"),
B("<b>Pressure Control / AVAPS</b> — consider for home NIV or OHS if standard S/T insufficient.", "bullet"),
SP(2),
Paragraph("⚠ Avoid Pressure Control (PC) mode in COPD — patient-generated tachypnoea "
"delivers all breaths at set Ti, worsening dynamic hyperinflation.", STYLE["note"]),
]
story += mode_items + [SP(6)]
story += [Paragraph("4.3 Initial Settings by Clinical Phenotype", STYLE["h2"])]
settings_headers = ["Setting", "COPD Exacerbation", "Cardiogenic Oedema", "OHS / NMD",
"Rationale"]
settings_rows = [
["Mode", "S/T", "S/T or CPAP", "S/T", "Backup RR protects apnoeic patients"],
["IPAP (cmH₂O)", "10–12 start\n→ target 20–25", "10–14", "12–16", "Drives tidal volume & CO₂ clearance"],
["EPAP (cmH₂O)", "4–5", "6–8", "4–6", "Higher EPAP for hypoxaemia/oedema"],
["PS = IPAP−EPAP", "≥ 6–8\n→ target ≥ 15", "≥ 6", "≥ 8–10", "Determines tidal volume and work offloading"],
["Backup RR", "12–16/min", "12–16/min", "14–18/min", "Set near patient's own RR"],
["FiO₂", "0.50–1.0\n(wean to SpO₂ 88–92%)", "1.0\n(wean to 94–98%)", "0.50–1.0", "Avoid hyperoxia in COPD/OHS"],
["I:E ratio", "1:2 to 1:3", "1:1 to 1:2", "1:1 to 1:2", "Longer expiration prevents auto-PEEP (COPD)"],
["Rise time", "Short (1–2)", "Medium (2–3)", "Short-Med", "Faster rise improves comfort in obstruction"],
["Ti", "Short", "Standard", "Standard", "Short Ti → longer expiration in COPD"],
]
col_w = [3.0*cm, 3.2*cm, 3.2*cm, 2.8*cm, 3.8*cm]
story += [simple_table(settings_headers, settings_rows, col_w, hdr_color=TEAL), SP(6)]
# Limits box
limits_items = [
Paragraph("⚠ PRESSURE LIMITS — DO NOT EXCEED WITHOUT SENIOR REVIEW", STYLE["warning"]),
SP(4),
two_col(
[B("<b>Max IPAP: 20–25 cmH₂O</b> (routine)", "bullet"),
B("Up to 30 cmH₂O with consultant review (BTS)", "bullet"),
B("Risk above 25: gastric insufflation, mask leak", "bullet")],
[B("<b>Max EPAP: 8 cmH₂O</b> without expert review", "bullet"),
B("If EPAP increased → also raise IPAP to maintain PS", "bullet"),
B("High-intensity BiPAP: PS > 15 cmH₂O (home COPD NIV)", "bullet")]
)
]
story += [coloured_box(limits_items, bg=LIGHT_AMB, border=AMBER), SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5 — TITRATION PROTOCOL
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("5. TITRATION PROTOCOL"), SP(6)]
story += [Paragraph("5.1 Step-by-Step Titration Algorithm", STYLE["h2"])]
# Flowchart as table
flow_data = [
[Paragraph("STEP 1 — INITIATE", STYLE["th"])],
[Paragraph(
"Apply interface · Mode: S/T · IPAP: 10–12 cmH₂O · EPAP: 4–5 cmH₂O · "
"FiO₂: 1.0 · Backup RR: 12–16/min\nExplain to patient; encourage compliance",
STYLE["td"])],
]
flow_t = Table(flow_data, colWidths=[CONTENT_W])
flow_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(0,0), TEAL),
("BACKGROUND", (0,1),(0,1), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.6, TEAL),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
]))
story += [flow_t, SP(3)]
flow2_data = [
[Paragraph("STEP 2 — OBSERVE (first 5–15 minutes)", STYLE["th"])],
[Paragraph(
"Assess: patient comfort · chest rise · patient-ventilator synchrony · "
"SpO₂ · RR · HR\nAdjust mask fit to minimise leak · "
"Offer sedating antihistamine if distressed (NOT opioids/benzodiazepines)",
STYLE["td"])],
]
flow2_t = Table(flow2_data, colWidths=[CONTENT_W])
flow2_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(0,0), TEAL),
("BACKGROUND", (0,1),(0,1), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.6, TEAL),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
]))
story += [flow2_t, SP(3)]
flow3_data = [
[Paragraph("STEP 3 — TITRATE (every 5–10 minutes based on response)", STYLE["th"])],
[Paragraph(
"↑ IPAP by 2 cmH₂O if: WOB still high | RR > 25 | Tidal volume < 6–8 mL/kg IBW | PaCO₂ not improving\n"
"↑ EPAP by 1–2 cmH₂O if: SpO₂ < 88% despite adequate IPAP | Suspected auto-PEEP | "
"(If EPAP ↑, also ↑ IPAP to maintain PS)\n"
"↓ FiO₂ to target SpO₂ 88–92% (COPD) or 92–96% (other) · "
"Titrate by 2–3 cmH₂O per increment · Max IPAP 20–25 cmH₂O",
STYLE["td"])],
]
flow3_t = Table(flow3_data, colWidths=[CONTENT_W])
flow3_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(0,0), TEAL),
("BACKGROUND", (0,1),(0,1), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.6, TEAL),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
]))
story += [flow3_t, SP(3)]
flow4_data = [
[Paragraph("STEP 4 — RECHECK ABG at 60–90 minutes", STYLE["th"])],
[Paragraph(
"pH > 7.35 and improving → Continue, maintain settings, repeat ABG at 4 hrs\n"
"pH 7.25–7.35 and improving → Optimise settings, repeat ABG 1 hr\n"
"pH < 7.25 OR not improving at 60 min → Senior review; consider intubation",
STYLE["td"])],
]
flow4_t = Table(flow4_data, colWidths=[CONTENT_W])
flow4_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(0,0), NAVY),
("BACKGROUND", (0,1),(0,1), LIGHT_BLUE),
("GRID", (0,0),(-1,-1), 0.6, NAVY),
("TOPPADDING", (0,0),(-1,-1), 5),
("BOTTOMPADDING", (0,0),(-1,-1), 5),
("LEFTPADDING", (0,0),(-1,-1), 8),
("RIGHTPADDING", (0,0),(-1,-1), 8),
]))
story += [flow4_t, SP(6)]
story += [Paragraph("5.2 ABG Targets During BiPAP", STYLE["h2"])]
target_headers = ["Parameter", "Target During NIV", "COPD-Specific", "Notes"]
target_rows = [
["pH", "> 7.35", "> 7.35", "Primary endpoint of NIV success"],
["PaCO₂", "Gradual ↓", "Toward chronic baseline", "Do NOT drop > 10 mmHg/hr — risk of seizures"],
["PaO₂", "55–80 mmHg", "55–75 mmHg", "Avoid excessive oxygenation in COPD"],
["SpO₂", "92–96%", "88–92%", "Hyperoxia abolishes hypoxic drive"],
["HCO₃⁻", "Will remain ↑", "Days to normalise","Renal excretion of buffer takes days"],
["RR", "< 25/min", "< 25/min", "Falling RR = sign of success"],
["Tidal Vol","6–8 mL/kg IBW", "6–8 mL/kg IBW", "Adjust IPAP to achieve target Vt"],
]
story += [simple_table(target_headers, target_rows,
[3.0*cm, 3.5*cm, 3.5*cm, 6.0*cm], hdr_color=TEAL), SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6 — MONITORING
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("6. MONITORING SCHEDULE"), SP(6)]
mon_headers = ["Time Point", "Continuous", "Intermittent", "ABG / Labs"]
mon_rows = [
["0 min (Baseline)", "SpO₂, HR, RR, BP", "GCS, WOB assessment", "ABG, VBG, FBC, U&E, lactate, CXR"],
["5–15 min", "SpO₂, HR, RR", "Mask fit, synchrony, comfort", "—"],
["30–60 min", "SpO₂, HR, RR", "WOB, GCS", "ABG (most critical check)"],
["4 hours", "SpO₂, HR, RR, BP", "Full clinical review", "ABG; adjust if setting change"],
["Every 4–6 hrs", "SpO₂, HR, RR", "GCS, tolerance", "ABG if clinically indicated"],
["After any change", "SpO₂, HR, RR", "Comfort, synchrony", "ABG 1 hr after change"],
["12 hrs", "Cardiac monitoring","Full assessment", "ABG, U&E, glucose"],
]
story += [simple_table(mon_headers, mon_rows,
[3.2*cm, 4.0*cm, 4.2*cm, 4.6*cm], hdr_color=TEAL), SP(6)]
asyn_items = [
Paragraph("6.1 Managing Patient-Ventilator Asynchrony", STYLE["h2"]),
B("Inspiratory leaks → prolonged inspiration (most common). Fix: adjust mask harness, re-seat mask, try different size.", "bullet"),
B("Expiratory leaks → auto-triggering. Fix: use NIV-dedicated ventilator with leak compensation algorithm.", "bullet"),
B("Asynchrony > 10% of breaths = significant. Adjust trigger sensitivity and cycling criteria.", "bullet"),
B("Persistent asynchrony despite optimisation → consider changing interface type.", "bullet"),
]
story += asyn_items + [SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# PAGE BREAK
# ══════════════════════════════════════════════════════════════════════════════
story += [PageBreak()]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 7 — RESPONSE & FAILURE CRITERIA
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("7. RESPONSE CRITERIA & FAILURE PROTOCOL"), SP(6)]
success_items = [
Paragraph("✔ SIGNS OF NIV SUCCESS — CONTINUE BiPAP", STYLE["h2"]),
B("pH trending up toward > 7.35", "bullet_grn"),
B("PaCO₂ falling gradually (not precipitously)", "bullet_grn"),
B("SpO₂ at target on stable or reduced FiO₂", "bullet_grn"),
B("RR decreasing (< 25 breaths/min)", "bullet_grn"),
B("Accessory muscle use decreasing", "bullet_grn"),
B("Patient subjectively more comfortable", "bullet_grn"),
B("Good patient-ventilator synchrony", "bullet_grn"),
B("GCS stable or improving", "bullet_grn"),
]
failure_items = [
Paragraph("✘ SIGNS OF NIV FAILURE — ESCALATE / INTUBATE", STYLE["h2"]),
B("pH < 7.25 at 60 min despite optimised BiPAP", "bullet_red"),
B("PaCO₂ not improving or worsening", "bullet_red"),
B("GCS falling or < 8", "bullet_red"),
B("RR > 35/min persisting after 60 min", "bullet_red"),
B("Haemodynamic deterioration (SBP < 90 mmHg)", "bullet_red"),
B("Inability to maintain SpO₂ target on BiPAP", "bullet_red"),
B("Excessive secretions / aspiration event", "bullet_red"),
B("Patient unable to tolerate mask", "bullet_red"),
]
story += [two_col(
coloured_box(success_items, bg=LIGHT_GRN, border=GREEN),
coloured_box(failure_items, bg=LIGHT_RED, border=RED)
), SP(4)]
fail_protocol = [
Paragraph("7.1 NIV Failure Action Protocol", STYLE["h2"]),
B("<b>Immediate:</b> Call senior physician (SpR/Consultant) + Anaesthesia if pH < 7.25 at 60 min", "bullet"),
B("<b>Prepare:</b> RSI drugs, intubation equipment, bougie, video laryngoscope", "bullet"),
B("<b>Pre-oxygenate:</b> BiPAP can be used for pre-oxygenation prior to intubation (superior to bag-mask)", "bullet"),
B("<b>Post-intubation targets:</b> Vt 6–8 mL/kg IBW, PEEP 5 cmH₂O, RR 12–16, FiO₂ 1.0 initially", "bullet"),
SP(2),
Paragraph(
"Evidence: NPPV reduces intubation in COPD by 65%; NNT = 12. "
"NIV failure with delayed intubation is associated with higher mortality than early intubation.",
STYLE["note"]),
]
story += fail_protocol + [SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 8 — WEANING
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("8. WEANING FROM BiPAP"), SP(6)]
story += [Paragraph("8.1 Criteria for Initiating Wean (all should be present)", STYLE["h2"])]
wean_criteria = [
B("pH > 7.35 on two consecutive ABGs", "bullet_grn"),
B("PaCO₂ at or near patient's chronic baseline", "bullet_grn"),
B("SpO₂ > 90% on ≤ 4 L/min supplemental O₂", "bullet_grn"),
B("RR < 24 breaths/min consistently", "bullet_grn"),
B("Underlying cause treated / improving", "bullet_grn"),
B("Patient awake, cooperative, managing secretions", "bullet_grn"),
B("Minimum 4 hours continuous BiPAP completed", "bullet_grn"),
]
story += wean_criteria + [SP(5)]
story += [Paragraph("8.2 Step-by-Step Weaning Protocol", STYLE["h2"])]
wean_tbl = [
[Paragraph("Wean Step", STYLE["th"]), Paragraph("Action", STYLE["th"]),
Paragraph("Interval", STYLE["th"]), Paragraph("Monitor / Check", STYLE["th"])],
[Paragraph("Step 1", STYLE["td_bold"]),
Paragraph("↓ FiO₂ toward 0.28–0.35 while maintaining SpO₂ target", STYLE["td"]),
Paragraph("q30–60 min", STYLE["td_c"]),
Paragraph("SpO₂, RR", STYLE["td"])],
[Paragraph("Step 2", STYLE["td_bold"]),
Paragraph("↓ IPAP by 2 cmH₂O every 2–4 hours", STYLE["td"]),
Paragraph("q2–4 hrs", STYLE["td_c"]),
Paragraph("RR, WOB, SpO₂", STYLE["td"])],
[Paragraph("Step 3", STYLE["td_bold"]),
Paragraph("Trial 1–2 hour break from BiPAP with nasal cannula O₂", STYLE["td"]),
Paragraph("Once IPAP ≤ 14", STYLE["td_c"]),
Paragraph("SpO₂, RR, WOB, GCS", STYLE["td"])],
[Paragraph("Step 4", STYLE["td_bold"]),
Paragraph("ABG 30–60 min after BiPAP removal; check for deterioration", STYLE["td"]),
Paragraph("After each break", STYLE["td_c"]),
Paragraph("ABG, clinical assessment", STYLE["td"])],
[Paragraph("Step 5", STYLE["td_bold"]),
Paragraph("↓ EPAP last — reduce to 4 cmH₂O before discontinuing BiPAP", STYLE["td"]),
Paragraph("Final step", STYLE["td_c"]),
Paragraph("Mask tolerance, SpO₂", STYLE["td"])],
[Paragraph("Step 6", STYLE["td_bold"]),
Paragraph("Discharge to ward on supplemental O₂ via Venturi/nasal cannula", STYLE["td"]),
Paragraph("After 2+ successful breaks", STYLE["td_c"]),
Paragraph("Reassess at 24 hrs", STYLE["td"])],
]
wean_t = Table(wean_tbl, colWidths=[2.0*cm, 6.5*cm, 2.8*cm, 4.7*cm])
wean_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("BACKGROUND", (0,2),(-1,2), LIGHT_TEAL),
("BACKGROUND", (0,4),(-1,4), LIGHT_TEAL),
("BACKGROUND", (0,6),(-1,6), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.4, colors.HexColor("#BDC3C7")),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
("ALIGN", (2,1),(2,-1), "CENTER"),
]))
story += [wean_t, SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 9 — MAINTENANCE & CONCURRENT TREATMENTS
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("9. MAINTENANCE & CONCURRENT TREATMENTS"), SP(6)]
maint_left = [
Paragraph("<b>Concurrent Medical Therapy</b>", STYLE["bold"]),
SP(2),
Paragraph("<i>COPD Exacerbation:</i>", STYLE["body"]),
B("Nebulised SABA (salbutamol 2.5–5 mg) q20 min × 3, then q4–6 hrs", "bullet"),
B("Nebulised ipratropium 0.5 mg q6–8 hrs", "bullet"),
B("Prednisolone 40 mg PO/IV × 5 days", "bullet"),
B("Antibiotics if purulent sputum / CRP > 20 / consolidation on CXR", "bullet"),
SP(4),
Paragraph("<i>Cardiogenic Pulmonary Oedema:</i>", STYLE["body"]),
B("IV furosemide 40–80 mg stat (titrate)", "bullet"),
B("IV GTN infusion (if SBP > 100 mmHg)", "bullet"),
B("Treat precipitant (ACS, AF, hypertensive crisis)", "bullet"),
]
maint_right = [
Paragraph("<b>Supportive Care on BiPAP</b>", STYLE["bold"]),
SP(2),
B("Nurse at 30–45° head-of-bed elevation", "bullet"),
B("Continuous SpO₂ and cardiac monitoring × 12 hrs minimum", "bullet"),
B("Oral care and skin protection at mask contact points", "bullet"),
B("NG tube if > 24 hrs NIV or risk of aspiration", "bullet"),
B("DVT prophylaxis once stabilised", "bullet"),
B("Avoid benzodiazepines / opioids during NIV", "bullet"),
B("Nutritional support — do NOT pause NIV for meals until stable", "bullet"),
SP(4),
Paragraph("<b>Minimum Continuous Duration:</b> 4 hrs for acute exacerbation", STYLE["bold"]),
]
story += [two_col(maint_left, maint_right), SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 10 — HOME / LONG-TERM NIV
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("10. HOME NIV — POST-DISCHARGE GUIDANCE"), SP(6)]
home_items = [
Paragraph(
"For patients with <b>persistent hypercapnia</b> (PaCO₂ ≥ 52 mmHg) 2–4 weeks after "
"discharge following an acute COPD exacerbation requiring NIV:",
STYLE["body"]),
SP(4),
]
home_tbl = [
[Paragraph("Parameter", STYLE["th"]), Paragraph("Recommendation", STYLE["th"]),
Paragraph("Evidence", STYLE["th"])],
[Paragraph("Indication threshold", STYLE["td_bold"]),
Paragraph("PaCO₂ ≥ 52 mmHg, 2–4 weeks post-discharge", STYLE["td"]),
Paragraph("NNT = 6 to prevent one readmission (Fishman's)", STYLE["td"])],
[Paragraph("Mode", STYLE["td_bold"]),
Paragraph("BiPAP S/T (or AVAPS for variable disease)", STYLE["td"]),
Paragraph("AVAPS assures consistent Vt despite progression", STYLE["td"])],
[Paragraph("Pressure support target", STYLE["td_bold"]),
Paragraph("IPAP−EPAP ≥ 15 cmH₂O (high-intensity)", STYLE["td"]),
Paragraph("High-PS reduces dynamic hyperinflation, improves V/Q", STYLE["td"])],
[Paragraph("IPAP target (home)", STYLE["td_bold"]),
Paragraph("20–30 cmH₂O (titrate to eucapnia)", STYLE["td"]),
Paragraph("Target PaCO₂ reduction ≥ 20% or to normal", STYLE["td"])],
[Paragraph("Backup RR", STYLE["td_bold"]),
Paragraph("12–16/min (started at 12; titrate)", STYLE["td"]),
Paragraph("I:E 1:3, short rise time", STYLE["td"])],
[Paragraph("Min daily usage", STYLE["td_bold"]),
Paragraph("≥ 4–5 hours/night", STYLE["td"]),
Paragraph("More hours = better CO₂ control", STYLE["td"])],
[Paragraph("Combine with LTOT", STYLE["td_bold"]),
Paragraph("Yes — add to long-term O₂ therapy", STYLE["td"]),
Paragraph("Home NIPPV + LTOT improves admission-free survival", STYLE["td"])],
[Paragraph("Follow-up ABG", STYLE["td_bold"]),
Paragraph("4–6 weeks, then 3–6 monthly", STYLE["td"]),
Paragraph("Titrate settings as disease evolves", STYLE["td"])],
]
home_t = Table(home_tbl, colWidths=[3.8*cm, 5.5*cm, 6.7*cm])
home_t.setStyle(TableStyle([
("BACKGROUND", (0,0),(-1,0), NAVY),
("BACKGROUND", (0,2),(-1,2), LIGHT_TEAL),
("BACKGROUND", (0,4),(-1,4), LIGHT_TEAL),
("BACKGROUND", (0,6),(-1,6), LIGHT_TEAL),
("BACKGROUND", (0,8),(-1,8), LIGHT_TEAL),
("GRID", (0,0),(-1,-1), 0.4, colors.HexColor("#BDC3C7")),
("TOPPADDING", (0,0),(-1,-1), 4),
("BOTTOMPADDING", (0,0),(-1,-1), 4),
("LEFTPADDING", (0,0),(-1,-1), 5),
("RIGHTPADDING", (0,0),(-1,-1), 5),
("VALIGN", (0,0),(-1,-1), "MIDDLE"),
]))
story += home_items + [home_t, SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 11 — QUICK REFERENCE CARD
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("11. QUICK REFERENCE CARD", color=RED), SP(6)]
qr_content = """
<b>INDICATION:</b> pH 7.25–7.35 | PaCO₂ > 45 mmHg | RR > 25 | ↑WOB | Cooperative patient
<b>INITIATE:</b> IPAP 10–12 / EPAP 4–5 cmH₂O | Mode: S/T | Backup RR 12–16 | FiO₂ 1.0
Full-face mask | 30–45° head-up position
<b>TITRATE:</b> ↑ IPAP by 2 cmH₂O q5–10 min if: WOB high / RR > 25 / Vt low / PaCO₂ not falling
↑ EPAP by 1–2 cmH₂O if: SpO₂ < 88% (then also ↑ IPAP to maintain PS)
Target PS (IPAP−EPAP) > 10–15 cmH₂O | Max IPAP 20–25 cmH₂O
<b>TARGETS:</b> pH > 7.35 | PaCO₂ ↓ gradually (< 10 mmHg/hr)
SpO₂ 88–92% (COPD) / 92–96% (other) | RR < 25 | Vt 6–8 mL/kg IBW
<b>ABG CHECK:</b> Baseline → 60 min → 4 hrs → q4–6 hrs | After every change: ABG at 1 hr
<b>FAILURE →</b> pH < 7.25 at 60 min | GCS ↓ | RR > 35 | No CO₂ improvement
<b>INTUBATE:</b> Call senior + anaesthesia STAT | Prepare RSI kit
"""
qr_para = Paragraph(qr_content, ParagraphStyle(
"qr", fontSize=8.5, fontName="Courier",
textColor=BLACK, leading=14, leftIndent=6
))
story += [coloured_box([qr_para], bg=LIGHT_AMB, border=ORANGE), SP(8)]
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 12 — DOCUMENTATION & GOVERNANCE
# ══════════════════════════════════════════════════════════════════════════════
story += [section_header("12. DOCUMENTATION & GOVERNANCE"), SP(6)]
doc_items = [
Paragraph("12.1 Mandatory Documentation", STYLE["h2"]),
B("Time and indication for BiPAP initiation", "bullet"),
B("Interface type and initial settings (IPAP/EPAP/RR/FiO₂/mode)", "bullet"),
B("All ABG results with time and settings at time of ABG", "bullet"),
B("All setting changes with reason and clinician name", "bullet"),
B("Response assessment at 60 min", "bullet"),
B("NIV care bundle completion checklist", "bullet"),
B("Weaning plan and discharge NIV prescription if applicable", "bullet"),
SP(4),
Paragraph("12.2 References & Evidence Base", STYLE["h2"]),
Paragraph(
"1. Murray & Nadel's Textbook of Respiratory Medicine, 7th ed. — CPAP and BiPAP section\n"
"2. Fishman's Pulmonary Diseases and Disorders — NIV in COPD (p.2533)\n"
"3. Brenner & Rector's The Kidney — Respiratory Acid-Base Disorders (p.707)\n"
"4. Rosen's Emergency Medicine — Noninvasive Ventilation (p.48–49)\n"
"5. BTS/ICS Guidelines for NIV in Acute Hypercapnic Respiratory Failure\n"
"6. Özsancak Uğurlu A, Ergan B. Pulmonology 2023 Mar–Apr. [PMID: 36137890]\n"
"7. Farmer MJS et al. Chest 2024 Jun. [PMID: 38417700]\n"
"8. AASM Best Practice Guide — NPPV Titration, J Clin Sleep Med 2010;6(5)",
STYLE["ref"]),
]
story += doc_items + [SP(8)]
# ── Final note ───────────────────────────────────────────────────────────────
final_note = [
Paragraph(
"This protocol is intended as a clinical decision-support tool. It does not replace "
"clinical judgment. Individual patient circumstances may require deviation from these "
"guidelines. All deviations should be documented with rationale. Senior review is "
"mandatory when pH < 7.25 or clinical deterioration occurs despite NIV.",
STYLE["caption"]),
]
story += [HR(color=NAVY), SP(3)] + final_note
# ─── Build ────────────────────────────────────────────────────────────────────
doc.build(story,
onFirstPage=on_first_page,
onLaterPages=on_page)
print(f"PDF generated: {OUT}")
print(f"File size: {os.path.getsize(OUT):,} bytes")
cd /home/daytona/workspace/resp-acidosis-protocol && python3 build_protocol.py 2>&1
~/resp-acidosis-protocol/build_protocol.py
Error: String not found in file: 'from reportlab.graphics.shapes import Drawing, Rect, String, Line, Arrow from reportlab.graphics import renderPDF from reportlab.graphics.shapes import Drawing, Rect, String, Line, Arrow'
head -25 /home/daytona/workspace/resp-acidosis-protocol/build_protocol.py | cat -n
sed -i '16s/.*/from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon/' /home/daytona/workspace/resp-acidosis-protocol/build_protocol.py && sed -i '17s/.*/from reportlab.graphics import renderPDF/' /home/daytona/workspace/resp-acidosis-protocol/build_protocol.py && sed -i '18d' /home/daytona/workspace/resp-acidosis-protocol/build_protocol.py && head -22 /home/daytona/workspace/resp-acidosis-protocol/build_protocol.py
python3 -c " content = open('/home/daytona/workspace/resp-acidosis-protocol/build_protocol.py').read() content = content.replace( 'from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon\nfrom reportlab.graphics import renderPDF\nfrom reportlab.graphics.shapes import Drawing, Rect, String, Line, Arrow', 'from reportlab.graphics.shapes import Drawing, Rect, String, Line, Polygon\nfrom reportlab.graphics import renderPDF' ) open('/home/daytona/workspace/resp-acidosis-protocol/build_protocol.py','w').write(content) print('done') "
cd /home/daytona/workspace/resp-acidosis-protocol && python3 build_protocol.py 2>&1
Acute Respiratory Acidosis Protocol
PDF Document · PDF
| Section | Content |
|---|---|
| Cover | Document number, version, effective/review dates, department |
| 1. Diagnostic Criteria | ABG table with normal vs. acidosis values, compensation formulae (acute vs. chronic), common causes in two columns |
| 2. Initial Assessment | 5-point rapid assessment table (airway → haemodynamics → ABG) |
| 3. Decision Pathway | Green box (BiPAP indications) vs. Red box (contraindications/intubate), NIV failure predictors |
| 4. BiPAP Setup | Interface selection table, mode selection guidance, phenotype-specific settings (COPD / Cardiogenic / OHS-NMD) |
| 5. Titration Protocol | Step-by-step 4-phase flow (Initiate → Observe → Titrate → Recheck ABG), ABG targets table |
| 6. Monitoring | Schedule from baseline to 12 hrs; asynchrony management |
| 7. Response & Failure | Success criteria (green) vs. failure criteria (red), NIV failure action protocol |
| 8. Weaning | Wean criteria checklist, 6-step weaning table with intervals |
| 9. Maintenance | Concurrent treatments (COPD / APO), supportive care checklist |
| 10. Home NIV | Post-discharge persistent hypercapnia guidance, AVAPS, LTOT, home settings targets |
| 11. Quick Reference Card | Monospace card with all key numbers at a glance |
| 12. Documentation & References | Mandatory charting points, 8 evidence-based references (textbooks + PubMed PMIDs) |