qnty 0.0.2__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qnty
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: High-performance unit system library for Python with dimensional safety and fast unit conversions
5
5
  License: Apache-2.0
6
6
  Keywords: units,dimensional analysis,engineering,physics,quantities,measurements
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "qnty"
3
- version = "0.0.2"
3
+ version = "0.0.3"
4
4
  description = "High-performance unit system library for Python with dimensional safety and fast unit conversions"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -0,0 +1,175 @@
1
+ """
2
+ Qnty - High-Performance Unit System for Engineering
3
+ ====================================================
4
+
5
+ A fast, type-safe unit system library for Python with dimensional safety
6
+ and optimized unit conversions for engineering calculations.
7
+ """
8
+
9
+ from .dimension import BaseDimension, DimensionSignature
10
+ from .equation import Equation
11
+ from .expression import Expression
12
+ from .unit import registry
13
+ from .units import register_all_units
14
+ from .variable import FastQuantity, TypeSafeSetter, TypeSafeVariable
15
+ from .variables import (
16
+ AbsorbedDose,
17
+ Acceleration,
18
+ ActivationEnergy,
19
+ AmountOfSubstance,
20
+ AnglePlane,
21
+ AngleSolid,
22
+ AngularAcceleration,
23
+ AngularMomentum,
24
+ Area,
25
+ AreaPerUnitVolume,
26
+ AtomicWeight,
27
+ Concentration,
28
+ DynamicFluidity,
29
+ ElectricCapacitance,
30
+ ElectricCharge,
31
+ ElectricCurrentIntensity,
32
+ ElectricDipoleMoment,
33
+ ElectricFieldStrength,
34
+ ElectricInductance,
35
+ ElectricPotential,
36
+ ElectricResistance,
37
+ ElectricalConductance,
38
+ ElectricalPermittivity,
39
+ ElectricalResistivity,
40
+ EnergyFlux,
41
+ EnergyHeatWork,
42
+ EnergyPerUnitArea,
43
+ Force,
44
+ ForceBody,
45
+ ForcePerUnitMass,
46
+ FrequencyVoltageRatio,
47
+ FuelConsumption,
48
+ HeatOfCombustion,
49
+ HeatOfFusion,
50
+ HeatOfVaporization,
51
+ HeatTransferCoefficient,
52
+ Illuminance,
53
+ KineticEnergyOfTurbulence,
54
+ Length,
55
+ LinearMassDensity,
56
+ LinearMomentum,
57
+ LuminanceSelf,
58
+ LuminousFlux,
59
+ LuminousIntensity,
60
+ MagneticField,
61
+ MagneticFlux,
62
+ MagneticInductionFieldStrength,
63
+ MagneticMoment,
64
+ MagneticPermeability,
65
+ MagnetomotiveForce,
66
+ Mass,
67
+ MassDensity,
68
+ MassFlowRate,
69
+ MassFlux,
70
+ MassFractionOfI,
71
+ MassTransferCoefficient,
72
+ MolalityOfSoluteI,
73
+ MolarConcentrationByMass,
74
+ MolarFlowRate,
75
+ MolarFlux,
76
+ MolarHeatCapacity,
77
+ MolarityOfI,
78
+ MoleFractionOfI,
79
+ MomentOfInertia,
80
+ MomentumFlowRate,
81
+ MomentumFlux,
82
+ NormalityOfSolution,
83
+ ParticleDensity,
84
+ Permeability,
85
+ PhotonEmissionRate,
86
+ PowerPerUnitMass,
87
+ PowerPerUnitVolume,
88
+ PowerThermalDuty,
89
+ Pressure,
90
+ RadiationDoseEquivalent,
91
+ RadiationExposure,
92
+ Radioactivity,
93
+ SecondMomentOfArea,
94
+ SecondRadiationConstantPlanck,
95
+ SpecificEnthalpy,
96
+ SpecificGravity,
97
+ SpecificHeatCapacityConstantPressure,
98
+ SpecificLength,
99
+ SpecificSurface,
100
+ SpecificVolume,
101
+ Stress,
102
+ SurfaceMassDensity,
103
+ SurfaceTension,
104
+ Temperature,
105
+ ThermalConductivity,
106
+ Time,
107
+ Torque,
108
+ TurbulenceEnergyDissipationRate,
109
+ VelocityAngular,
110
+ VelocityLinear,
111
+ ViscosityDynamic,
112
+ ViscosityKinematic,
113
+ Volume,
114
+ VolumeFractionOfI,
115
+ VolumetricCalorificHeatingValue,
116
+ VolumetricCoefficientOfExpansion,
117
+ VolumetricFlowRate,
118
+ VolumetricFlux,
119
+ VolumetricMassFlowRate,
120
+ Wavenumber
121
+ )
122
+
123
+ # Register all units to the global registry
124
+ register_all_units(registry)
125
+
126
+ # Finalize registry after all registrations
127
+ registry.finalize_registration()
128
+
129
+ # Version information
130
+ __version__ = "0.0.3"
131
+
132
+ # Define public API
133
+ __all__ = [
134
+ # Core variable types (most commonly used)
135
+ "Length", "Pressure", "Temperature", "Time", "Mass", "Volume", "Area",
136
+ "Force", "EnergyHeatWork", "PowerThermalDuty",
137
+
138
+ # Core classes for advanced usage
139
+ "FastQuantity", "TypeSafeVariable", "TypeSafeSetter",
140
+ "DimensionSignature", "BaseDimension",
141
+ "Expression", "Equation",
142
+
143
+ # All other variable types (95 additional types)
144
+ "AbsorbedDose", "Acceleration", "ActivationEnergy", "AmountOfSubstance",
145
+ "AnglePlane", "AngleSolid", "AngularAcceleration", "AngularMomentum",
146
+ "AreaPerUnitVolume", "AtomicWeight", "Concentration", "DynamicFluidity",
147
+ "ElectricCapacitance", "ElectricCharge", "ElectricCurrentIntensity",
148
+ "ElectricDipoleMoment", "ElectricFieldStrength", "ElectricInductance",
149
+ "ElectricPotential", "ElectricResistance", "ElectricalConductance",
150
+ "ElectricalPermittivity", "ElectricalResistivity", "EnergyFlux",
151
+ "EnergyPerUnitArea", "ForceBody", "ForcePerUnitMass",
152
+ "FrequencyVoltageRatio", "FuelConsumption", "HeatOfCombustion",
153
+ "HeatOfFusion", "HeatOfVaporization", "HeatTransferCoefficient",
154
+ "Illuminance", "KineticEnergyOfTurbulence", "LinearMassDensity",
155
+ "LinearMomentum", "LuminanceSelf", "LuminousFlux", "LuminousIntensity",
156
+ "MagneticField", "MagneticFlux", "MagneticInductionFieldStrength",
157
+ "MagneticMoment", "MagneticPermeability", "MagnetomotiveForce",
158
+ "MassDensity", "MassFlowRate", "MassFlux", "MassFractionOfI",
159
+ "MassTransferCoefficient", "MolalityOfSoluteI", "MolarConcentrationByMass",
160
+ "MolarFlowRate", "MolarFlux", "MolarHeatCapacity", "MolarityOfI",
161
+ "MoleFractionOfI", "MomentOfInertia", "MomentumFlowRate", "MomentumFlux",
162
+ "NormalityOfSolution", "ParticleDensity", "Permeability",
163
+ "PhotonEmissionRate", "PowerPerUnitMass", "PowerPerUnitVolume",
164
+ "RadiationDoseEquivalent", "RadiationExposure", "Radioactivity",
165
+ "SecondMomentOfArea", "SecondRadiationConstantPlanck", "SpecificEnthalpy",
166
+ "SpecificGravity", "SpecificHeatCapacityConstantPressure",
167
+ "SpecificLength", "SpecificSurface", "SpecificVolume", "Stress",
168
+ "SurfaceMassDensity", "SurfaceTension", "ThermalConductivity", "Torque",
169
+ "TurbulenceEnergyDissipationRate", "VelocityAngular", "VelocityLinear",
170
+ "ViscosityDynamic", "ViscosityKinematic", "VolumeFractionOfI",
171
+ "VolumetricCalorificHeatingValue", "VolumetricCoefficientOfExpansion",
172
+ "VolumetricFlowRate", "VolumetricFlux", "VolumetricMassFlowRate",
173
+ "Wavenumber",
174
+ ]
175
+
@@ -0,0 +1,184 @@
1
+ """
2
+ Dimension System
3
+ ================
4
+
5
+ Compile-time dimensional analysis using type system for ultra-fast operations.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from enum import IntEnum
10
+ from typing import final
11
+
12
+
13
+ class BaseDimension(IntEnum):
14
+ """Base dimensions as prime numbers for efficient bit operations."""
15
+ LENGTH = 2
16
+ MASS = 3
17
+ TIME = 5
18
+ CURRENT = 7
19
+ TEMPERATURE = 11
20
+ AMOUNT = 13
21
+ LUMINOSITY = 17
22
+
23
+
24
+ @final
25
+ @dataclass(frozen=True)
26
+ class DimensionSignature:
27
+ """Immutable dimension signature for zero-cost dimensional analysis."""
28
+
29
+ # Store as bit pattern for ultra-fast comparison
30
+ _signature: int = 1
31
+
32
+ @classmethod
33
+ def create(cls, length=0, mass=0, time=0, current=0, temp=0, amount=0, luminosity=0):
34
+ """Create dimension from exponents."""
35
+ signature = 1
36
+ if length != 0:
37
+ signature *= BaseDimension.LENGTH ** length
38
+ if mass != 0:
39
+ signature *= BaseDimension.MASS ** mass
40
+ if time != 0:
41
+ signature *= BaseDimension.TIME ** time
42
+ if current != 0:
43
+ signature *= BaseDimension.CURRENT ** current
44
+ if temp != 0:
45
+ signature *= BaseDimension.TEMPERATURE ** temp
46
+ if amount != 0:
47
+ signature *= BaseDimension.AMOUNT ** amount
48
+ if luminosity != 0:
49
+ signature *= BaseDimension.LUMINOSITY ** luminosity
50
+
51
+ return cls(signature)
52
+
53
+ def __mul__(self, other):
54
+ return DimensionSignature(self._signature * other._signature)
55
+
56
+ def __truediv__(self, other):
57
+ return DimensionSignature(self._signature // other._signature)
58
+
59
+ def __pow__(self, power):
60
+ return DimensionSignature(self._signature ** power)
61
+
62
+ def is_compatible(self, other):
63
+ """Ultra-fast dimensional compatibility check."""
64
+ return self._signature == other._signature
65
+
66
+ def __eq__(self, other):
67
+ """Fast equality check for dimensions."""
68
+ return isinstance(other, DimensionSignature) and self._signature == other._signature
69
+
70
+ def __hash__(self):
71
+ """Enable dimensions as dictionary keys."""
72
+ return hash(self._signature)
73
+
74
+
75
+ # Pre-defined dimension constants (alphabetically ordered)
76
+ ABSORBED_DOSE = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
77
+ ACCELERATION = DimensionSignature.create(length=1, time=-2) # L T^-2
78
+ ACTIVATION_ENERGY = DimensionSignature.create(amount=-1, length=2, time=-2) # N^-1 L^2 T^-2
79
+ AMOUNT = DimensionSignature.create(amount=1) # N
80
+ AMOUNT_OF_SUBSTANCE = DimensionSignature.create(amount=1) # N
81
+ ANGLE_PLANE = DimensionSignature.create() # Dimensionless
82
+ ANGLE_SOLID = DimensionSignature.create() # Dimensionless
83
+ ANGULAR_ACCELERATION = DimensionSignature.create(time=-2) # T^-2
84
+ ANGULAR_MOMENTUM = DimensionSignature.create(length=2, mass=1, time=-1) # L^2 M T^-1
85
+ AREA = DimensionSignature.create(length=2) # L^2
86
+ AREA_PER_UNIT_VOLUME = DimensionSignature.create(length=-1) # L^-1
87
+ ATOMIC_WEIGHT = DimensionSignature.create(amount=-1, mass=1) # N^-1 M
88
+ CONCENTRATION = DimensionSignature.create(length=-3, mass=1) # L^-3 M
89
+ CURRENT = DimensionSignature.create(current=1) # A
90
+ DIMENSIONLESS = DimensionSignature.create() # Dimensionless
91
+ DYNAMIC_FLUIDITY = DimensionSignature.create(length=1, mass=-1, time=1) # L M^-1 T
92
+ ELECTRICAL_CONDUCTANCE = DimensionSignature.create(current=2, length=-2, mass=-1, time=3) # A^2 L^-2 M^-1 T^3
93
+ ELECTRICAL_PERMITTIVITY = DimensionSignature.create(current=2, length=-3, mass=-1, time=4) # A^2 L^-3 M^-1 T^4
94
+ ELECTRICAL_RESISTIVITY = DimensionSignature.create(current=-2, length=3, mass=1, time=-3) # A^-2 L^3 M T^-3
95
+ ELECTRIC_CAPACITANCE = DimensionSignature.create(current=2, length=-2, mass=-1, time=4) # A^2 L^-2 M^-1 T^4
96
+ ELECTRIC_CHARGE = DimensionSignature.create(amount=-1, current=1, time=1) # N^-1 A T
97
+ ELECTRIC_CURRENT_INTENSITY = DimensionSignature.create(current=1) # A
98
+ ELECTRIC_DIPOLE_MOMENT = DimensionSignature.create(current=1, length=1, time=1) # A L T
99
+ ELECTRIC_FIELD_STRENGTH = DimensionSignature.create(current=-1, length=1, mass=1, time=-3) # A^-1 L M T^-3
100
+ ELECTRIC_INDUCTANCE = DimensionSignature.create(current=-2, length=2, mass=1, time=-2) # A^-2 L^2 M T^-2
101
+ ELECTRIC_POTENTIAL = DimensionSignature.create(current=-1, length=2, mass=1, time=-3) # A^-1 L^2 M T^-3
102
+ ELECTRIC_RESISTANCE = DimensionSignature.create(current=-2, length=2, mass=1, time=-3) # A^-2 L^2 M T^-3
103
+ ENERGY_FLUX = DimensionSignature.create(mass=1, time=-3) # M T^-3
104
+ ENERGY_HEAT_WORK = DimensionSignature.create(length=2, mass=1, time=-2) # L^2 M T^-2
105
+ ENERGY_PER_UNIT_AREA = DimensionSignature.create(mass=1, time=-2) # M T^-2
106
+ FORCE = DimensionSignature.create(length=1, mass=1, time=-2) # L M T^-2
107
+ FORCE_BODY = DimensionSignature.create(length=-2, mass=1, time=-2) # L^-2 M T^-2
108
+ FORCE_PER_UNIT_MASS = DimensionSignature.create(length=1, time=-2) # L T^-2
109
+ FREQUENCY_VOLTAGE_RATIO = DimensionSignature.create(current=1, length=-2, mass=-1, time=3) # A L^-2 M^-1 T^3
110
+ FUEL_CONSUMPTION = DimensionSignature.create(length=-2) # L^-2
111
+ HEAT_OF_COMBUSTION = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
112
+ HEAT_OF_FUSION = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
113
+ HEAT_OF_VAPORIZATION = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
114
+ HEAT_TRANSFER_COEFFICIENT = DimensionSignature.create(mass=1, temp=-1, time=-3) # M Θ^-1 T^-3
115
+ ILLUMINANCE = DimensionSignature.create(length=-2, luminosity=1) # L^-2 J
116
+ KINETIC_ENERGY_OF_TURBULENCE = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
117
+ LENGTH = DimensionSignature.create(length=1) # L
118
+ LINEAR_MASS_DENSITY = DimensionSignature.create(length=-1, mass=1) # L^-1 M
119
+ LINEAR_MOMENTUM = DimensionSignature.create(length=1, mass=1, time=-1) # L M T^-1
120
+ LUMINANCE_SELF = DimensionSignature.create(length=-2, luminosity=1) # L^-2 J
121
+ LUMINOSITY = DimensionSignature.create(luminosity=1) # J
122
+ LUMINOUS_FLUX = DimensionSignature.create(luminosity=1) # J
123
+ LUMINOUS_INTENSITY = DimensionSignature.create(luminosity=1) # J
124
+ MAGNETIC_FIELD = DimensionSignature.create(current=1, length=-1) # A L^-1
125
+ MAGNETIC_FLUX = DimensionSignature.create(current=-1, length=2, mass=1, time=-2) # A^-1 L^2 M T^-2
126
+ MAGNETIC_INDUCTION_FIELD_STRENGTH = DimensionSignature.create(current=-1, mass=1, time=-2) # A^-1 M T^-2
127
+ MAGNETIC_MOMENT = DimensionSignature.create(current=1, length=2) # A L^2
128
+ MAGNETIC_PERMEABILITY = DimensionSignature.create(current=-2, length=2, mass=1, time=-2) # A^-2 L^2 M T^-2
129
+ MAGNETOMOTIVE_FORCE = DimensionSignature.create(current=1) # A
130
+ MASS = DimensionSignature.create(mass=1) # M
131
+ MASS_DENSITY = DimensionSignature.create(length=-3, mass=1) # L^-3 M
132
+ MASS_FLOW_RATE = DimensionSignature.create(mass=1, time=-1) # M T^-1
133
+ MASS_FLUX = DimensionSignature.create(length=-2, mass=1, time=-1) # L^-2 M T^-1
134
+ MASS_FRACTION_OF_I = DimensionSignature.create() # Dimensionless
135
+ MASS_TRANSFER_COEFFICIENT = DimensionSignature.create(length=-2, mass=1, time=-1) # L^-2 M T^-1
136
+ MOLALITY_OF_SOLUTE_I = DimensionSignature.create(amount=1, mass=-1) # N M^-1
137
+ MOLARITY_OF_I = DimensionSignature.create(amount=1, length=-3) # N L^-3
138
+ MOLAR_CONCENTRATION_BY_MASS = DimensionSignature.create(amount=1) # N
139
+ MOLAR_FLOW_RATE = DimensionSignature.create(amount=1, time=-1) # N T^-1
140
+ MOLAR_FLUX = DimensionSignature.create(amount=1, length=-2, time=-1) # N L^-2 T^-1
141
+ MOLAR_HEAT_CAPACITY = DimensionSignature.create(amount=-1, length=2, temp=-1, time=-2) # N^-1 L^2 Θ^-1 T^-2
142
+ MOLE_FRACTION_OF_I = DimensionSignature.create() # Dimensionless
143
+ MOMENTUM_FLOW_RATE = DimensionSignature.create(length=1, mass=1, time=-2) # L M T^-2
144
+ MOMENTUM_FLUX = DimensionSignature.create(length=-1, mass=1, time=-2) # L^-1 M T^-2
145
+ MOMENT_OF_INERTIA = DimensionSignature.create(length=2, mass=1) # L^2 M
146
+ NORMALITY_OF_SOLUTION = DimensionSignature.create(amount=1, length=-3) # N L^-3
147
+ PARTICLE_DENSITY = DimensionSignature.create(length=-3) # L^-3
148
+ PERMEABILITY = DimensionSignature.create(length=2) # L^2
149
+ PHOTON_EMISSION_RATE = DimensionSignature.create(length=-2, time=-1) # L^-2 T^-1
150
+ POWER_PER_UNIT_MASS = DimensionSignature.create(length=2, time=-3) # L^2 T^-3
151
+ POWER_PER_UNIT_VOLUME = DimensionSignature.create(length=-1, mass=1, time=-3) # L^-1 M T^-3
152
+ POWER_THERMAL_DUTY = DimensionSignature.create(length=2, mass=1, time=-3) # L^2 M T^-3
153
+ PRESSURE = DimensionSignature.create(length=-1, mass=1, time=-2) # L^-1 M T^-2
154
+ RADIATION_DOSE_EQUIVALENT = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
155
+ RADIATION_EXPOSURE = DimensionSignature.create(current=1, mass=-1, time=1) # A M^-1 T
156
+ RADIOACTIVITY = DimensionSignature.create(time=-1) # T^-1
157
+ SECOND_MOMENT_OF_AREA = DimensionSignature.create(length=4) # L^4
158
+ SECOND_RADIATION_CONSTANT_PLANCK = DimensionSignature.create(length=1, temp=1) # L Θ
159
+ SPECIFIC_ENTHALPY = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
160
+ SPECIFIC_GRAVITY = DimensionSignature.create() # Dimensionless
161
+ SPECIFIC_HEAT_CAPACITY_CONSTANT_PRESSURE = DimensionSignature.create(length=2, mass=1, temp=-1, time=-2) # L^2 M Θ^-1 T^-2
162
+ SPECIFIC_LENGTH = DimensionSignature.create(length=1, mass=-1) # L M^-1
163
+ SPECIFIC_SURFACE = DimensionSignature.create(length=2, mass=-1) # L^2 M^-1
164
+ SPECIFIC_VOLUME = DimensionSignature.create(length=3, mass=-1) # L^3 M^-1
165
+ STRESS = DimensionSignature.create(length=-1, mass=1, time=-2) # L^-1 M T^-2
166
+ SURFACE_MASS_DENSITY = DimensionSignature.create(length=-2, mass=1) # L^-2 M
167
+ SURFACE_TENSION = DimensionSignature.create(mass=1, time=-2) # M T^-2
168
+ TEMPERATURE = DimensionSignature.create(temp=1) # Θ
169
+ THERMAL_CONDUCTIVITY = DimensionSignature.create(length=1, mass=1, temp=1, time=-3) # L M Θ T^-3
170
+ TIME = DimensionSignature.create(time=1) # T
171
+ TORQUE = DimensionSignature.create(length=2, mass=1, time=-2) # L^2 M T^-2
172
+ TURBULENCE_ENERGY_DISSIPATION_RATE = DimensionSignature.create(length=2, time=-3) # L^2 T^-3
173
+ VELOCITY_ANGULAR = DimensionSignature.create(time=-1) # T^-1
174
+ VELOCITY_LINEAR = DimensionSignature.create(length=1, time=-1) # L T^-1
175
+ VISCOSITY_DYNAMIC = DimensionSignature.create(length=-1, mass=1, time=-1) # L^-1 M T^-1
176
+ VISCOSITY_KINEMATIC = DimensionSignature.create(length=2, time=-1) # L^2 T^-1
177
+ VOLUME = DimensionSignature.create(length=3) # L^3
178
+ VOLUMETRIC_CALORIFIC_HEATING_VALUE = DimensionSignature.create(length=-1, mass=1, time=-2) # L^-1 M T^-2
179
+ VOLUMETRIC_COEFFICIENT_OF_EXPANSION = DimensionSignature.create(length=-3, mass=1, temp=-1) # L^-3 M Θ^-1
180
+ VOLUMETRIC_FLOW_RATE = DimensionSignature.create(length=3, time=-1) # L^3 T^-1
181
+ VOLUMETRIC_FLUX = DimensionSignature.create(length=1, time=-1) # L T^-1
182
+ VOLUMETRIC_MASS_FLOW_RATE = DimensionSignature.create(length=-3, mass=1, time=-1) # L^-3 M T^-1
183
+ VOLUME_FRACTION_OF_I = DimensionSignature.create() # Dimensionless
184
+ WAVENUMBER = DimensionSignature.create(length=-1) # L^-1
@@ -0,0 +1,229 @@
1
+ """
2
+ SI Prefix System
3
+ ================
4
+
5
+ Standard SI prefixes for unit multiplication/division.
6
+ Provides systematic handling of metric prefixes like kilo-, milli-, micro-, etc.
7
+ """
8
+
9
+ from dataclasses import dataclass
10
+ from enum import Enum
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class SIPrefix:
15
+ """
16
+ Standard SI prefix definition.
17
+
18
+ Attributes:
19
+ name: Full prefix name (e.g., "kilo", "milli")
20
+ symbol: Standard symbol (e.g., "k", "m")
21
+ factor: Multiplication factor relative to base unit (e.g., 1000, 0.001)
22
+ """
23
+ name: str
24
+ symbol: str
25
+ factor: float
26
+
27
+ def apply_to_name(self, base_name: str) -> str:
28
+ """Apply prefix to a base unit name."""
29
+ if not self.name:
30
+ return base_name
31
+ return f"{self.name}{base_name}"
32
+
33
+ def apply_to_symbol(self, base_symbol: str) -> str:
34
+ """Apply prefix to a base unit symbol."""
35
+ if not self.symbol:
36
+ return base_symbol
37
+ return f"{self.symbol}{base_symbol}"
38
+
39
+
40
+ class StandardPrefixes(Enum):
41
+ """
42
+ Standard SI prefixes with their multiplication factors.
43
+
44
+ From yotta (10^24) to yocto (10^-24).
45
+ """
46
+ # Larger prefixes (10^3 to 10^24)
47
+ YOTTA = SIPrefix("yotta", "Y", 1e24)
48
+ ZETTA = SIPrefix("zetta", "Z", 1e21)
49
+ EXA = SIPrefix("exa", "E", 1e18)
50
+ PETA = SIPrefix("peta", "P", 1e15)
51
+ TERA = SIPrefix("tera", "T", 1e12)
52
+ GIGA = SIPrefix("giga", "G", 1e9)
53
+ MEGA = SIPrefix("mega", "M", 1e6)
54
+ KILO = SIPrefix("kilo", "k", 1e3)
55
+ HECTO = SIPrefix("hecto", "h", 1e2)
56
+ DECA = SIPrefix("deca", "da", 1e1)
57
+
58
+ # Base (no prefix)
59
+ NONE = SIPrefix("", "", 1.0)
60
+
61
+ # Smaller prefixes (10^-1 to 10^-24)
62
+ DECI = SIPrefix("deci", "d", 1e-1)
63
+ CENTI = SIPrefix("centi", "c", 1e-2)
64
+ MILLI = SIPrefix("milli", "m", 1e-3)
65
+ MICRO = SIPrefix("micro", "μ", 1e-6)
66
+ NANO = SIPrefix("nano", "n", 1e-9)
67
+ PICO = SIPrefix("pico", "p", 1e-12)
68
+ FEMTO = SIPrefix("femto", "f", 1e-15)
69
+ ATTO = SIPrefix("atto", "a", 1e-18)
70
+ ZEPTO = SIPrefix("zepto", "z", 1e-21)
71
+ YOCTO = SIPrefix("yocto", "y", 1e-24)
72
+
73
+
74
+ # Common prefix groups for different unit types
75
+ COMMON_LENGTH_PREFIXES = [
76
+ StandardPrefixes.KILO,
77
+ StandardPrefixes.CENTI,
78
+ StandardPrefixes.MILLI,
79
+ StandardPrefixes.MICRO,
80
+ StandardPrefixes.NANO,
81
+ ]
82
+
83
+ COMMON_MASS_PREFIXES = [
84
+ StandardPrefixes.KILO, # Note: kilogram is the SI base unit
85
+ StandardPrefixes.MILLI,
86
+ StandardPrefixes.MICRO,
87
+ ]
88
+
89
+ COMMON_TIME_PREFIXES = [
90
+ StandardPrefixes.MILLI,
91
+ StandardPrefixes.MICRO,
92
+ StandardPrefixes.NANO,
93
+ StandardPrefixes.PICO,
94
+ ]
95
+
96
+ COMMON_ELECTRIC_PREFIXES = [
97
+ StandardPrefixes.KILO,
98
+ StandardPrefixes.MILLI,
99
+ StandardPrefixes.MICRO,
100
+ StandardPrefixes.NANO,
101
+ StandardPrefixes.PICO,
102
+ ]
103
+
104
+ COMMON_ENERGY_PREFIXES = [
105
+ StandardPrefixes.KILO,
106
+ StandardPrefixes.MEGA,
107
+ StandardPrefixes.GIGA,
108
+ ]
109
+
110
+ COMMON_POWER_PREFIXES = [
111
+ StandardPrefixes.KILO,
112
+ StandardPrefixes.MEGA,
113
+ StandardPrefixes.GIGA,
114
+ StandardPrefixes.MILLI,
115
+ StandardPrefixes.MICRO,
116
+ ]
117
+
118
+ COMMON_PRESSURE_PREFIXES = [
119
+ StandardPrefixes.KILO,
120
+ StandardPrefixes.MEGA,
121
+ StandardPrefixes.GIGA,
122
+ ]
123
+
124
+
125
+ def get_prefix_by_name(name: str) -> SIPrefix | None:
126
+ """Get a prefix by its name (e.g., 'kilo', 'milli')."""
127
+ for prefix_enum in StandardPrefixes:
128
+ if prefix_enum.value.name == name:
129
+ return prefix_enum.value
130
+ return None
131
+
132
+
133
+ def get_prefix_by_symbol(symbol: str) -> SIPrefix | None:
134
+ """Get a prefix by its symbol (e.g., 'k', 'm')."""
135
+ for prefix_enum in StandardPrefixes:
136
+ if prefix_enum.value.symbol == symbol:
137
+ return prefix_enum.value
138
+ return None
139
+
140
+
141
+ def get_prefix_by_factor(factor: float, tolerance: float = 1e-10) -> SIPrefix | None:
142
+ """Get a prefix by its multiplication factor."""
143
+ for prefix_enum in StandardPrefixes:
144
+ if abs(prefix_enum.value.factor - factor) < tolerance:
145
+ return prefix_enum.value
146
+ return None
147
+
148
+
149
+ # Define which units should get automatic prefixes
150
+ PREFIXABLE_UNITS = {
151
+ # Base SI units
152
+ 'meter': COMMON_LENGTH_PREFIXES,
153
+ 'gram': COMMON_MASS_PREFIXES,
154
+ 'second': COMMON_TIME_PREFIXES,
155
+ 'ampere': COMMON_ELECTRIC_PREFIXES,
156
+ 'kelvin': [], # Temperature usually doesn't use prefixes
157
+ 'mole': [
158
+ StandardPrefixes.MILLI,
159
+ StandardPrefixes.MICRO
160
+ ],
161
+ 'candela': [], # Luminous intensity rarely uses prefixes
162
+
163
+ # Derived SI units
164
+ 'pascal': COMMON_PRESSURE_PREFIXES,
165
+ 'joule': COMMON_ENERGY_PREFIXES,
166
+ 'watt': COMMON_POWER_PREFIXES,
167
+ 'coulomb': COMMON_ELECTRIC_PREFIXES,
168
+ 'volt': COMMON_ELECTRIC_PREFIXES,
169
+ 'farad': [
170
+ StandardPrefixes.MILLI,
171
+ StandardPrefixes.MICRO,
172
+ StandardPrefixes.NANO,
173
+ StandardPrefixes.PICO
174
+ ],
175
+ 'ohm': [
176
+ StandardPrefixes.KILO,
177
+ StandardPrefixes.MEGA,
178
+ StandardPrefixes.MILLI
179
+ ],
180
+ 'siemens': [
181
+ StandardPrefixes.MILLI,
182
+ StandardPrefixes.MICRO
183
+ ],
184
+ 'weber': [
185
+ StandardPrefixes.MILLI,
186
+ StandardPrefixes.MICRO
187
+ ],
188
+ 'tesla': [
189
+ StandardPrefixes.MILLI,
190
+ StandardPrefixes.MICRO,
191
+ StandardPrefixes.NANO
192
+ ],
193
+ 'henry': [
194
+ StandardPrefixes.MILLI,
195
+ StandardPrefixes.MICRO,
196
+ StandardPrefixes.NANO
197
+ ],
198
+ 'lumen': [],
199
+ 'lux': [],
200
+ 'becquerel': [
201
+ StandardPrefixes.KILO,
202
+ StandardPrefixes.MEGA,
203
+ StandardPrefixes.GIGA
204
+ ],
205
+ 'gray': [
206
+ StandardPrefixes.MILLI,
207
+ StandardPrefixes.MICRO
208
+ ],
209
+ 'sievert': [
210
+ StandardPrefixes.MILLI,
211
+ StandardPrefixes.MICRO
212
+ ],
213
+ 'hertz': [
214
+ StandardPrefixes.KILO,
215
+ StandardPrefixes.MEGA,
216
+ StandardPrefixes.GIGA
217
+ ],
218
+ 'newton': [
219
+ StandardPrefixes.KILO,
220
+ StandardPrefixes.MILLI
221
+ ],
222
+ 'bar': [
223
+ StandardPrefixes.MILLI
224
+ ], # Common non-SI unit
225
+ 'liter': [
226
+ StandardPrefixes.MILLI,
227
+ StandardPrefixes.MICRO
228
+ ], # Common non-SI unit
229
+ }