ABG analysis

Reading File
Finding Sources
Finding Sources
Reading File
Reading File
Reading File
Reading File
Finding Sources
Reading File
Loading Image
Here is a thorough guide to Arterial Blood Gas (ABG) Analysis, synthesized from Roberts and Hedges' Clinical Procedures in Emergency Medicine, Murray & Nadel's Textbook of Respiratory Medicine, and Tintinalli's Emergency Medicine.

Arterial Blood Gas (ABG) Analysis

What an ABG Measures

An ABG directly measures:
  • pH - hydrogen ion concentration
  • PaCO2 - partial pressure of CO2 (reflects ventilation)
  • PaO2 - partial pressure of oxygen (reflects oxygenation)
  • HCO3- - calculated via the Henderson-Hasselbalch equation
  • Modern analyzers also report lactate, hemoglobin, and electrolytes

Normal Values

ParameterNormal Range
pH7.35 - 7.45
PaCO235 - 45 mmHg
PaO280 - 100 mmHg
HCO3-21 - 27 mEq/L
SaO295 - 100%

Step-by-Step ABG Interpretation

Step 1 - Determine the pH

  • pH < 7.35 = Acidemia
  • pH > 7.45 = Alkalemia
  • pH 7.35-7.45 = Normal (may still have a compensated or mixed disorder)

Step 2 - Identify the Primary Disorder

ConditionpHPaCO2HCO3-
Respiratory acidosisLowHigh (>45)Normal/High
Respiratory alkalosisHighLow (<35)Normal/Low
Metabolic acidosisLowNormal/LowLow (<22)
Metabolic alkalosisHighNormal/HighHigh (>26)
Key rule: If the pH and PaCO2 move in opposite directions, the primary disorder is respiratory. If the pH and HCO3- move in the same direction, the primary disorder is metabolic.

Step 3 - Assess Compensation

Compensation is never complete. It brings the pH toward normal but never normalizes it (unless a mixed disorder is present).
Primary DisorderExpected Compensation
Metabolic acidosisPCO2 decreases: APCO2 = 1.3 x AHCO3- (Winter's formula)
Metabolic alkalosisPCO2 increases: APCO2 = 0.6 x AHCO3-
Acute respiratory acidosisHCO3- rises 1 mEq/L per 10 mmHg rise in PCO2
Chronic respiratory acidosisHCO3- rises 3.5-5 mEq/L per 10 mmHg rise in PCO2
Acute respiratory alkalosisHCO3- falls 2 mEq/L per 10 mmHg fall in PCO2
Chronic respiratory alkalosisHCO3- falls 5 mEq/L per 10 mmHg fall in PCO2
If the actual compensation does not match the predicted value, a mixed disorder is present.

Step 4 - Calculate the Anion Gap (for metabolic acidosis)

AG = Na+ - (Cl- + HCO3-)
  • Normal AG = 8-12 mEq/L (some labs use 12 as upper limit)
High AG Metabolic Acidosis (MUDPILES)Normal AG Metabolic Acidosis (HARDASS/USED CARP)
MethanolHyperalimentation
UremiaAddison's disease
DKARenal tubular acidosis (RTA)
Propylene glycol / ParacetamolDiarrhea
Isoniazid / IronAcetazolamide
Lactic acidosisSpironolactone / Saline excess
Ethylene glycol
Salicylates

Step 5 - Delta-Delta Ratio (for high AG metabolic acidosis)

The Delta-Delta (AAG/AHCO3-) ratio detects mixed metabolic disorders:
AAG = Calculated AG - 12
  • Ratio ~1:1 = Pure AG metabolic acidosis (e.g., DKA, early lactic acidosis)
  • Ratio > 2:1 = Concurrent metabolic alkalosis (HCO3- higher than expected)
  • Ratio < 1:1 = Concurrent non-AG metabolic acidosis (HCO3- lower than expected)

Step 6 - Assess Oxygenation

A-a gradient = PAO2 - PaO2
Where: PAO2 = FiO2 x (Patm - PH2O) - PaCO2/0.8
  • On room air at sea level: PAO2 = 0.21 x (760 - 47) - PaCO2/0.8 = ~150 - PaCO2/0.8
  • Normal A-a gradient ~ 10 mmHg (increases with age; rough formula: age/4 + 4)
A-a GradientInterpretation
NormalHypoventilation (CNS depression, NMD, chest wall)
ElevatedV/Q mismatch, shunt, diffusion impairment

Acid-Base Map

This classic map plots pH vs. PaCO2 with HCO3- isopleths. Points falling within the labeled bands suggest a simple disorder; points falling outside (colored zones 1-4) indicate a mixed disorder.
Acid-base map showing zones for simple and mixed acid-base disorders
  • Zone 1 (red): Mixed respiratory + metabolic acidosis
  • Zone 2 (pink): Mixed respiratory + metabolic alkalosis
  • Zone 3 (yellow): Metabolic alkalosis + respiratory acidosis
  • Zone 4 (orange): Metabolic acidosis + respiratory alkalosis

Worked Examples

Example 1 - Metabolic Acidosis with Diarrhea

Patient: Na+ 133, K+ 2.8, Cl- 118, pH 7.26, PCO2 13, HCO3- 5
  1. Acidemia (pH < 7.35)
  2. Metabolic acidosis (low HCO3-, PCO2 not elevated)
  3. Expected PCO2 = 40 - (1.3 x 20) = 40 - 26 = 14 mmHg - actual is 13 mmHg - appropriate compensation
  4. AG = 133 - (118 + 5) = 10 - normal AG acidosis (diarrhea - HCO3- loss)

Example 2 - Respiratory Alkalosis (Sepsis)

Patient: Na+ 138, K+ 3.2, Cl- 105, pH 7.49, PCO2 25, HCO3- 22
  1. Alkalemia (pH > 7.45)
  2. Respiratory alkalosis (low PCO2, HCO3- not elevated)
  3. PCO2 drop = 15 mmHg; expected HCO3- drop = 3 mEq/L - actual drop is 3 mEq/L - appropriate compensation
  4. No mixed disorder - consistent with early sepsis-driven hyperventilation

Arterial vs. Venous Blood Gas

ParameterABGVBG
pHGold standardCorrelates closely (±0.05 units)
PCO2ReferenceTrends with arterial (±20 mmHg)
PO2ReferenceCannot substitute - do not use
LactateReferenceUseful for screening; confirm if mildly elevated
A normal venous PCO2 is predictive of normal PaCO2, making VBG useful as a screening tool when arterial sampling is difficult.

Key Clinical Pearls

  • Acute vs. chronic respiratory acidosis: In acute, pH falls 0.08 per 10 mmHg rise in PCO2 with only a 1 mEq/L rise in HCO3-. In chronic (3-5 days of renal compensation), HCO3- rises 3.5-5 mEq/L per 10 mmHg rise in PCO2 and pH is near-normal.
  • Over-compensation does not occur physiologically - if pH is overcorrected, suspect a mixed disorder.
  • Metabolic compensation for respiratory disorders takes 3-5 days (renal); respiratory compensation for metabolic disorders is rapid (minutes to hours).
  • Always check the anion gap even when the pH and HCO3- appear normal - a masked AG acidosis can co-exist with metabolic alkalosis.

Sources: Roberts and Hedges' Clinical Procedures in Emergency Medicine; Murray & Nadel's Textbook of Respiratory Medicine; Tintinalli's Emergency Medicine: A Comprehensive Study.

Create a quick reference ABG interpretation card (PDF)

Reading File
Running Command

ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace empty or not found"

Running Command

mkdir -p /home/daytona/workspace/abg-card && python3 -c "from reportlab.lib import colors; print('reportlab ok')"

Writing File

~/abg-card/generate_abg_card.py

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

# ── Colour palette ──────────────────────────────────────────────────────────
NAVY      = colors.HexColor('#1B3A6B')
TEAL      = colors.HexColor('#1A7F7A')
RED       = colors.HexColor('#C0392B')
ORANGE    = colors.HexColor('#D35400')
GOLD      = colors.HexColor('#F0A500')
PURPLE    = colors.HexColor('#6C3483')
LIGHT_BG  = colors.HexColor('#F4F8FC')
ACID_BG   = colors.HexColor('#FFF3CD')
ALK_BG    = colors.HexColor('#D4EDDA')
RESP_BG   = colors.HexColor('#D1ECF1')
MET_BG    = colors.HexColor('#F8D7DA')
WHITE     = colors.white
DARK_GREY = colors.HexColor('#333333')
MID_GREY  = colors.HexColor('#666666')
LIGHT_LINE= colors.HexColor('#CCDDEE')

OUT = '/home/daytona/workspace/abg-card/ABG_Quick_Reference.pdf'

doc = SimpleDocTemplate(
    OUT,
    pagesize=A4,
    leftMargin=12*mm, rightMargin=12*mm,
    topMargin=10*mm,  bottomMargin=10*mm
)

W = A4[0] - 24*mm   # usable width

styles = getSampleStyleSheet()

def sty(name, **kw):
    s = ParagraphStyle(name, **kw)
    return s

H1 = sty('H1', fontSize=18, fontName='Helvetica-Bold',
          textColor=WHITE, alignment=TA_CENTER, spaceAfter=0, leading=22)
H2 = sty('H2', fontSize=10, fontName='Helvetica-Bold',
          textColor=WHITE, alignment=TA_CENTER, spaceAfter=0, leading=13)
SEC = sty('SEC', fontSize=8.5, fontName='Helvetica-Bold',
          textColor=NAVY, alignment=TA_LEFT, spaceAfter=1, leading=11)
BODY = sty('BODY', fontSize=7.5, fontName='Helvetica',
           textColor=DARK_GREY, leading=10)
SMALL = sty('SMALL', fontSize=6.8, fontName='Helvetica',
            textColor=MID_GREY, leading=9)
BOLD_SM = sty('BOLD_SM', fontSize=7.5, fontName='Helvetica-Bold',
              textColor=DARK_GREY, leading=10)
WHITE_SM = sty('WHITE_SM', fontSize=7.5, fontName='Helvetica-Bold',
               textColor=WHITE, alignment=TA_CENTER, leading=10)
TINY = sty('TINY', fontSize=6.5, fontName='Helvetica',
           textColor=MID_GREY, alignment=TA_CENTER, leading=8)

# ── helpers ──────────────────────────────────────────────────────────────────
def section_header(text, bg=NAVY, fg=WHITE):
    data = [[Paragraph(text, sty('sh', fontSize=8.5, fontName='Helvetica-Bold',
                                 textColor=fg, alignment=TA_LEFT, leading=11))]]
    t = Table(data, colWidths=[W])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), bg),
        ('TOPPADDING',    (0,0), (-1,-1), 3),
        ('BOTTOMPADDING', (0,0), (-1,-1), 3),
        ('LEFTPADDING',   (0,0), (-1,-1), 6),
        ('RIGHTPADDING',  (0,0), (-1,-1), 6),
    ]))
    return t

def mini_spacer(h=2):
    return Spacer(1, h*mm)

story = []

# ══════════════════════════════════════════════════════════════════════════════
# TITLE BANNER
# ══════════════════════════════════════════════════════════════════════════════
title_data = [[
    Paragraph('ABG QUICK REFERENCE CARD', H1),
    Paragraph('Arterial Blood Gas Interpretation', H2),
]]
title_tbl = Table([[
    Paragraph('ABG QUICK REFERENCE CARD', H1),
]], colWidths=[W])
title_tbl.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,-1), NAVY),
    ('TOPPADDING',    (0,0), (-1,-1), 7),
    ('BOTTOMPADDING', (0,0), (-1,-1), 4),
    ('LEFTPADDING',   (0,0), (-1,-1), 8),
]))

subtitle_tbl = Table([[
    Paragraph('Arterial Blood Gas — Systematic Interpretation Guide', H2),
]], colWidths=[W])
subtitle_tbl.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,-1), TEAL),
    ('TOPPADDING',    (0,0), (-1,-1), 3),
    ('BOTTOMPADDING', (0,0), (-1,-1), 3),
]))
story += [title_tbl, subtitle_tbl, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1 – NORMAL VALUES
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header('  NORMAL VALUES'))
story.append(mini_spacer(1))

nv_data = [
    [Paragraph('<b>Parameter</b>', BOLD_SM),
     Paragraph('<b>Normal Range</b>', BOLD_SM),
     Paragraph('<b>Acidosis</b>', BOLD_SM),
     Paragraph('<b>Alkalosis</b>', BOLD_SM)],
    [Paragraph('pH', BODY), Paragraph('7.35 – 7.45', BODY),
     Paragraph('< 7.35', BODY), Paragraph('> 7.45', BODY)],
    [Paragraph('PaCO₂', BODY), Paragraph('35 – 45 mmHg', BODY),
     Paragraph('> 45 mmHg', BODY), Paragraph('< 35 mmHg', BODY)],
    [Paragraph('HCO₃⁻', BODY), Paragraph('22 – 26 mEq/L', BODY),
     Paragraph('< 22 mEq/L', BODY), Paragraph('> 26 mEq/L', BODY)],
    [Paragraph('PaO₂', BODY), Paragraph('80 – 100 mmHg', BODY),
     Paragraph('< 80 mmHg (hypoxia)', BODY), Paragraph('—', BODY)],
    [Paragraph('SaO₂', BODY), Paragraph('95 – 100%', BODY),
     Paragraph('< 95%', BODY), Paragraph('—', BODY)],
    [Paragraph('Base Excess', BODY), Paragraph('−2 to +2 mEq/L', BODY),
     Paragraph('< −2', BODY), Paragraph('> +2', BODY)],
]
cw = [W*0.22, W*0.25, W*0.27, W*0.26]
nv_t = Table(nv_data, colWidths=cw)
nv_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), NAVY),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
]))
story += [nv_t, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2 – 6-STEP METHOD  (left) + PRIMARY DISORDERS (right)
# ══════════════════════════════════════════════════════════════════════════════
# Left: steps
steps_rows = [
    [Paragraph('<b>Step</b>', BOLD_SM), Paragraph('<b>Action</b>', BOLD_SM)],
    [Paragraph('1', BOLD_SM), Paragraph('Check <b>pH</b>: acidemia (<7.35) or alkalemia (>7.45)?', BODY)],
    [Paragraph('2', BOLD_SM), Paragraph('Identify <b>primary disorder</b> from PaCO₂ and HCO₃⁻', BODY)],
    [Paragraph('3', BOLD_SM), Paragraph('Assess <b>compensation</b> — is it adequate?', BODY)],
    [Paragraph('4', BOLD_SM), Paragraph('Calculate <b>Anion Gap</b> (if metabolic acidosis)', BODY)],
    [Paragraph('5', BOLD_SM), Paragraph('Delta-Delta ratio (if high AG metabolic acidosis)', BODY)],
    [Paragraph('6', BOLD_SM), Paragraph('Assess <b>oxygenation</b>: PaO₂ and A-a gradient', BODY)],
]
steps_t = Table(steps_rows, colWidths=[W*0.06, W*0.36])
steps_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), TEAL),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
    ('ALIGN', (0,0), (0,-1), 'CENTER'),
    ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))

# Right: primary disorders
pd_rows = [
    [Paragraph('<b>Disorder</b>', BOLD_SM),
     Paragraph('<b>pH</b>', BOLD_SM),
     Paragraph('<b>PaCO₂</b>', BOLD_SM),
     Paragraph('<b>HCO₃⁻</b>', BOLD_SM)],
    [Paragraph('Resp. Acidosis', BODY), Paragraph('↓', BODY), Paragraph('↑ PRIMARY', BODY), Paragraph('↑ comp.', BODY)],
    [Paragraph('Resp. Alkalosis', BODY), Paragraph('↑', BODY), Paragraph('↓ PRIMARY', BODY), Paragraph('↓ comp.', BODY)],
    [Paragraph('Met. Acidosis', BODY), Paragraph('↓', BODY), Paragraph('↓ comp.', BODY), Paragraph('↓ PRIMARY', BODY)],
    [Paragraph('Met. Alkalosis', BODY), Paragraph('↑', BODY), Paragraph('↑ comp.', BODY), Paragraph('↑ PRIMARY', BODY)],
]
pd_t = Table(pd_rows, colWidths=[W*0.27, W*0.08, W*0.16, W*0.14])
pd_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), TEAL),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
    ('ALIGN', (1,0), (-1,-1), 'CENTER'),
    ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))

left_hdr  = section_header('  6-STEP SYSTEMATIC APPROACH', bg=TEAL)
right_hdr = section_header('  PRIMARY DISORDERS AT A GLANCE', bg=TEAL)

lr_table = Table(
    [[left_hdr, right_hdr],
     [steps_t,   pd_t]],
    colWidths=[W*0.45, W*0.55],
    spaceBefore=0
)
lr_table.setStyle(TableStyle([
    ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ('LEFTPADDING',  (0,0), (-1,-1), 0),
    ('RIGHTPADDING', (0,0), (-1,-1), 0),
    ('TOPPADDING',   (0,0), (-1,-1), 0),
    ('BOTTOMPADDING',(0,0), (-1,-1), 0),
    ('LINEAFTER', (0,0), (0,-1), 0.5, LIGHT_LINE),
]))
story += [lr_table, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 3 – COMPENSATION FORMULAS
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header('  COMPENSATION FORMULAS'))
story.append(mini_spacer(1))

comp_data = [
    [Paragraph('<b>Primary Disorder</b>', BOLD_SM),
     Paragraph('<b>Expected Compensatory Response</b>', BOLD_SM),
     Paragraph('<b>If Actual ≠ Expected</b>', BOLD_SM)],
    [Paragraph('Metabolic Acidosis', BODY),
     Paragraph('PaCO₂ = 1.5 × HCO₃⁻ + 8 ± 2  (Winters formula)\nor  ↓ PaCO₂ = 1.3 × ↓ HCO₃⁻', BODY),
     Paragraph('Mixed metabolic + respiratory', BODY)],
    [Paragraph('Metabolic Alkalosis', BODY),
     Paragraph('↑ PaCO₂ = 0.6 × ↑ HCO₃⁻  (max ~55 mmHg)', BODY),
     Paragraph('Mixed metabolic + respiratory', BODY)],
    [Paragraph('Acute Resp. Acidosis', BODY),
     Paragraph('↑ HCO₃⁻ = 1 mEq/L per 10 mmHg ↑ PaCO₂', BODY),
     Paragraph('Superimposed metabolic disorder', BODY)],
    [Paragraph('Chronic Resp. Acidosis', BODY),
     Paragraph('↑ HCO₃⁻ = 3.5 mEq/L per 10 mmHg ↑ PaCO₂\n(onset 3–5 days)', BODY),
     Paragraph('Superimposed metabolic disorder', BODY)],
    [Paragraph('Acute Resp. Alkalosis', BODY),
     Paragraph('↓ HCO₃⁻ = 2 mEq/L per 10 mmHg ↓ PaCO₂', BODY),
     Paragraph('Superimposed metabolic disorder', BODY)],
    [Paragraph('Chronic Resp. Alkalosis', BODY),
     Paragraph('↓ HCO₃⁻ = 5 mEq/L per 10 mmHg ↓ PaCO₂', BODY),
     Paragraph('Superimposed metabolic disorder', BODY)],
]
comp_t = Table(comp_data, colWidths=[W*0.22, W*0.45, W*0.33])
comp_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), NAVY),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
    ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))
story += [comp_t, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 4 – ANION GAP  +  DELTA-DELTA
# ══════════════════════════════════════════════════════════════════════════════
ag_left_hdr  = section_header('  ANION GAP', bg=PURPLE)
ag_right_hdr = section_header('  DELTA-DELTA RATIO (ΔAG / ΔHCO₃⁻)', bg=PURPLE)
story.append(mini_spacer(0))

ag_formula = Table([[
    Paragraph('AG = Na⁺ − (Cl⁻ + HCO₃⁻)', sty('af', fontSize=9,
              fontName='Helvetica-Bold', textColor=PURPLE, alignment=TA_CENTER, leading=12)),
]], colWidths=[W*0.42])
ag_formula.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#F3E9FF')),
    ('TOPPADDING',    (0,0), (-1,-1), 4),
    ('BOTTOMPADDING', (0,0), (-1,-1), 4),
    ('BOX', (0,0), (-1,-1), 1, PURPLE),
    ('ALIGN', (0,0), (-1,-1), 'CENTER'),
]))

ag_rows = [
    [Paragraph('<b>AG</b>', BOLD_SM), Paragraph('<b>Interpretation</b>', BOLD_SM)],
    [Paragraph('< 8', BODY),  Paragraph('Normal / low (hypoalbuminaemia, bromism)', BODY)],
    [Paragraph('8–12', BODY), Paragraph('Normal', BODY)],
    [Paragraph('> 12', BODY), Paragraph('High AG — unmeasured anions present', BODY)],
]
ag_t = Table(ag_rows, colWidths=[W*0.10, W*0.32])
ag_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#6C3483')),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
    ('ALIGN', (0,0), (0,-1), 'CENTER'),
    ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))

note_alb = Paragraph(
    '* Correct AG for albumin: AG + 2.5 × (4 − albumin g/dL)', SMALL)

# Delta-delta
dd_formula = Table([[
    Paragraph('ΔAG = Measured AG − 12\n\nRatio = ΔAG / (24 − measured HCO₃⁻)',
              sty('df', fontSize=8.5, fontName='Helvetica-Bold',
                  textColor=PURPLE, alignment=TA_CENTER, leading=12)),
]], colWidths=[W*0.55])
dd_formula.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,-1), colors.HexColor('#F3E9FF')),
    ('TOPPADDING',    (0,0), (-1,-1), 4),
    ('BOTTOMPADDING', (0,0), (-1,-1), 4),
    ('BOX', (0,0), (-1,-1), 1, PURPLE),
    ('ALIGN', (0,0), (-1,-1), 'CENTER'),
]))

dd_rows = [
    [Paragraph('<b>Ratio</b>', BOLD_SM), Paragraph('<b>Interpretation</b>', BOLD_SM)],
    [Paragraph('< 1', BODY),    Paragraph('Mixed high AG + non-AG metabolic acidosis', BODY)],
    [Paragraph('1 – 2', BODY),  Paragraph('Pure high AG metabolic acidosis', BODY)],
    [Paragraph('> 2', BODY),    Paragraph('High AG acidosis + concurrent metabolic alkalosis', BODY)],
]
dd_t = Table(dd_rows, colWidths=[W*0.11, W*0.44])
dd_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#6C3483')),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
    ('ALIGN', (0,0), (0,-1), 'CENTER'),
    ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))

ag_side = Table(
    [[ag_left_hdr, ag_right_hdr],
     [ag_formula,   dd_formula],
     [ag_t,         dd_t],
     [note_alb,     Paragraph('', SMALL)]],
    colWidths=[W*0.44, W*0.56]
)
ag_side.setStyle(TableStyle([
    ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ('LEFTPADDING',  (0,0), (-1,-1), 0),
    ('RIGHTPADDING', (0,0), (-1,-1), 0),
    ('TOPPADDING',   (0,0), (-1,-1), 2),
    ('BOTTOMPADDING',(0,0), (-1,-1), 2),
    ('LINEAFTER', (0,0), (0,-1), 0.5, LIGHT_LINE),
]))
story += [ag_side, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 5 – CAUSES
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header('  CAUSES'))
story.append(mini_spacer(1))

# Box helper
def cause_box(title, bg, items, width):
    rows = [[Paragraph(f'<b>{title}</b>',
                       sty('cb', fontSize=7.5, fontName='Helvetica-Bold',
                           textColor=WHITE, alignment=TA_CENTER, leading=10))]]
    for item in items:
        rows.append([Paragraph(f'• {item}', BODY)])
    t = Table(rows, colWidths=[width])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (0,0), bg),
        ('BACKGROUND', (0,1), (-1,-1), colors.HexColor('#FAFBFF')),
        ('BOX',    (0,0), (-1,-1), 0.5, bg),
        ('INNERGRID', (0,0), (-1,-1), 0.2, LIGHT_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 2),
        ('BOTTOMPADDING', (0,0), (-1,-1), 2),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('RIGHTPADDING',  (0,0), (-1,-1), 5),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
    ]))
    return t

bw = W / 4 - 2*mm

high_ag = cause_box('High AG Acidosis\n(MUDPILES)', RED, [
    'Methanol', 'Uraemia', 'DKA', 'Propylene glycol',
    'Isoniazid / Iron', 'Lactic acidosis',
    'Ethylene glycol', 'Salicylates'
], bw)

norm_ag = cause_box('Normal AG Acidosis\n(HARDASS)', ORANGE, [
    'Hyperalimentation', 'Addison\'s disease',
    'Renal tubular acidosis', 'Diarrhoea',
    'Acetazolamide',
    'Spironolactone / Saline'
], bw)

resp_ac = cause_box('Respiratory\nAcidosis', colors.HexColor('#1A5276'), [
    'COPD / asthma', 'Pneumonia',
    'Pulmonary oedema', 'Obesity hypoventilation',
    'NMD (GBS, MG)', 'Opioids / sedatives',
    'Chest wall deformity'
], bw)

met_alk = cause_box('Metabolic\nAlkalosis', TEAL, [
    'Vomiting / NG suction', 'Diuretics',
    'Hypokalaemia', 'Hyperaldosteronism',
    'Excess NaHCO₃',
    'Post-hypercapnia'
], bw)

causes_t = Table([[high_ag, norm_ag, resp_ac, met_alk]],
                 colWidths=[bw]*4)
causes_t.setStyle(TableStyle([
    ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ('LEFTPADDING',  (0,0), (-1,-1), 2),
    ('RIGHTPADDING', (0,0), (-1,-1), 2),
]))

story += [causes_t, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 6 – OXYGENATION  +  RESP ALKALOSIS CAUSES
# ══════════════════════════════════════════════════════════════════════════════
oxy_hdr  = section_header('  OXYGENATION & A-a GRADIENT', bg=colors.HexColor('#1A5276'))
ra_hdr   = section_header('  RESPIRATORY ALKALOSIS CAUSES', bg=colors.HexColor('#1A5276'))

oxy_formula = Paragraph(
    '<b>PAO₂ = FiO₂ × (713) − PaCO₂ / 0.8</b>  (room air: FiO₂ = 0.21, Patm = 760 mmHg)',
    sty('of', fontSize=8, fontName='Helvetica-Bold', textColor=colors.HexColor('#1A5276'),
        leading=11))
aa_normal = Paragraph(
    'Normal A-a gradient ≈ Age/4 + 4  (typically < 15 mmHg on room air)',
    BODY)

oxy_rows = [
    [Paragraph('<b>PaO₂ (room air)</b>', BOLD_SM), Paragraph('<b>Classification</b>', BOLD_SM)],
    [Paragraph('80–100 mmHg', BODY), Paragraph('Normal', BODY)],
    [Paragraph('60–79 mmHg', BODY), Paragraph('Mild hypoxaemia', BODY)],
    [Paragraph('40–59 mmHg', BODY), Paragraph('Moderate hypoxaemia', BODY)],
    [Paragraph('< 40 mmHg', BODY),  Paragraph('Severe hypoxaemia', BODY)],
]
oxy_t = Table(oxy_rows, colWidths=[W*0.22, W*0.22])
oxy_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1A5276')),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
]))

aa_rows = [
    [Paragraph('<b>A-a gradient</b>', BOLD_SM), Paragraph('<b>Cause of hypoxaemia</b>', BOLD_SM)],
    [Paragraph('Normal', BODY),   Paragraph('Hypoventilation (CNS depression, NMD)', BODY)],
    [Paragraph('Elevated', BODY), Paragraph('V/Q mismatch, shunt, diffusion defect', BODY)],
]
aa_t = Table(aa_rows, colWidths=[W*0.14, W*0.28])
aa_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), colors.HexColor('#1A5276')),
    ('TEXTCOLOR',  (0,0), (-1,0), WHITE),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [LIGHT_BG, WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
]))

ra_causes = [
    '• Anxiety / hyperventilation',
    '• Pain', '• Fever', '• Sepsis (early)',
    '• Altitude / hypoxia', '• Salicylates',
    '• PE', '• Liver failure', '• Pregnancy',
    '• Mechanical overventilation',
]
ra_items = Table([[Paragraph(c, BODY)] for c in ra_causes],
                 colWidths=[W*0.42 - 4*mm])
ra_items.setStyle(TableStyle([
    ('ROWBACKGROUNDS', (0,0), (-1,-1), [LIGHT_BG, WHITE]),
    ('TOPPADDING',    (0,0), (-1,-1), 2),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
]))

left_oxy = Table(
    [[oxy_hdr],
     [oxy_formula],
     [mini_spacer(1)],
     [aa_normal],
     [mini_spacer(1)],
     [oxy_t],
     [mini_spacer(1)],
     [aa_t]],
    colWidths=[W*0.58]
)
left_oxy.setStyle(TableStyle([
    ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ('LEFTPADDING',  (0,0), (-1,-1), 0),
    ('RIGHTPADDING', (0,0), (-1,-1), 3),
    ('TOPPADDING',   (0,0), (-1,-1), 2),
    ('BOTTOMPADDING',(0,0), (-1,-1), 2),
]))

right_ra = Table(
    [[ra_hdr],
     [ra_items]],
    colWidths=[W*0.42]
)
right_ra.setStyle(TableStyle([
    ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ('LEFTPADDING',  (0,0), (-1,-1), 0),
    ('RIGHTPADDING', (0,0), (-1,-1), 0),
    ('TOPPADDING',   (0,0), (-1,-1), 2),
    ('BOTTOMPADDING',(0,0), (-1,-1), 2),
]))

oxy_ra_t = Table([[left_oxy, right_ra]], colWidths=[W*0.58, W*0.42])
oxy_ra_t.setStyle(TableStyle([
    ('VALIGN', (0,0), (-1,-1), 'TOP'),
    ('LEFTPADDING',  (0,0), (-1,-1), 0),
    ('RIGHTPADDING', (0,0), (-1,-1), 0),
    ('TOPPADDING',   (0,0), (-1,-1), 0),
    ('BOTTOMPADDING',(0,0), (-1,-1), 0),
    ('LINEAFTER', (0,0), (0,-1), 0.5, LIGHT_LINE),
]))
story += [oxy_ra_t, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# SECTION 7 – CLINICAL PEARLS
# ══════════════════════════════════════════════════════════════════════════════
story.append(section_header('  CLINICAL PEARLS', bg=GOLD, fg=DARK_GREY))
story.append(mini_spacer(1))

pearls = [
    ('pH movement direction', 'Never crosses 7.40 with simple compensation alone — if pH overcorrects, suspect a mixed disorder.'),
    ('Acute vs chronic resp. acidosis', 'Acute: pH drops 0.08 per 10 mmHg PCO₂ rise. Chronic (>3–5 d): renal compensation normalises pH with ↑ HCO₃⁻.'),
    ('Hidden AG metabolic acidosis', 'Always calculate the AG even with a normal pH — a masked AG acidosis can coexist with metabolic alkalosis.'),
    ('Albumin correction', 'For every 1 g/dL fall in albumin below 4, add 2.5 to the AG. Hypoalbuminaemia lowers the "normal" AG.'),
    ('VBG vs ABG', 'VBG pH ≈ ABG pH (±0.05). Venous PCO₂ trends with arterial. Venous PO₂ cannot assess oxygenation.'),
    ('Metabolic compensation speed', 'Respiratory compensation for metabolic disorders = minutes to hours. Renal compensation for respiratory disorders = 3–5 days.'),
]
pearl_rows = [[Paragraph('<b>Pearl</b>', BOLD_SM), Paragraph('<b>Detail</b>', BOLD_SM)]]
for p, d in pearls:
    pearl_rows.append([Paragraph(f'<b>{p}</b>', BODY), Paragraph(d, BODY)])
pearl_t = Table(pearl_rows, colWidths=[W*0.28, W*0.72])
pearl_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,0), GOLD),
    ('TEXTCOLOR',  (0,0), (-1,0), DARK_GREY),
    ('ROWBACKGROUNDS', (0,1), (-1,-1), [colors.HexColor('#FFFDF0'), WHITE]),
    ('GRID', (0,0), (-1,-1), 0.3, LIGHT_LINE),
    ('TOPPADDING',    (0,0), (-1,-1), 2.5),
    ('BOTTOMPADDING', (0,0), (-1,-1), 2.5),
    ('LEFTPADDING',   (0,0), (-1,-1), 5),
    ('RIGHTPADDING',  (0,0), (-1,-1), 5),
    ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
]))
story += [pearl_t, mini_spacer(3)]

# ══════════════════════════════════════════════════════════════════════════════
# FOOTER
# ══════════════════════════════════════════════════════════════════════════════
footer_t = Table([[
    Paragraph('ABG Quick Reference Card  |  Sources: Roberts & Hedges\' Clinical Procedures; Murray & Nadel\'s Respiratory Medicine; Tintinalli\'s Emergency Medicine',
              TINY),
    Paragraph('For clinical use by trained healthcare professionals only', TINY),
]], colWidths=[W*0.70, W*0.30])
footer_t.setStyle(TableStyle([
    ('BACKGROUND', (0,0), (-1,-1), NAVY),
    ('TEXTCOLOR',  (0,0), (-1,-1), WHITE),
    ('TOPPADDING',    (0,0), (-1,-1), 3),
    ('BOTTOMPADDING', (0,0), (-1,-1), 3),
    ('LEFTPADDING',   (0,0), (-1,-1), 6),
    ('RIGHTPADDING',  (0,0), (-1,-1), 6),
    ('ALIGN', (1,0), (1,0), 'RIGHT'),
]))
story.append(footer_t)

# ── Build ────────────────────────────────────────────────────────────────────
doc.build(story)
print(f'PDF saved to {OUT}')
Running Command

python3 /home/daytona/workspace/abg-card/generate_abg_card.py

Running Command

ls -lh /home/daytona/workspace/abg-card/ABG_Quick_Reference.pdf

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/abg-card/ABG_Quick_Reference.pdf') print(f'Pages: {len(r.pages)}') for i, p in enumerate(r.pages): t = p.extract_text() print(f'Page {i+1} chars: {len(t)}') print(t[:300]) print('---') "

Running Command

python3 -c " from pypdf import PdfReader r = PdfReader('/home/daytona/workspace/abg-card/ABG_Quick_Reference.pdf') print(f'Pages: {len(r.pages)}') for i, p in enumerate(r.pages): print(f'Page {i+1}: {p.mediabox.width:.0f} x {p.mediabox.height:.0f} pts = {p.mediabox.width/72*25.4:.0f} x {p.mediabox.height/72*25.4:.0f} mm') print(f' Text length: {len(p.extract_text())} chars') "

Your ABG Quick Reference Card is ready - a clean 2-page A4 PDF:

ABG Quick Reference

PDF Document · PDF


What's included on the card:
SectionContent
Normal ValuespH, PaCO₂, HCO₃⁻, PaO₂, SaO₂, Base Excess with acidosis/alkalosis thresholds
6-Step ApproachSystematic interpretation method side-by-side with primary disorders table
Compensation FormulasAll 6 formulas including Winter's formula, acute vs chronic
Anion GapFormula, normal ranges, albumin correction note
Delta-Delta RatioFormula + interpretation of <1, 1-2, >2
CausesMUDPILES, HARDASS, respiratory acidosis, metabolic alkalosis - colour-coded boxes
OxygenationA-a gradient formula, PaO₂ classification, hypoxaemia causes
Respiratory AlkalosisFull cause list
Clinical Pearls6 high-yield pearls on mixed disorders, hidden AG, VBG vs ABG, compensation timing
This is a shared conversation. Sign in to Orris to start your own chat.