Make a schematic flowchart for the diagnosis and management of bipolar disorder in accordance with the latest guidelines
bipolar disorder diagnosis management guidelines 2024 2025 APA CANMAT
https://pmc.ncbi.nlm.nih.gov/articles/PMC12900052
https://pmc.ncbi.nlm.nih.gov/articles/PMC12900052
bipolar disorder management guidelines
mkdir -p /home/daytona/workspace/bipolar-flowchart && ls /home/daytona/workspace/
~/bipolar-flowchart/bipolar_flowchart.py
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import numpy as np
fig, ax = plt.subplots(figsize=(26, 44))
ax.set_xlim(0, 26)
ax.set_ylim(0, 44)
ax.axis('off')
fig.patch.set_facecolor('#F7F9FC')
# ── colour palette ──────────────────────────────────────────────────────────
C_TITLE = '#1A237E' # dark navy
C_STEP = '#1565C0' # medium blue (process)
C_DIAG = '#283593' # darker blue (diagnostic section header)
C_MANIA = '#B71C1C' # deep red (mania branch)
C_DEPRES = '#1B5E20' # deep green (depression branch)
C_MIXED = '#E65100' # deep orange (mixed)
C_MAINT = '#4A148C' # deep purple (maintenance)
C_SPECIAL = '#006064' # teal (special situations)
C_DECI = '#F57F17' # amber (decision diamond)
C_TEXT = '#FAFAFA'
C_WARN = '#FF8F00'
C_LIGHT = '#EDE7F6'
# ── helper functions ─────────────────────────────────────────────────────────
def rounded_box(ax, x, y, w, h, color, text, fontsize=8.5, text_color='white',
alpha=1.0, style='round,pad=0.05', bold=False, wrap_width=None):
box = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle=style,
linewidth=1.2, edgecolor='white',
facecolor=color, alpha=alpha, zorder=3)
ax.add_patch(box)
weight = 'bold' if bold else 'normal'
ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
color=text_color, weight=weight,
wrap=True, multialignment='center', zorder=4,
linespacing=1.35)
def diamond(ax, x, y, w, h, color, text, fontsize=7.8):
dx, dy = w/2, h/2
pts = np.array([[x, y+dy], [x+dx, y], [x, y-dy], [x-dx, y]])
poly = plt.Polygon(pts, closed=True, facecolor=color,
edgecolor='white', linewidth=1.2, zorder=3)
ax.add_patch(poly)
ax.text(x, y, text, ha='center', va='center', fontsize=fontsize,
color='white', weight='bold', multialignment='center', zorder=4,
linespacing=1.3)
def arrow(ax, x1, y1, x2, y2, color='#455A64', label='', lw=1.6):
ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle='->', color=color,
lw=lw, connectionstyle='arc3,rad=0.0'),
zorder=2)
if label:
mx, my = (x1+x2)/2, (y1+y2)/2
ax.text(mx+0.15, my, label, fontsize=7, color=color, weight='bold', zorder=5)
def section_label(ax, x, y, text, color):
ax.text(x, y, text, ha='center', va='center', fontsize=9,
color=color, weight='bold', style='italic', zorder=5)
# ════════════════════════════════════════════════════════════════════════════
# TITLE
# ════════════════════════════════════════════════════════════════════════════
title_box = FancyBboxPatch((0.3, 42.3), 25.4, 1.4, boxstyle='round,pad=0.1',
facecolor=C_TITLE, edgecolor='none', zorder=3)
ax.add_patch(title_box)
ax.text(13, 43.15, 'DIAGNOSIS & MANAGEMENT OF BIPOLAR DISORDER',
ha='center', va='center', fontsize=15, color='white', weight='bold', zorder=4)
ax.text(13, 42.62, 'Based on DSM-5-TR | CANMAT/ISBD 2023 | NICE 2025 | VA/DoD 2023 Guidelines',
ha='center', va='center', fontsize=8, color='#B3C8E8', zorder=4)
# ════════════════════════════════════════════════════════════════════════════
# STEP 1 — INITIAL PRESENTATION & SCREENING
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 41.5, 16, 0.75, C_STEP,
'STEP 1 — INITIAL PRESENTATION', fontsize=10, bold=True)
rounded_box(ax, 13, 40.6, 20, 0.95, '#37474F',
'Patient presents with: mood episodes (elevated/irritable/depressed) • sleep disturbance • energy changes\n'
'impulsive/risky behaviour • psychotic features • functional impairment',
fontsize=7.8, alpha=0.95)
arrow(ax, 13, 40.12, 13, 39.55)
# ════════════════════════════════════════════════════════════════════════════
# STEP 2 — SCREENING TOOLS
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 39.2, 16, 0.65, C_STEP,
'STEP 2 — SCREENING', fontsize=10, bold=True)
rounded_box(ax, 13, 38.45, 20, 0.9, '#37474F',
'Validated tools: Mood Disorder Questionnaire (MDQ) | Bipolar Spectrum Diagnostic Scale (BSDS)\n'
'Especially in patients with recurrent depression or poor antidepressant response',
fontsize=7.8)
arrow(ax, 13, 37.99, 13, 37.42)
# ════════════════════════════════════════════════════════════════════════════
# STEP 3 — FULL PSYCHIATRIC ASSESSMENT
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 37.1, 16, 0.65, C_STEP,
'STEP 3 — COMPREHENSIVE PSYCHIATRIC ASSESSMENT', fontsize=9.5, bold=True)
rounded_box(ax, 5.5, 35.95, 9.5, 1.35, '#37474F',
'History: current & past mood episodes (duration, severity)\n'
'Psychiatric & family history (bipolarity in 1st-degree relatives)\n'
'Collateral from family/caregivers | Mood charting\n'
'Substance/medication use history', fontsize=7.5)
rounded_box(ax, 13, 35.95, 5.5, 1.35, '#37474F',
'Exclude organic causes:\nHead trauma, stroke, epilepsy\nThyroid/electrolyte disease\n'
'Drug-induced mania\n(corticosteroids, stimulants)', fontsize=7.5)
rounded_box(ax, 20.5, 35.95, 9.5, 1.35, '#37474F',
'Investigations:\nTFTs, LFTs, renal function, FBC\nUrine drug screen\n'
'ECG (if antipsychotic planned)\nNeuroimaging if atypical features', fontsize=7.5)
for xb in [5.5, 13, 20.5]:
arrow(ax, xb, 37.27+0.3*0, xb, 36.28+0.35, color='#78909C')
arrow(ax, 5.5, 35.27, 9.5, 35.27, color='#78909C')
arrow(ax, 20.5, 35.27, 16.5, 35.27, color='#78909C')
arrow(ax, 13, 35.27, 13, 34.77)
# ════════════════════════════════════════════════════════════════════════════
# STEP 4 — APPLY DSM-5-TR DIAGNOSTIC CRITERIA
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 34.45, 16, 0.65, C_DIAG,
'STEP 4 — APPLY DSM-5-TR DIAGNOSTIC CRITERIA', fontsize=9.5, bold=True)
# Manic criteria box
rounded_box(ax, 5.2, 32.95, 9.8, 2.35, '#C62828',
'MANIC EPISODE (BD-I)\n'
'≥7 days (or any duration if hospitalised)\n'
'Abnormally elevated/expansive/irritable mood\n'
'+ increased energy/activity PLUS ≥3 of:\n'
'• Grandiosity • Decreased sleep need\n'
'• Pressured speech • Racing thoughts\n'
'• Distractibility • Psychomotor agitation\n'
'• Risky/pleasurable activities with poor judgement\n'
'Causes marked impairment / needs hospitalisation',
fontsize=7.4, bold=False)
# Hypomanic criteria box
rounded_box(ax, 13, 32.95, 5.5, 2.35, '#558B2F',
'HYPOMANIC EPISODE\n(BD-II)\n'
'≥4 days, same criteria\nas mania BUT:\n'
'• No marked impairment\n• No hospitalisation\n'
'• No psychosis\n'
'+ ≥1 MDE required\nfor BD-II diagnosis',
fontsize=7.4)
# MDE criteria box
rounded_box(ax, 20.5, 32.95, 9.8, 2.35, '#1B5E20',
'MAJOR DEPRESSIVE EPISODE\n'
'≥2 weeks: ≥5 of:\n'
'• Depressed mood (most of day)\n'
'• Anhedonia • Weight/appetite change\n'
'• Insomnia or hypersomnia\n'
'• Psychomotor change • Fatigue\n'
'• Worthlessness/guilt • Poor concentration\n'
'• Suicidal ideation / attempts\n'
'At least 1 symptom = depressed mood or anhedonia',
fontsize=7.4)
for xb in [5.2, 13, 20.5]:
arrow(ax, xb, 34.12, xb, 34.12, color='#78909C')
arrow(ax, 13, 34.12, xb, 34.12, color='#78909C', lw=1.2)
ax.annotate('', xy=(5.2, 34.12), xytext=(13, 34.12),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
ax.annotate('', xy=(20.5, 34.12), xytext=(13, 34.12),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
arrow(ax, 13, 34.12, 13, 34.12)
# draw three arrows down from step 4 to the boxes
arrow(ax, 5.2, 34.12, 5.2, 34.12)
for xb, ystart in [(5.2, 34.12), (13, 34.12), (20.5, 34.12)]:
ax.annotate('', xy=(xb, 34.12), xytext=(13, 34.12),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
# straight down from step 4 header to the three boxes
ax.annotate('', xy=(5.2, 34.12), xytext=(5.2, 34.12), zorder=2)
# Re-draw arrows cleanly: from step4 bottom to each criteria box
step4_y = 34.12
for xb, ytop in [(5.2, 34.12), (13, 34.12), (20.5, 34.12)]:
pass # handled below with a horizontal line
# Horizontal connector at y=34.12
ax.plot([5.2, 20.5], [34.12, 34.12], color='#78909C', lw=1.5, zorder=2)
for xb in [5.2, 13, 20.5]:
arrow(ax, xb, 34.12, xb, 34.12)
# Proper arrows from horizontal line down to each box
for xb, ybot in [(5.2, 34.12), (13, 34.12), (20.5, 34.12)]:
ax.annotate('', xy=(xb, 34.12), xytext=(xb, 34.45),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 34.12, 13, 34.12)
# arrows from step4 down to horizontal line, then down to boxes
ax.annotate('', xy=(13, 34.12), xytext=(13, 34.45-0.33),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
ax.plot([5.2, 20.5], [34.12, 34.12], color='#78909C', lw=1.6, zorder=2)
for xb in [5.2, 13, 20.5]:
ax.annotate('', xy=(xb, 34.12), xytext=(xb, 34.12),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
ax.annotate('', xy=(xb, 34.127), xytext=(xb, 34.11),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
# SIMPLER arrows: straight from 34.12 down to box tops
for xb in [5.2, 13, 20.5]:
ax.annotate('', xy=(xb, 34.12), xytext=(xb, 34.45),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
# from horizontal bar down to each box top
for xb in [5.2, 13, 20.5]:
ax.annotate('', xy=(xb, 34.12-0.001), xytext=(xb, 34.12+0.001),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
# ════════════════════════════════════════════════════════════════════════════
# DIFFERENTIAL DIAGNOSIS band
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 31.3, 24, 0.85, '#4E342E',
'DIFFERENTIAL DIAGNOSIS — Exclude before confirming BD:\n'
'MDD (no mania/hypomania) | Schizophrenia/SZA | Substance-induced mood disorder | '
'ADHD | BPD | Secondary mania (neurological/medical/iatrogenic) | Cyclothymia',
fontsize=7.5)
for xb in [5.2, 13, 20.5]:
arrow(ax, xb, 31.77, xb, 31.73)
# merge arrows to diff dx
ax.plot([5.2, 20.5], [31.77, 31.77], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 31.72), xytext=(13, 31.77),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 30.87, 13, 30.37)
# ════════════════════════════════════════════════════════════════════════════
# DIAGNOSIS CONFIRMED DIAMOND
# ════════════════════════════════════════════════════════════════════════════
diamond(ax, 13, 29.9, 7, 0.85, C_DECI, 'DIAGNOSIS CONFIRMED?', fontsize=9)
ax.text(17.3, 29.9, 'NO → Re-evaluate / refer', fontsize=7.5, color='#E65100',
weight='bold', va='center', zorder=5)
ax.annotate('', xy=(16.52, 29.9), xytext=(16.52, 29.9), zorder=2)
arrow(ax, 13, 29.47, 13, 28.95)
# ════════════════════════════════════════════════════════════════════════════
# BD TYPE ASSIGNMENT + RISK ASSESSMENT
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 28.65, 16, 0.55, C_DIAG,
'STEP 5 — ASSIGN BD SUBTYPE & RISK ASSESSMENT', fontsize=9.5, bold=True)
rounded_box(ax, 5.5, 27.8, 9.5, 1.05, '#4527A0',
'BD Subtype Assignment:\n'
'BD-I: ≥1 manic episode (± MDE, ± psychosis)\n'
'BD-II: hypomania + MDE (no full mania ever)\n'
'Cyclothymia: 2yr hypomanic + depressive sx < threshold\n'
'Specifiers: mixed, rapid cycling, peripartum, seasonal',
fontsize=7.3)
rounded_box(ax, 20.5, 27.8, 9.5, 1.05, '#B71C1C',
'Suicide / Safety Risk Assessment (MANDATORY):\n'
'Suicidal ideation, plan, intent, access to means\n'
'Hospitalisation if imminent risk\n'
'Agitation, psychosis, self-harm, harm-to-others assessment',
fontsize=7.3)
ax.plot([5.5, 20.5], [28.37, 28.37], color='#78909C', lw=1.4, zorder=2)
for xb in [5.5, 20.5]:
ax.annotate('', xy=(xb, 28.37), xytext=(13, 28.37),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
ax.annotate('', xy=(13, 28.37), xytext=(13, 28.37+0.31),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
for xb in [5.5, 20.5]:
arrow(ax, xb, 27.27, xb, 27.27)
ax.plot([5.5, 20.5], [27.27, 27.27], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 27.27), xytext=(13, 27.32),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 27.27, 13, 26.75)
# ════════════════════════════════════════════════════════════════════════════
# CURRENT PHASE DIAMOND
# ════════════════════════════════════════════════════════════════════════════
diamond(ax, 13, 26.3, 8.5, 0.95, C_DECI,
'IDENTIFY CURRENT PHASE OF ILLNESS', fontsize=9)
arrow(ax, 13, 25.82, 13, 25.32)
ax.plot([4.5, 21.5], [25.32, 25.32], color='#455A64', lw=1.8, zorder=2)
# Labels above the three branch points
for xb, label in [(4.5, 'MANIC / HYPOMANIC'), (13, 'MIXED EPISODE'), (21.5, 'DEPRESSIVE')]:
ax.text(xb, 25.52, label, ha='center', fontsize=7.5,
color='white' if label=='MIXED EPISODE' else '#455A64',
weight='bold', zorder=5,
bbox=dict(facecolor=C_MANIA if 'MANIC' in label else
C_MIXED if 'MIXED' in label else C_DEPRES,
edgecolor='none', boxstyle='round,pad=0.2', alpha=0.9))
for xb in [4.5, 13, 21.5]:
ax.annotate('', xy=(xb, 25.32), xytext=(xb, 25.47),
arrowprops=dict(arrowstyle='->', color='#455A64', lw=1.6), zorder=2)
# ════════════════════════════════════════════════════════════════════════════
# THREE TREATMENT BRANCHES
# ════════════════════════════════════════════════════════════════════════════
# ── MANIA BRANCH (left) ──────────────────────────────────────────────────
rounded_box(ax, 4.5, 24.55, 8.8, 0.65, C_MANIA,
'ACUTE MANIA / HYPOMANIA MANAGEMENT', fontsize=8, bold=True)
rounded_box(ax, 4.5, 23.55, 8.8, 1.35, '#C62828',
'1st LINE (BD-I Mania):\n'
'Lithium OR Divalproex/Valproate OR\n'
'Atypical antipsychotic monotherapy:\n'
'Risperidone | Quetiapine | Aripiprazole\n'
'Asenapine | Cariprazine | Olanzapine\n'
'Consider combination if severe/psychotic',
fontsize=7.2)
rounded_box(ax, 4.5, 22.25, 8.8, 0.95, '#E53935',
'2nd LINE: Ziprasidone | Haloperidol\n'
'Carbamazepine | Paliperidone\n'
'Add benzodiazepine (short-term) for agitation',
fontsize=7.2)
rounded_box(ax, 4.5, 21.4, 8.8, 0.65, '#FF7043',
'ECT: severe/refractory mania, catatonia,\ndelirious mania, extreme agitation',
fontsize=7.2)
rounded_box(ax, 4.5, 20.7, 8.8, 0.55, '#BF360C',
'STOP antidepressants | Minimise stimulants',
fontsize=7.2)
for y1, y2 in [(24.22, 24.22), (23.22, 22.72), (21.92, 21.72), (21.07, 20.97)]:
pass
# arrows within mania column
for ytop, ybot in [(24.22, 23.22), (22.77, 21.72), (21.07, 20.97)]:
ax.annotate('', xy=(4.5, ybot), xytext=(4.5, ytop),
arrowprops=dict(arrowstyle='->', color=C_MANIA, lw=1.3), zorder=2)
# ── MIXED EPISODE BRANCH (centre) ────────────────────────────────────────
rounded_box(ax, 13, 24.55, 8.8, 0.65, C_MIXED,
'MIXED EPISODE MANAGEMENT', fontsize=8, bold=True)
rounded_box(ax, 13, 23.6, 8.8, 1.2, '#E65100',
'1st LINE:\n'
'Divalproex/Valproate (preferred over lithium)\n'
'Atypical antipsychotic:\n'
'Olanzapine | Quetiapine | Aripiprazole | Asenapine\n'
'Cariprazine (evidence for mixed features)',
fontsize=7.2)
rounded_box(ax, 13, 22.4, 8.8, 0.8, '#EF6C00',
'2nd LINE: Carbamazepine | Ziprasidone\n'
'Lithium (less effective for mixed)\n'
'Short-term BZD for acute distress',
fontsize=7.2)
rounded_box(ax, 13, 21.65, 8.8, 0.55, '#FF8F00',
'AVOID antidepressants (worsen mixed features)\nAVOID monotherapy lithium if dysphoric',
fontsize=7.2, text_color='white')
for ytop, ybot in [(24.22, 24.19), (23.0, 22.8), (22.0, 21.93)]:
ax.annotate('', xy=(13, ybot), xytext=(13, ytop),
arrowprops=dict(arrowstyle='->', color=C_MIXED, lw=1.3), zorder=2)
# ── DEPRESSION BRANCH (right) ────────────────────────────────────────────
rounded_box(ax, 21.5, 24.55, 8.8, 0.65, C_DEPRES,
'ACUTE BIPOLAR DEPRESSION MANAGEMENT', fontsize=8, bold=True)
rounded_box(ax, 21.5, 23.55, 8.8, 1.35, '#1B5E20',
'1st LINE (BD-I & BD-II):\n'
'Quetiapine (XR) monotherapy\n'
'Lithium + Lamotrigine\n'
'Divalproex | Lurasidone (± lithium/divalproex)\n'
'Cariprazine (BD-I depression — FDA approved)\n'
'Lumateperone (BD-I & II — FDA approved 2021)',
fontsize=7.2)
rounded_box(ax, 21.5, 22.2, 8.8, 1.0, '#2E7D32',
'2nd LINE: Olanzapine-fluoxetine combination\n'
'Lamotrigine monotherapy | Lithium monotherapy\n'
'Antidepressant + mood stabilizer (if no mixed/rapid)\n'
'(SSRI/SNRI — add only to mood stabilizer backbone)',
fontsize=7.2)
rounded_box(ax, 21.5, 21.35, 8.8, 0.6, '#33691E',
'CAUTION: Antidepressants alone — risk of manic switch\nMonitor closely for emergent hypomania/mania',
fontsize=7.2)
rounded_box(ax, 21.5, 20.7, 8.8, 0.45, '#558B2F',
'ECT: severe depression, suicidality, catatonia, pregnancy',
fontsize=7.2)
for ytop, ybot in [(24.22, 24.19), (22.87, 22.7), (21.7, 21.65), (21.07, 20.92)]:
ax.annotate('', xy=(21.5, ybot), xytext=(21.5, ytop),
arrowprops=dict(arrowstyle='->', color=C_DEPRES, lw=1.3), zorder=2)
# ── Converge to response assessment ─────────────────────────────────────
# Bottom of three branches → response assessment
for xb, ybot in [(4.5, 20.37+0.3), (13, 21.37), (21.5, 20.42+0.25)]:
pass
ax.plot([4.5, 21.5], [20.25, 20.25], color='#455A64', lw=1.8, zorder=2)
for xb, ytop in [(4.5, 20.47), (13, 21.37), (21.5, 20.47)]:
ax.annotate('', xy=(xb, 20.25), xytext=(xb, ytop),
arrowprops=dict(arrowstyle='->', color='#455A64', lw=1.4), zorder=2)
ax.annotate('', xy=(13, 20.25), xytext=(13, 20.3),
arrowprops=dict(arrowstyle='->', color='#455A64', lw=1.4), zorder=2)
# ════════════════════════════════════════════════════════════════════════════
# RESPONSE ASSESSMENT DIAMOND
# ════════════════════════════════════════════════════════════════════════════
arrow(ax, 13, 20.25, 13, 19.8)
diamond(ax, 13, 19.35, 8.5, 0.85, C_DECI,
'ASSESS RESPONSE AT 2–4 WEEKS', fontsize=9)
# Adequate response path
ax.text(12.5, 18.78, '✓ Adequate response', fontsize=7.5, color='#1B5E20',
weight='bold', ha='center', zorder=5)
arrow(ax, 13, 18.92, 13, 18.42)
# No response path (right)
rounded_box(ax, 20, 19.35, 8.5, 0.85, '#B71C1C',
'INADEQUATE RESPONSE / POOR TOLERABILITY:\n'
'Review diagnosis & adherence | Check drug levels\n'
'Exclude comorbidity (thyroid, substance use, personality disorder)\n'
'Optimise dose → Switch to 2nd-line → Combination therapy\n'
'Consider LAI (long-acting injectable) for adherence issues',
fontsize=7.2)
ax.annotate('', xy=(16.52, 19.35), xytext=(16.52, 19.35),
arrowprops=dict(arrowstyle='->', color='#B71C1C', lw=1.3), zorder=2)
ax.annotate('', xy=(15.77, 19.35), xytext=(16.52, 19.35),
arrowprops=dict(arrowstyle='->', color='#B71C1C', lw=1.3), zorder=2)
# ════════════════════════════════════════════════════════════════════════════
# MAINTENANCE TREATMENT
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 18.1, 16, 0.6, C_MAINT,
'STEP 6 — MAINTENANCE / RELAPSE PREVENTION', fontsize=9.5, bold=True)
rounded_box(ax, 5.5, 17.05, 9.5, 1.2, '#6A1B9A',
'1st LINE Maintenance Agents:\n'
'Lithium (gold standard — antisuicidal effect)\n'
'Divalproex/Valproate | Quetiapine\n'
'Aripiprazole | Lamotrigine (depressive relapse)\n'
'Asenapine | Cariprazine (adjunctive)',
fontsize=7.3)
rounded_box(ax, 20.5, 17.05, 9.5, 1.2, '#7B1FA2',
'2nd LINE Maintenance:\n'
'Olanzapine | Carbamazepine\n'
'Ziprasidone (adjunctive) | Risperidone\n'
'Combination: Lithium + Valproate (superior)\n'
'LAI (risperidone, aripiprazole) if adherence issues',
fontsize=7.3)
ax.plot([5.5, 20.5], [17.82, 17.82], color='#78909C', lw=1.4, zorder=2)
for xb in [5.5, 20.5]:
ax.annotate('', xy=(xb, 17.82), xytext=(13, 17.82),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
ax.annotate('', xy=(13, 17.82), xytext=(13, 17.82+0.28),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
for xb in [5.5, 20.5]:
arrow(ax, xb, 16.45, xb, 16.45)
ax.plot([5.5, 20.5], [16.45, 16.45], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 16.45), xytext=(13, 16.5),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 16.45, 13, 15.92)
# Duration note
rounded_box(ax, 13, 15.65, 18, 0.5, '#4527A0',
'DURATION: Continue maintenance ≥2 years after first episode | ≥5 years after second episode | '
'Often lifelong after 3+ episodes or high-risk features',
fontsize=7.5)
arrow(ax, 13, 15.39, 13, 14.87)
# ════════════════════════════════════════════════════════════════════════════
# PSYCHOSOCIAL & PSYCHOEDUCATION
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 14.6, 16, 0.55, C_STEP,
'STEP 7 — PSYCHOSOCIAL INTERVENTIONS (ALL PHASES)', fontsize=9.5, bold=True)
rounded_box(ax, 6, 13.7, 10.5, 1.15, '#1565C0',
'Psychoeducation (patient & family):\n'
'Illness nature, triggers, early warning signs\n'
'Adherence to medication | Lifestyle regularity\n'
'Sleep hygiene | Substance avoidance',
fontsize=7.4)
rounded_box(ax, 20, 13.7, 10.5, 1.15, '#1565C0',
'Evidence-based psychotherapy:\n'
'CBT | Interpersonal & Social Rhythm Therapy (IPSRT)\n'
'Family-focused therapy | DBT (mixed features/BPD)\n'
'Mindfulness-based cognitive therapy (MBCT)',
fontsize=7.4)
ax.plot([6, 20], [14.32, 14.32], color='#78909C', lw=1.4, zorder=2)
for xb in [6, 20]:
ax.annotate('', xy=(xb, 14.32), xytext=(13, 14.32),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
ax.annotate('', xy=(13, 14.32), xytext=(13, 14.32+0.28),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.3), zorder=2)
for xb in [6, 20]:
arrow(ax, xb, 13.12, xb, 13.12)
ax.plot([6, 20], [13.12, 13.12], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 13.12), xytext=(13, 13.17),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 13.12, 13, 12.62)
# ════════════════════════════════════════════════════════════════════════════
# MONITORING
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 12.35, 16, 0.55, C_STEP,
'STEP 8 — ONGOING MONITORING', fontsize=9.5, bold=True)
rounded_box(ax, 13, 11.55, 22, 1.15, '#37474F',
'Regular follow-up every 1–3 months (stable) | Every 1–4 weeks (acute phase)\n'
'Lithium: serum levels + renal function + TFTs every 6 months\n'
'Valproate: LFTs + FBC + drug levels | Carbamazepine: FBC + LFTs + drug levels\n'
'Atypical antipsychotics: metabolic monitoring (weight, glucose, lipids) every 3–6 months\n'
'Mood charting | Suicide risk reassessment at each visit',
fontsize=7.4)
arrow(ax, 13, 12.07, 13, 11.12+0.0)
arrow(ax, 13, 10.97, 13, 10.47)
# ════════════════════════════════════════════════════════════════════════════
# SPECIAL SITUATIONS
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 10.2, 16, 0.55, C_SPECIAL,
'STEP 9 — SPECIAL SITUATIONS & COMORBIDITIES', fontsize=9.5, bold=True)
rounded_box(ax, 4.5, 9.2, 8.5, 1.3, '#006064',
'PREGNANCY / PERIPARTUM:\n'
'Avoid valproate (teratogenic)\n'
'Lithium: cautious use (Ebstein risk low but real)\n'
'Lamotrigine: preferred for depression maintenance\n'
'Quetiapine/Olanzapine: acceptable if needed\n'
'ECT: safe option for severe episodes',
fontsize=7.2)
rounded_box(ax, 13, 9.2, 8.5, 1.3, '#00695C',
'RAPID CYCLING (≥4 episodes/year):\n'
'Optimise mood stabiliser\n'
'Valproate preferred (better than Li alone)\n'
'Thyroid/substance comorbidity check\n'
'AVOID antidepressants\n'
'Combination mood stabiliser therapy',
fontsize=7.2)
rounded_box(ax, 21.5, 9.2, 8.5, 1.3, '#004D40',
'COMORBIDITIES:\n'
'Anxiety: quetiapine, lamotrigine preferred\n'
'Substance use disorder: address concurrently\n'
'ADHD: cautious stimulant use (+ mood stabiliser)\n'
'Metabolic syndrome: favour agents with low\n'
'metabolic risk (aripiprazole, lamotrigine)',
fontsize=7.2)
ax.plot([4.5, 21.5], [9.92, 9.92], color='#78909C', lw=1.4, zorder=2)
for xb in [4.5, 13, 21.5]:
ax.annotate('', xy=(xb, 9.92), xytext=(13, 9.92),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
ax.annotate('', xy=(13, 9.92), xytext=(13, 9.92+0.28),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.2), zorder=2)
for xb in [4.5, 13, 21.5]:
arrow(ax, xb, 8.55, xb, 8.55)
ax.plot([4.5, 21.5], [8.55, 8.55], color='#78909C', lw=1.4, zorder=2)
ax.annotate('', xy=(13, 8.55), xytext=(13, 8.6),
arrowprops=dict(arrowstyle='->', color='#78909C', lw=1.4), zorder=2)
arrow(ax, 13, 8.55, 13, 8.05)
# ════════════════════════════════════════════════════════════════════════════
# TREATMENT RESISTANCE
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 7.8, 16, 0.55, '#B71C1C',
'TREATMENT RESISTANCE PATHWAY', fontsize=9.5, bold=True)
rounded_box(ax, 13, 7.05, 22, 1.0, '#C62828',
'1. Confirm diagnosis | 2. Confirm adherence | 3. Optimise serum drug levels\n'
'4. Exclude organic/comorbid causes | 5. Clozapine (BD-I refractory mania/mixed)\n'
'6. ECT (any refractory phase) | 7. Consider rTMS (depression) | '
'8. Multidisciplinary team review | 9. Ketamine (investigational for refractory depression)',
fontsize=7.3)
arrow(ax, 13, 7.52, 13, 6.55)
arrow(ax, 13, 6.54, 13, 6.04)
# ════════════════════════════════════════════════════════════════════════════
# CHRONIC DISEASE MODEL BOX
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 5.75, 22, 0.9, '#1A237E',
'CHRONIC DISEASE MANAGEMENT MODEL (BD is a lifelong illness):\n'
'Shared decision-making | Patient-centred care | Peer support | Care coordination\n'
'Advanced care directives | Regular review of wellbeing, functioning & quality of life',
fontsize=7.5)
arrow(ax, 13, 5.29, 13, 4.79)
# ════════════════════════════════════════════════════════════════════════════
# CONTINUOUS MONITORING LOOP ARROW (back to step 8)
# ════════════════════════════════════════════════════════════════════════════
rounded_box(ax, 13, 4.5, 14, 0.55, '#263238',
'Symptom recurrence / episode detected → Return to Phase-Specific Acute Treatment',
fontsize=7.5)
ax.annotate('', xy=(0.8, 4.5), xytext=(6.0, 4.5),
arrowprops=dict(arrowstyle='->', color='#263238', lw=1.4), zorder=2)
ax.annotate('', xy=(0.8, 26.3), xytext=(0.8, 4.5),
arrowprops=dict(arrowstyle='->', color='#263238', lw=1.4, linestyle='dashed'), zorder=2)
ax.plot([0.8, 0.8], [4.5, 26.3], color='#263238', lw=1.4, linestyle='dashed', zorder=2)
ax.text(0.38, 15.4, 'RELAPSE', fontsize=7, color='#263238', weight='bold',
rotation=90, va='center', zorder=5)
ax.text(0.38, 15.0, 'LOOP', fontsize=7, color='#263238', weight='bold',
rotation=90, va='center', zorder=5)
# ════════════════════════════════════════════════════════════════════════════
# LEGEND / KEY
# ════════════════════════════════════════════════════════════════════════════
legend_patches = [
mpatches.Patch(color=C_MANIA, label='Mania / Hypomania Branch'),
mpatches.Patch(color=C_MIXED, label='Mixed Episode Branch'),
mpatches.Patch(color=C_DEPRES, label='Depression Branch'),
mpatches.Patch(color=C_MAINT, label='Maintenance Therapy'),
mpatches.Patch(color=C_SPECIAL, label='Special Situations'),
mpatches.Patch(color=C_DECI, label='Decision / Assessment'),
mpatches.Patch(color=C_STEP, label='Diagnostic / Management Step'),
mpatches.Patch(color='#B71C1C', label='Treatment Resistance'),
]
ax.legend(handles=legend_patches, loc='lower right',
bbox_to_anchor=(1.0, 0.0),
fontsize=7, framealpha=0.9, title='Colour Key', title_fontsize=7.5,
edgecolor='#455A64', facecolor='#ECEFF1')
# Footer
ax.text(13, 0.35,
'Sources: DSM-5-TR (APA 2022) | CANMAT/ISBD Guidelines 2023 | NICE Bipolar Disorder Guidelines 2025 | VA/DoD CPG 2023 | Goldman-Cecil Medicine\n'
'This flowchart is for educational use only — clinical judgement, local formulary, and patient preferences must guide individual decisions.',
ha='center', va='center', fontsize=6.5, color='#607D8B', style='italic')
plt.tight_layout(pad=0.4)
plt.savefig('/home/daytona/workspace/bipolar-flowchart/bipolar_flowchart.png',
dpi=170, bbox_inches='tight', facecolor='#F7F9FC')
print("Saved successfully.")
cd /home/daytona/workspace/bipolar-flowchart && python bipolar_flowchart.py 2>&1
ls -lh /home/daytona/workspace/bipolar-flowchart/bipolar_flowchart.png
fetch_image tool only works with HTTP URLs, not local file paths. The image is ready for download.bipolar flowchart
Image · PNG
| Step | Content |
|---|---|
| 1 | Initial presentation (mood, sleep, energy, behaviour) |
| 2 | Screening - MDQ and BSDS tools |
| 3 | Full assessment - history, exclude organic causes, investigations (TFTs, renal, ECG, urine drug screen) |
| 4 | DSM-5-TR criteria for manic episode (BD-I), hypomanic episode (BD-II), and MDE displayed side-by-side |
| 5 | BD subtype assignment (I, II, cyclothymia, specifiers) + mandatory suicide/safety risk assessment |