{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8cc79bc5",
   "metadata": {},
   "source": [
    "# Units\n",
    "\n",
    "In `braincell`, **every physical quantity carries an explicit unit.** This is\n",
    "not optional decoration — it is enforced. Passing a bare number where a quantity\n",
    "is expected raises a `TypeError`. Units come from\n",
    "[`brainunit`](https://github.com/chaobrain/brainunit), which `braincell`\n",
    "re-exports nothing of; you import it directly as `u`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5243789e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import brainunit as u"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af66e9c3",
   "metadata": {},
   "source": [
    "## Why mandatory units?\n",
    "\n",
    "Biophysical models mix quantities across many orders of magnitude — millivolts,\n",
    "nanoamps, microfarads per square centimeter. Silent unit mismatches are one of\n",
    "the most common and hardest-to-find bugs in neural modeling. By requiring units\n",
    "everywhere, `braincell`:\n",
    "\n",
    "- catches dimensional errors *immediately*, at construction time;\n",
    "- performs automatic unit conversion (adding `10 * u.mV` to `0.01 * u.V` just\n",
    "  works);\n",
    "- makes model code self-documenting — you can see at a glance that `g_max` is a\n",
    "  conductance density.\n",
    "\n",
    "```{important}\n",
    "The rejection of bare numbers happens in `normalize_param` inside\n",
    "`braincell._misc`. If you see `TypeError: ... expected a quantity`, you almost\n",
    "certainly passed a plain `float` or `int` where a united quantity was required.\n",
    "```\n",
    "\n",
    "## Creating quantities\n",
    "\n",
    "Multiply a number (or array) by a unit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2d625e2a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import jax.numpy as jnp\n",
    "import brainunit as u\n",
    "\n",
    "v_rest = -65.0 * u.mV                 # scalar\n",
    "dt     = 0.1 * u.ms\n",
    "\n",
    "lengths = np.array([10.5, 20.0]) * u.um   # numpy array\n",
    "coords  = jnp.zeros((10, 3)) * u.um       # JAX array\n",
    "radii   = [2.0, 3.0, 4.0] * u.um          # list"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e9a6bb1",
   "metadata": {},
   "source": [
    "## Common units\n",
    "\n",
    "| Category | Quantity | Units |\n",
    "|----------|----------|-------|\n",
    "| Electrical | Voltage | `u.V`, `u.mV` |\n",
    "| Electrical | Current | `u.A`, `u.mA`, `u.uA`, `u.nA`, `u.pA` |\n",
    "| Electrical | Conductance | `u.S`, `u.mS`, `u.uS`, `u.nS`, `u.pS` |\n",
    "| Electrical | Resistance | `u.ohm`, `u.kohm`, `u.Mohm` |\n",
    "| Electrical | Capacitance | `u.F`, `u.uF`, `u.nF`, `u.pF` |\n",
    "| Space/Time | Length | `u.m`, `u.cm`, `u.mm`, `u.um` |\n",
    "| Space/Time | Time | `u.s`, `u.ms` |\n",
    "| Substance/Temp | Concentration | `u.M`, `u.mM` |\n",
    "| Substance/Temp | Temperature | `u.kelvin`, `u.celsius` |\n",
    "\n",
    "Prefixes follow SI: `m` (milli), `u` (micro), `n` (nano), `p` (pico),\n",
    "`k` (kilo), `M` (mega).\n",
    "\n",
    "## Arithmetic and dimensional analysis\n",
    "\n",
    "Units propagate through arithmetic and combine into compound dimensions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18f19300",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Conductance density × voltage → current density (Ohm's law)\n",
    "g = 50 * u.nS / u.cm**2\n",
    "I = g * (-65 * u.mV)\n",
    "\n",
    "# Geometry: surface area of a cylinder\n",
    "radius, length = 1.0 * u.um, 10.0 * u.um\n",
    "area = 2 * np.pi * radius * length     # → u.um**2\n",
    "\n",
    "# Membrane capacitance density\n",
    "cm = 1.0 * u.uF / u.cm**2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5ca3d7f",
   "metadata": {},
   "source": [
    "## Extracting raw values\n",
    "\n",
    "When you need a plain number — for plotting, say — strip the unit explicitly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5bac232d",
   "metadata": {},
   "outputs": [],
   "source": [
    "from brainunit import get_unit, get_mantissa\n",
    "\n",
    "a = jnp.array([1, 2, 3]) * u.mV\n",
    "\n",
    "get_unit(a)        # u.mV\n",
    "get_mantissa(a)    # array([1, 2, 3])\n",
    "\n",
    "a.to_decimal(u.V)  # array([0.001, 0.002, 0.003]) — float in target unit\n",
    "a / u.mV           # array([1., 2., 3.])           — divide the unit out\n",
    "a.to(u.V)          # 0.001 * u.V ...               — convert, keep unit"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f560a36",
   "metadata": {},
   "source": [
    "```{tip}\n",
    "For plotting, `value.to_decimal(u.ms)` (or whatever unit you want on the axis)\n",
    "gives you a clean float array. You will see this pattern throughout the\n",
    "examples.\n",
    "```\n",
    "\n",
    "## See also\n",
    "\n",
    "- {doc}`mechanisms` — every mechanism parameter is a united quantity.\n",
    "- {doc}`../developer/troubleshooting` — the most common unit-related errors and fixes."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
