qnty 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl
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.
- qnty/__init__.py +171 -2
- qnty/dimension.py +110 -12
- qnty/prefixes.py +229 -0
- qnty/unit.py +68 -0
- qnty/unit_types/__init__.py +0 -0
- qnty/unit_types/base.py +47 -0
- qnty/units.py +8079 -33
- qnty/variable.py +2 -1
- qnty/variable_types/__init__.py +0 -0
- qnty/variable_types/base.py +58 -0
- qnty/variable_types/expression_variable.py +68 -0
- qnty/variable_types/typed_variable.py +92 -0
- qnty/variables.py +2270 -196
- qnty/variables.pyi +6097 -0
- {qnty-0.0.2.dist-info → qnty-0.0.4.dist-info}/METADATA +1 -1
- qnty-0.0.4.dist-info/RECORD +19 -0
- qnty-0.0.2.dist-info/RECORD +0 -11
- {qnty-0.0.2.dist-info → qnty-0.0.4.dist-info}/WHEEL +0 -0
qnty/__init__.py
CHANGED
@@ -1,3 +1,172 @@
|
|
1
|
-
|
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
|
+
# Define public API
|
130
|
+
__all__ = [
|
131
|
+
# Core variable types (most commonly used)
|
132
|
+
"Length", "Pressure", "Temperature", "Time", "Mass", "Volume", "Area",
|
133
|
+
"Force", "EnergyHeatWork", "PowerThermalDuty",
|
134
|
+
|
135
|
+
# Core classes for advanced usage
|
136
|
+
"FastQuantity", "TypeSafeVariable", "TypeSafeSetter",
|
137
|
+
"DimensionSignature", "BaseDimension",
|
138
|
+
"Expression", "Equation",
|
139
|
+
|
140
|
+
# All other variable types (95 additional types)
|
141
|
+
"AbsorbedDose", "Acceleration", "ActivationEnergy", "AmountOfSubstance",
|
142
|
+
"AnglePlane", "AngleSolid", "AngularAcceleration", "AngularMomentum",
|
143
|
+
"AreaPerUnitVolume", "AtomicWeight", "Concentration", "DynamicFluidity",
|
144
|
+
"ElectricCapacitance", "ElectricCharge", "ElectricCurrentIntensity",
|
145
|
+
"ElectricDipoleMoment", "ElectricFieldStrength", "ElectricInductance",
|
146
|
+
"ElectricPotential", "ElectricResistance", "ElectricalConductance",
|
147
|
+
"ElectricalPermittivity", "ElectricalResistivity", "EnergyFlux",
|
148
|
+
"EnergyPerUnitArea", "ForceBody", "ForcePerUnitMass",
|
149
|
+
"FrequencyVoltageRatio", "FuelConsumption", "HeatOfCombustion",
|
150
|
+
"HeatOfFusion", "HeatOfVaporization", "HeatTransferCoefficient",
|
151
|
+
"Illuminance", "KineticEnergyOfTurbulence", "LinearMassDensity",
|
152
|
+
"LinearMomentum", "LuminanceSelf", "LuminousFlux", "LuminousIntensity",
|
153
|
+
"MagneticField", "MagneticFlux", "MagneticInductionFieldStrength",
|
154
|
+
"MagneticMoment", "MagneticPermeability", "MagnetomotiveForce",
|
155
|
+
"MassDensity", "MassFlowRate", "MassFlux", "MassFractionOfI",
|
156
|
+
"MassTransferCoefficient", "MolalityOfSoluteI", "MolarConcentrationByMass",
|
157
|
+
"MolarFlowRate", "MolarFlux", "MolarHeatCapacity", "MolarityOfI",
|
158
|
+
"MoleFractionOfI", "MomentOfInertia", "MomentumFlowRate", "MomentumFlux",
|
159
|
+
"NormalityOfSolution", "ParticleDensity", "Permeability",
|
160
|
+
"PhotonEmissionRate", "PowerPerUnitMass", "PowerPerUnitVolume",
|
161
|
+
"RadiationDoseEquivalent", "RadiationExposure", "Radioactivity",
|
162
|
+
"SecondMomentOfArea", "SecondRadiationConstantPlanck", "SpecificEnthalpy",
|
163
|
+
"SpecificGravity", "SpecificHeatCapacityConstantPressure",
|
164
|
+
"SpecificLength", "SpecificSurface", "SpecificVolume", "Stress",
|
165
|
+
"SurfaceMassDensity", "SurfaceTension", "ThermalConductivity", "Torque",
|
166
|
+
"TurbulenceEnergyDissipationRate", "VelocityAngular", "VelocityLinear",
|
167
|
+
"ViscosityDynamic", "ViscosityKinematic", "VolumeFractionOfI",
|
168
|
+
"VolumetricCalorificHeatingValue", "VolumetricCoefficientOfExpansion",
|
169
|
+
"VolumetricFlowRate", "VolumetricFlux", "VolumetricMassFlowRate",
|
170
|
+
"Wavenumber",
|
171
|
+
]
|
2
172
|
|
3
|
-
__all__ = ["Length", "Pressure", "Dimensionless"]
|
qnty/dimension.py
CHANGED
@@ -72,15 +72,113 @@ class DimensionSignature:
|
|
72
72
|
return hash(self._signature)
|
73
73
|
|
74
74
|
|
75
|
-
# Pre-defined dimension constants
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
qnty/prefixes.py
ADDED
@@ -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
|
+
}
|
qnty/unit.py
CHANGED
@@ -8,6 +8,7 @@ Unit definitions, constants and registry for the high-performance unit system.
|
|
8
8
|
from dataclasses import dataclass
|
9
9
|
|
10
10
|
from .dimension import DIMENSIONLESS, LENGTH, PRESSURE, DimensionSignature
|
11
|
+
from .prefixes import SIPrefix, StandardPrefixes
|
11
12
|
|
12
13
|
|
13
14
|
@dataclass(frozen=True)
|
@@ -18,6 +19,21 @@ class UnitDefinition:
|
|
18
19
|
dimension: DimensionSignature
|
19
20
|
si_factor: float
|
20
21
|
si_offset: float = 0.0
|
22
|
+
base_unit_name: str | None = None # Base unit without prefix
|
23
|
+
prefix: SIPrefix | None = None # SI prefix if applicable
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
def with_prefix(cls, base_def: 'UnitDefinition', prefix: SIPrefix) -> 'UnitDefinition':
|
27
|
+
"""Create a new unit definition with an SI prefix."""
|
28
|
+
return cls(
|
29
|
+
name=prefix.apply_to_name(base_def.name),
|
30
|
+
symbol=prefix.apply_to_symbol(base_def.symbol),
|
31
|
+
dimension=base_def.dimension,
|
32
|
+
si_factor=base_def.si_factor * prefix.factor,
|
33
|
+
si_offset=base_def.si_offset,
|
34
|
+
base_unit_name=base_def.name,
|
35
|
+
prefix=prefix
|
36
|
+
)
|
21
37
|
|
22
38
|
|
23
39
|
class UnitConstant:
|
@@ -50,7 +66,11 @@ class HighPerformanceRegistry:
|
|
50
66
|
self.conversion_table: dict[tuple[str, str], float] = {} # (from_unit, to_unit) -> factor
|
51
67
|
self.dimensional_groups: dict[int, list[UnitDefinition]] = {}
|
52
68
|
self._dimension_cache: dict[int, UnitConstant] = {} # Cache for common dimension mappings
|
69
|
+
self._finalized = False
|
70
|
+
self.base_units: dict[str, UnitDefinition] = {} # Track base units for prefix generation
|
71
|
+
self.prefixable_units: set[str] = set() # Track which units can have prefixes
|
53
72
|
|
73
|
+
# Keep legacy initialization for backward compatibility
|
54
74
|
self._initialize_units()
|
55
75
|
self._precompute_conversions()
|
56
76
|
|
@@ -85,8 +105,56 @@ class HighPerformanceRegistry:
|
|
85
105
|
self.dimensional_groups[dim_sig] = []
|
86
106
|
self.dimensional_groups[dim_sig].append(unit_def)
|
87
107
|
|
108
|
+
def register_unit(self, unit_def: UnitDefinition):
|
109
|
+
"""Register a single unit definition."""
|
110
|
+
if self._finalized:
|
111
|
+
raise RuntimeError("Cannot register units after registry is finalized")
|
112
|
+
|
113
|
+
self.units[unit_def.name] = unit_def
|
114
|
+
|
115
|
+
# Group by dimension
|
116
|
+
dim_sig = unit_def.dimension._signature
|
117
|
+
if dim_sig not in self.dimensional_groups:
|
118
|
+
self.dimensional_groups[dim_sig] = []
|
119
|
+
self.dimensional_groups[dim_sig].append(unit_def)
|
120
|
+
|
121
|
+
def register_with_prefixes(
|
122
|
+
self,
|
123
|
+
unit_def: UnitDefinition,
|
124
|
+
prefixes: list[StandardPrefixes] | None = None
|
125
|
+
):
|
126
|
+
"""
|
127
|
+
Register a unit and automatically generate prefixed variants.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
unit_def: The base unit definition
|
131
|
+
prefixes: List of StandardPrefixes enum values to apply. If None, uses common prefixes.
|
132
|
+
"""
|
133
|
+
if self._finalized:
|
134
|
+
raise RuntimeError("Cannot register units after registry is finalized")
|
135
|
+
|
136
|
+
# Register base unit
|
137
|
+
self.register_unit(unit_def)
|
138
|
+
self.base_units[unit_def.name] = unit_def
|
139
|
+
self.prefixable_units.add(unit_def.name)
|
140
|
+
|
141
|
+
# Generate and register prefixed variants
|
142
|
+
if prefixes:
|
143
|
+
for prefix_enum in prefixes:
|
144
|
+
prefix = prefix_enum.value
|
145
|
+
if prefix.name: # Skip NONE prefix (empty name)
|
146
|
+
prefixed_unit = UnitDefinition.with_prefix(unit_def, prefix)
|
147
|
+
self.register_unit(prefixed_unit)
|
148
|
+
|
149
|
+
def finalize_registration(self):
|
150
|
+
"""Called after all units registered to precompute conversions."""
|
151
|
+
if not self._finalized:
|
152
|
+
self._precompute_conversions()
|
153
|
+
self._finalized = True
|
154
|
+
|
88
155
|
def _precompute_conversions(self):
|
89
156
|
"""Pre-compute all unit conversions for maximum speed."""
|
157
|
+
self.conversion_table.clear() # Clear existing conversions
|
90
158
|
for group in self.dimensional_groups.values():
|
91
159
|
for from_unit in group:
|
92
160
|
for to_unit in group:
|
File without changes
|
qnty/unit_types/base.py
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
"""
|
2
|
+
Base Unit Module Definition
|
3
|
+
===========================
|
4
|
+
|
5
|
+
Provides abstract base class for unit modules and registration functionality.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
from typing import Any
|
10
|
+
|
11
|
+
from ..unit import UnitDefinition
|
12
|
+
|
13
|
+
|
14
|
+
class UnitModule(ABC):
|
15
|
+
"""Abstract base class for unit modules."""
|
16
|
+
|
17
|
+
@abstractmethod
|
18
|
+
def get_unit_definitions(self) -> list[UnitDefinition]:
|
19
|
+
"""Return list of unit definitions for this module."""
|
20
|
+
pass
|
21
|
+
|
22
|
+
@abstractmethod
|
23
|
+
def get_units_class(self) -> type[Any]:
|
24
|
+
"""Return the units class for this module."""
|
25
|
+
pass
|
26
|
+
|
27
|
+
def register_to_registry(self, unit_registry):
|
28
|
+
"""Register all unit definitions to the given registry."""
|
29
|
+
from ..unit import UnitConstant
|
30
|
+
|
31
|
+
for unit_def in self.get_unit_definitions():
|
32
|
+
if unit_def.name not in unit_registry.units:
|
33
|
+
unit_registry.register_unit(unit_def)
|
34
|
+
|
35
|
+
# Populate the units class with constants from registry
|
36
|
+
units_class = self.get_units_class()
|
37
|
+
for unit_def in self.get_unit_definitions():
|
38
|
+
unit_constant = UnitConstant(unit_registry.units[unit_def.name])
|
39
|
+
setattr(units_class, unit_def.name, unit_constant)
|
40
|
+
|
41
|
+
# Add any aliases
|
42
|
+
if unit_def.symbol and unit_def.symbol != unit_def.name:
|
43
|
+
setattr(units_class, unit_def.symbol, unit_constant)
|
44
|
+
|
45
|
+
# Special case for inch - add in_ alias since 'in' is a Python keyword
|
46
|
+
if unit_def.name == "inch":
|
47
|
+
units_class.in_ = unit_constant
|