{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Unit Conversion\n",
    "\n",
    "[![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/chaobrain/brainunit/blob/master/docs/physical_units/conversion.ipynb)\n",
    "[![Open in Kaggle](https://kaggle.com/static/images/open-in-kaggle.svg)](https://kaggle.com/kernels/welcome?src=https://github.com/chaobrain/brainunit/blob/master/docs/physical_units/conversion.ipynb)\n",
    "\n",
    "This tutorial covers converting quantities between different units, extracting numeric values, \n",
    "and working with dimensionless quantities."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "import brainunit as u\n",
    "import jax.numpy as jnp"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Converting Between Units with `to_decimal()`\n",
    "\n",
    "The `to_decimal()` method converts a `Quantity` to a plain numeric array expressed in the target unit.\n",
    "It extracts the mantissa scaled to the given unit."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "distance = 5.0 * u.kmeter\n",
    "print('Original:', distance)\n",
    "print('In meters:', distance.to_decimal(u.meter))\n",
    "print('In centimeters:', distance.to_decimal(u.cmeter))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "voltage = jnp.array([100., 200., 300.]) * u.mV\n",
    "print('Original:', voltage)\n",
    "print('In volts:', voltage.to_decimal(u.volt))\n",
    "print('In microvolts:', voltage.to_decimal(u.uvolt))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Converting to an incompatible unit raises an error:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "try:\n",
    "    distance.to_decimal(u.second)\n",
    "except Exception as e:\n",
    "    print(type(e).__name__, ':', e)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Rescaling with `in_unit()`\n",
    "\n",
    "Unlike `to_decimal()` which returns a plain array, `in_unit()` returns a new `Quantity` \n",
    "expressed in the target unit — preserving the unit information."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "t = 1500.0 * u.ms\n",
    "print('Original:', t)\n",
    "print('In seconds:', t.in_unit(u.second))\n",
    "print('In microseconds:', t.in_unit(u.usecond))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# in_unit preserves Quantity type\n",
    "t_sec = t.in_unit(u.second)\n",
    "print('Type:', type(t_sec))\n",
    "print('Unit:', t_sec.unit)\n",
    "print('Mantissa:', t_sec.mantissa)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dimensionless Quantities\n",
    "\n",
    "When you divide two quantities with the same dimension, the result is dimensionless.\n",
    "Dimensionless results are automatically returned as plain `jax.Array` values."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "ratio = (3.0 * u.kmeter) / (4.0 * u.meter)\n",
    "print('Ratio:', ratio)\n",
    "print('Type:', type(ratio))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# Since the result is already a plain number, you can convert directly\n",
    "print('As float:', float(ratio))\n",
    "print('As int:', int(ratio))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When dividing quantities with different scales but same dimension, brainunit \n",
    "automatically handles the scale conversion:"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "print('km / m:', (6.0 * u.kmeter) / (2.0 * u.meter))     # = 3000\n",
    "print('ms / us:', (1.0 * u.ms) / (500.0 * u.usecond))     # = 2.0\n",
    "print('mV / V:', (100.0 * u.mV) / (2.0 * u.volt))         # = 0.05"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Converting Quantities to Python Scalars\n",
    "\n",
    "Only dimensionless quantities can be converted to Python scalars with `float()` or `int()`.\n",
    "Quantities with units will raise a `TypeError`."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# This works: first convert to decimal, then to float\n",
    "voltage = 3.0 * u.mV\n",
    "print('As volts (float):', float(voltage.to_decimal(u.volt)))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# Direct float() on a quantity with units fails\n",
    "try:\n",
    "    float(3.0 * u.mV)\n",
    "except TypeError as e:\n",
    "    print('TypeError:', e)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extracting Mantissa and Unit\n",
    "\n",
    "You can decompose a `Quantity` into its numeric part and unit using `split_mantissa_unit()` \n",
    "or the `.mantissa` and `.unit` attributes."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "q = jnp.array([1.0, 2.0, 3.0]) * u.newton\n",
    "\n",
    "# Using attributes\n",
    "print('Mantissa:', q.mantissa)\n",
    "print('Unit:', q.unit)\n",
    "print('Dimension:', q.dim)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# Using split_mantissa_unit\n",
    "mantissa, unit = u.split_mantissa_unit(q)\n",
    "print('Mantissa:', mantissa)\n",
    "print('Unit:', unit)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# Reconstruct the quantity\n",
    "q_reconstructed = mantissa * unit\n",
    "print('Reconstructed:', q_reconstructed)"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Checking Dimension Compatibility\n",
    "\n",
    "Before converting, you can check if two quantities or units have compatible dimensions."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "print('meter and km compatible?', u.have_same_dim(u.meter, u.kmeter))\n",
    "print('meter and second compatible?', u.have_same_dim(u.meter, u.second))\n",
    "print('mV and volt compatible?', u.have_same_dim(u.mV, u.volt))\n",
    "print('newton and kg*m/s^2?', u.have_same_dim(u.newton, u.kilogram * u.meter / u.second**2))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# is_dimensionless checks if a value has no physical dimension\n",
    "print('Scalar is dimensionless?', u.is_dimensionless(3.14))\n",
    "print('Ratio is dimensionless?', u.is_dimensionless((5.0 * u.meter) / (2.0 * u.meter)))\n",
    "print('Quantity with unit is dimensionless?', u.is_dimensionless(5.0 * u.meter))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unit Display with `display_in_unit()`\n",
    "\n",
    "You can display a quantity in a specific unit without losing the `Quantity` type."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "distance = 2500.0 * u.meter\n",
    "print('Display in km:', u.display_in_unit(distance, u.kmeter))\n",
    "print('Display in cm:', u.display_in_unit(distance, u.cmeter))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Conversion Between Unit Systems\n",
    "\n",
    "brainunit supports both SI and non-SI units, allowing conversions between systems."
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# Length conversions\n",
    "d = 1.0 * u.mile\n",
    "print('1 mile in meters:', d.to_decimal(u.meter))\n",
    "print('1 mile in km:', d.to_decimal(u.kmeter))\n",
    "print('1 mile in feet:', d.to_decimal(u.foot))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# Pressure conversions  \n",
    "p = 1.0 * u.atmosphere\n",
    "print('1 atm in Pascal:', p.to_decimal(u.pascal))\n",
    "print('1 atm in bar:', p.to_decimal(u.bar))\n",
    "print('1 atm in mmHg:', p.to_decimal(u.mmHg))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "code",
   "metadata": {},
   "source": [
    "# Energy conversions\n",
    "e = 1.0 * u.joule\n",
    "print('1 joule in calories:', e.to_decimal(u.calorie))\n",
    "print('1 joule in eV:', e.to_decimal(u.electron_volt))\n",
    "print('1 joule in erg:', e.to_decimal(u.erg))"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "| Method | Returns | Use Case |\n",
    "|--------|---------|----------|\n",
    "| `q.to_decimal(unit)` | `jax.Array` | Extract numeric value in target unit |\n",
    "| `q.in_unit(unit)` | `Quantity` | Convert to target unit, keeping Quantity type |\n",
    "| `q.mantissa` | `jax.Array` | Get raw numeric value (current unit scale) |\n",
    "| `split_mantissa_unit(q)` | `(array, unit)` | Decompose into parts |\n",
    "| `display_in_unit(q, unit)` | `str` | Format for display |\n",
    "| `have_same_dim(a, b)` | `bool` | Check compatibility before conversion |"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
