Units & Tensors
The phaethon.units module is the mathematical core of the Phaethon framework. At its center is the BaseUnit—a highly optimized, dimensionally-aware tensor that deeply integrates with NumPy's C-API.
While BaseUnit is the underlying engine, you will never instantiate it directly. Instead, you interact with its concrete descendants (like u.Meter, u.Newton, or u.Decibel). Every physical entity you instantiate inherits a massive arsenal of properties, array mechanics, formatting tools, and casting methods from this foundational class.
(Note: For documentation on how these units interact mathematically via operators like + or *, please refer to the Dimensional Algebra section).
BaseUnit (NumPy) vs. PTensor (PyTorch)
The BaseUnit class documented here is a pure NumPy subclass meant for high-speed CPU computations and Data Engineering. If you are building Physics-Informed Neural Networks (PINNs) and require autograd/GPU tracking, do not use BaseUnit. Instead, use the specialized PyTorch equivalent: PTensor.
Unit Instantiation
The most direct way to create a physical entity in Phaethon is by calling the unit's class constructor directly. Because Phaethon is built on top of NumPy, these constructors natively accept scalars, standard Python lists, or raw multidimensional arrays.
Example Usage:
import phaethon.units as u
import numpy as np
# 1. Scalar Instantiation
temperature = u.Kelvin(300.5)
# 2. Vector / List Instantiation
velocities = u.MeterPerSecond([10.0, 15.5, 20.0])
# 3. Wrapping Raw NumPy Tensors
raw_grid = np.random.rand(10, 10)
pressure_field = u.Pascal(raw_grid)
print(pressure_field.shape)
# Output: (10, 10)
Native Array Proxies (NumPy Protocol)
Legacy unit libraries use slow, high-level "wrappers" that degrade performance. Phaethon takes a hyper-optimized approach: it implements the NumPy Array Protocol (__array_ufunc__ and __array_function__).
While structurally storing the array, BaseUnit acts as a Zero-Overhead Native Proxy. It completely bypasses Python-level bottlenecks by directly intercepting math operations at the NumPy C-API layer, ensuring your arrays retain their physical DNA without sacrificing raw C-speed.
phaethon.array
A fast, physics-aware tensor constructor. It seamlessly converts Python lists, scalars, or existing NumPy ndarray objects directly into a Phaethon NumPy subclass, permanently fusing the specified physical dimension into the array.
Arguments:
ndarray.'kg') or a class.Returns:
Example Usage:
import phaethon as ptn
import phaethon.units as u
velocity = ptn.array([[10.5, 20.1], [5.0, 9.8]], unit=u.MeterPerSecond, dtype="float32")
print(velocity.shape)
# Output: (2, 2)
phaethon.asarray
Converts the input to an array, attaching physics without copying the underlying memory if the input is already a compatible ndarray. Essential for wrapping massive simulation grids with zero overhead.
Arguments:
Returns:
Example Usage:
import numpy as np
import phaethon as ptn
# A massive 1GB numerical grid
massive_grid = np.zeros((1000, 1000, 125))
# Wraps the pointer instantly without duplicating the 1GB memory
physical_grid = ptn.asarray(massive_grid, unit='J')
phaethon.asanyarray
Similar to asarray, but allows subclasses of ndarray (like numpy.ma.MaskedArray) to pass through without losing their subclass identity. Phaethon preserves the subclass behavior entirely inside the physics envelope.
Arguments:
Returns:
Example Usage:
import numpy.ma as ma
import phaethon as ptn
# A masked array containing invalid/missing sensor data
sensor_data = ma.masked_array([10.5, -999.0, 12.0], mask=[0, 1, 0])
# Phaethon preserves the mask inside the physics tensor
safe_temp = ptn.asanyarray(sensor_data, unit='degC')
print(safe_temp.mag.mask)
# Output: [False True False]
Properties & Array Mechanics
Every instantiated physical tensor automatically exposes NumPy-native properties and methods. These methods route calculations directly to the underlying C-engine while perfectly preserving their physical dimension.
Core Properties
"energy").() for scalars.Shaping & Reductions
NumPy Subclassing & Delegation
Phaethon's BaseUnit acts as a highly optimized NumPy proxy. It intercepts NumPy's C-API calls via __array_ufunc__ and __array_function__, allowing you to use native NumPy functions directly on physical tensors.
np.sqrt(Area) automatically returns Length).Example Usage:
import numpy as np
import phaethon.units as u
import phaethon as ptn
# instantiating a negative velocity
with ptn.using(axiom_strictness_level='ignore'):
velocities = u.MeterPerSecond([-10.0, 5.0, -2.5])
# Absolute value is safely passed to the NumPy C-Engine
absolute_v = np.abs(velocities)
print(absolute_v)
# Output: <MeterPerSecond Array: [10. 5. 2.5] m/s>
# NumPy square triggers Dimensional Synthesis
squared = np.square(velocities)
print(squared)
# Output: <JoulePerKilogram Array: [100, 25, 6.25]>
print(squared.dimension)
# Output: 'specific_energy'
Direct Attribute Delegation: If a specific NumPy method is not natively overridden by Phaethon, the tensor will automatically search the underlying NumPy ndarray and delegate the call, wrapping the result back into the physics envelope if applicable.
Diagnostics & Formatting
Phaethon units provide powerful built-in methods to inspect their underlying SI structure or format their output for UIs.
.decompose()
Returns a string representation of the unit's absolute, canonical SI base structure (its fundamental DNA). It breaks down complex derived units (like Watts or Pascals) into their fundamental SI exponents (Kilograms, Meters, Seconds, etc.).
Returns:
Example Usage:
.format()
Applies precise structural and numeric formatting to the magnitude using pure float formatting, bypassing the default __str__ behavior.
Arguments:
4).True for ",", or supply a custom string like ".").True).Returns:
Example Usage:
import phaethon.units as u
distance = u.Meter(12500.55)
print(distance.format(delim=True, prec=1))
# Output: 12,500.6 m
print(distance.format(scinote=True, sigfigs=3, tag=False))
# Output: 1.25E+04
Dimensional Casting & Escaping
Phaethon is designed to be strictly typed and physically guarded. However, advanced scientific computing often requires traversing domains, stripping semantic ghosts, or linearizing complex logarithmic scales. Phaethon provides deterministic methods to safely navigate these operations.
.to() (Standard Casting)
Safely converts the unit instance to another physical unit within the same semantic domain. This operation is strictly guarded by the Physics Engine.
Arguments:
Returns:
Raises:
Example Usage:
import phaethon as ptn
import phaethon.units as u
distance = ptn.array([1.5, 3.2], unit='km')
speed = u.MeterPerSecond(150)
meters = distance.to('m') # <Meter Array: [1500.0, 3200.0]>
kmh = speed.to(u.KilometerPerHour) # # <KilometerPerHour: 540.0 km/h>
.si (The Core Extractor / De-Phantomizer)
The semantic escape hatch. It explicitly attacks the DNA of the unit. It strips away all Phantom Units (e.g., cycle, decay, radian), gracefully downgrading the entity to its pure, generic SI blank canvas (multiplier = 1.0).
Use this when you need to intentionally break a domain lock (e.g., extracting raw energy from torque) to perform generalized mathematics.
Example Usage:
import phaethon.units as u
# Torque possesses an Exclusive Domain Lock and a Phantom Unit (Angle⁻¹)
torque = u.NewtonMeter(100)
print(torque.dimension)
# Output: 'torque'
# Extracting the core strips the Phantom Unit, collapsing it back to basic Energy
raw_energy = torque.si
print(raw_energy.dimension)
# Output: 'energy'
print(raw_energy)
# Output: <Joule: 100.0 J>
~ (The Base Converter / Linearizer)
The absolute canonical scale converter (triggered using the bitwise NOT ~ operator). Unlike .si which attacks the semantics, ~ attacks the Scale.
It forces any derived multiplier (like kilo), physical constant (like SpeedOfLight), or non-linear shell (like Decibel) to collapse directly into the primary SI Base Unit of its respective dimension, while perfectly preserving its Semantic Domain Lock. It is also the primary tool for instantly linearizing logarithmic physics.
Example Usage: Scale Unrolling vs Semantic Extraction
import phaethon.units as u
# 1. Unrolling a Physical Constant
c = u.SpeedOfLight(1)
print(~c)
# Output: <MeterPerSecond: 2.9979E+08 m/s>
# 2. Preserving the Semantic Domain
torque = u.NewtonMeter(100)
print(~torque)
# Output: <NewtonMeter: 100.0 N·m> (Notice it remains Torque, unlike .si!)
# 3. Linearizing Logarithmic Scales natively
signal = u.DecibelMilliwatt(30.0) # 30 dBm
# Both approaches yield the linear SI Base (Watt),
# because stripping the log shell naturally reveals the linear base core.
linear_power = ~signal
si_power = signal.si
print(linear_power)
# Output: <Watt: 1.0 W>
print(si_power.to(u.Milliwatt))
# Output: <Milliwatt: 1000.0 mW>