qnty 0.0.4__py3-none-any.whl → 0.0.6__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 +6 -4
- qnty/dimension.py +9 -7
- qnty/expression.py +15 -3
- qnty/unit.py +2 -34
- qnty/units.py +90 -69
- qnty/variable.py +29 -4
- qnty/variable_types/typed_variable.py +8 -13
- qnty/variables.py +52 -58
- qnty/variables.pyi +88 -37
- {qnty-0.0.4.dist-info → qnty-0.0.6.dist-info}/METADATA +1 -1
- qnty-0.0.6.dist-info/RECORD +19 -0
- qnty-0.0.4.dist-info/RECORD +0 -19
- {qnty-0.0.4.dist-info → qnty-0.0.6.dist-info}/WHEEL +0 -0
qnty/__init__.py
CHANGED
@@ -25,6 +25,7 @@ from .variables import (
|
|
25
25
|
AreaPerUnitVolume,
|
26
26
|
AtomicWeight,
|
27
27
|
Concentration,
|
28
|
+
Dimensionless,
|
28
29
|
DynamicFluidity,
|
29
30
|
ElectricCapacitance,
|
30
31
|
ElectricCharge,
|
@@ -81,6 +82,7 @@ from .variables import (
|
|
81
82
|
MomentumFlux,
|
82
83
|
NormalityOfSolution,
|
83
84
|
ParticleDensity,
|
85
|
+
Percent,
|
84
86
|
Permeability,
|
85
87
|
PhotonEmissionRate,
|
86
88
|
PowerPerUnitMass,
|
@@ -129,15 +131,15 @@ registry.finalize_registration()
|
|
129
131
|
# Define public API
|
130
132
|
__all__ = [
|
131
133
|
# Core variable types (most commonly used)
|
132
|
-
"Length", "Pressure", "Temperature", "Time", "Mass",
|
133
|
-
"Force", "EnergyHeatWork", "PowerThermalDuty",
|
134
|
+
"Dimensionless", "Length", "Pressure", "Temperature", "Time", "Mass",
|
135
|
+
"Volume", "Area", "Force", "EnergyHeatWork", "PowerThermalDuty",
|
134
136
|
|
135
137
|
# Core classes for advanced usage
|
136
138
|
"FastQuantity", "TypeSafeVariable", "TypeSafeSetter",
|
137
139
|
"DimensionSignature", "BaseDimension",
|
138
140
|
"Expression", "Equation",
|
139
141
|
|
140
|
-
# All other variable types (
|
142
|
+
# All other variable types (96 additional types)
|
141
143
|
"AbsorbedDose", "Acceleration", "ActivationEnergy", "AmountOfSubstance",
|
142
144
|
"AnglePlane", "AngleSolid", "AngularAcceleration", "AngularMomentum",
|
143
145
|
"AreaPerUnitVolume", "AtomicWeight", "Concentration", "DynamicFluidity",
|
@@ -156,7 +158,7 @@ __all__ = [
|
|
156
158
|
"MassTransferCoefficient", "MolalityOfSoluteI", "MolarConcentrationByMass",
|
157
159
|
"MolarFlowRate", "MolarFlux", "MolarHeatCapacity", "MolarityOfI",
|
158
160
|
"MoleFractionOfI", "MomentOfInertia", "MomentumFlowRate", "MomentumFlux",
|
159
|
-
"NormalityOfSolution", "ParticleDensity", "Permeability",
|
161
|
+
"NormalityOfSolution", "ParticleDensity", "Percent", "Permeability",
|
160
162
|
"PhotonEmissionRate", "PowerPerUnitMass", "PowerPerUnitVolume",
|
161
163
|
"RadiationDoseEquivalent", "RadiationExposure", "Radioactivity",
|
162
164
|
"SecondMomentOfArea", "SecondRadiationConstantPlanck", "SpecificEnthalpy",
|
qnty/dimension.py
CHANGED
@@ -19,6 +19,7 @@ class BaseDimension(IntEnum):
|
|
19
19
|
TEMPERATURE = 11
|
20
20
|
AMOUNT = 13
|
21
21
|
LUMINOSITY = 17
|
22
|
+
DIMENSIONLESS = 1 # Must be 1 to act as multiplicative identity
|
22
23
|
|
23
24
|
|
24
25
|
@final
|
@@ -78,8 +79,8 @@ ACCELERATION = DimensionSignature.create(length=1, time=-2) # L T^-2
|
|
78
79
|
ACTIVATION_ENERGY = DimensionSignature.create(amount=-1, length=2, time=-2) # N^-1 L^2 T^-2
|
79
80
|
AMOUNT = DimensionSignature.create(amount=1) # N
|
80
81
|
AMOUNT_OF_SUBSTANCE = DimensionSignature.create(amount=1) # N
|
81
|
-
ANGLE_PLANE = DimensionSignature.
|
82
|
-
ANGLE_SOLID = DimensionSignature.
|
82
|
+
ANGLE_PLANE = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
83
|
+
ANGLE_SOLID = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
83
84
|
ANGULAR_ACCELERATION = DimensionSignature.create(time=-2) # T^-2
|
84
85
|
ANGULAR_MOMENTUM = DimensionSignature.create(length=2, mass=1, time=-1) # L^2 M T^-1
|
85
86
|
AREA = DimensionSignature.create(length=2) # L^2
|
@@ -87,7 +88,7 @@ AREA_PER_UNIT_VOLUME = DimensionSignature.create(length=-1) # L^-1
|
|
87
88
|
ATOMIC_WEIGHT = DimensionSignature.create(amount=-1, mass=1) # N^-1 M
|
88
89
|
CONCENTRATION = DimensionSignature.create(length=-3, mass=1) # L^-3 M
|
89
90
|
CURRENT = DimensionSignature.create(current=1) # A
|
90
|
-
DIMENSIONLESS = DimensionSignature.
|
91
|
+
DIMENSIONLESS = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
91
92
|
DYNAMIC_FLUIDITY = DimensionSignature.create(length=1, mass=-1, time=1) # L M^-1 T
|
92
93
|
ELECTRICAL_CONDUCTANCE = DimensionSignature.create(current=2, length=-2, mass=-1, time=3) # A^2 L^-2 M^-1 T^3
|
93
94
|
ELECTRICAL_PERMITTIVITY = DimensionSignature.create(current=2, length=-3, mass=-1, time=4) # A^2 L^-3 M^-1 T^4
|
@@ -131,7 +132,7 @@ MASS = DimensionSignature.create(mass=1) # M
|
|
131
132
|
MASS_DENSITY = DimensionSignature.create(length=-3, mass=1) # L^-3 M
|
132
133
|
MASS_FLOW_RATE = DimensionSignature.create(mass=1, time=-1) # M T^-1
|
133
134
|
MASS_FLUX = DimensionSignature.create(length=-2, mass=1, time=-1) # L^-2 M T^-1
|
134
|
-
MASS_FRACTION_OF_I = DimensionSignature.
|
135
|
+
MASS_FRACTION_OF_I = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
135
136
|
MASS_TRANSFER_COEFFICIENT = DimensionSignature.create(length=-2, mass=1, time=-1) # L^-2 M T^-1
|
136
137
|
MOLALITY_OF_SOLUTE_I = DimensionSignature.create(amount=1, mass=-1) # N M^-1
|
137
138
|
MOLARITY_OF_I = DimensionSignature.create(amount=1, length=-3) # N L^-3
|
@@ -139,12 +140,13 @@ MOLAR_CONCENTRATION_BY_MASS = DimensionSignature.create(amount=1) # N
|
|
139
140
|
MOLAR_FLOW_RATE = DimensionSignature.create(amount=1, time=-1) # N T^-1
|
140
141
|
MOLAR_FLUX = DimensionSignature.create(amount=1, length=-2, time=-1) # N L^-2 T^-1
|
141
142
|
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.
|
143
|
+
MOLE_FRACTION_OF_I = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
143
144
|
MOMENTUM_FLOW_RATE = DimensionSignature.create(length=1, mass=1, time=-2) # L M T^-2
|
144
145
|
MOMENTUM_FLUX = DimensionSignature.create(length=-1, mass=1, time=-2) # L^-1 M T^-2
|
145
146
|
MOMENT_OF_INERTIA = DimensionSignature.create(length=2, mass=1) # L^2 M
|
146
147
|
NORMALITY_OF_SOLUTION = DimensionSignature.create(amount=1, length=-3) # N L^-3
|
147
148
|
PARTICLE_DENSITY = DimensionSignature.create(length=-3) # L^-3
|
149
|
+
PERCENT = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
148
150
|
PERMEABILITY = DimensionSignature.create(length=2) # L^2
|
149
151
|
PHOTON_EMISSION_RATE = DimensionSignature.create(length=-2, time=-1) # L^-2 T^-1
|
150
152
|
POWER_PER_UNIT_MASS = DimensionSignature.create(length=2, time=-3) # L^2 T^-3
|
@@ -157,7 +159,7 @@ RADIOACTIVITY = DimensionSignature.create(time=-1) # T^-1
|
|
157
159
|
SECOND_MOMENT_OF_AREA = DimensionSignature.create(length=4) # L^4
|
158
160
|
SECOND_RADIATION_CONSTANT_PLANCK = DimensionSignature.create(length=1, temp=1) # L Θ
|
159
161
|
SPECIFIC_ENTHALPY = DimensionSignature.create(length=2, time=-2) # L^2 T^-2
|
160
|
-
SPECIFIC_GRAVITY = DimensionSignature.
|
162
|
+
SPECIFIC_GRAVITY = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
161
163
|
SPECIFIC_HEAT_CAPACITY_CONSTANT_PRESSURE = DimensionSignature.create(length=2, mass=1, temp=-1, time=-2) # L^2 M Θ^-1 T^-2
|
162
164
|
SPECIFIC_LENGTH = DimensionSignature.create(length=1, mass=-1) # L M^-1
|
163
165
|
SPECIFIC_SURFACE = DimensionSignature.create(length=2, mass=-1) # L^2 M^-1
|
@@ -180,5 +182,5 @@ VOLUMETRIC_COEFFICIENT_OF_EXPANSION = DimensionSignature.create(length=-3, mass=
|
|
180
182
|
VOLUMETRIC_FLOW_RATE = DimensionSignature.create(length=3, time=-1) # L^3 T^-1
|
181
183
|
VOLUMETRIC_FLUX = DimensionSignature.create(length=1, time=-1) # L T^-1
|
182
184
|
VOLUMETRIC_MASS_FLOW_RATE = DimensionSignature.create(length=-3, mass=1, time=-1) # L^-3 M T^-1
|
183
|
-
VOLUME_FRACTION_OF_I = DimensionSignature.
|
185
|
+
VOLUME_FRACTION_OF_I = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
|
184
186
|
WAVENUMBER = DimensionSignature.create(length=-1) # L^-1
|
qnty/expression.py
CHANGED
@@ -246,11 +246,23 @@ class BinaryOperation(Expression):
|
|
246
246
|
left_str = str(self.left)
|
247
247
|
right_str = str(self.right)
|
248
248
|
|
249
|
-
# Add parentheses
|
249
|
+
# Add parentheses for left side when precedence is strictly lower
|
250
250
|
if isinstance(self.left, BinaryOperation) and precedence.get(self.left.operator, 0) < precedence.get(self.operator, 0):
|
251
251
|
left_str = f"({left_str})"
|
252
|
-
|
253
|
-
|
252
|
+
|
253
|
+
# CRITICAL FIX: For right side, add parentheses when:
|
254
|
+
# 1. Precedence is strictly lower, OR
|
255
|
+
# 2. Precedence is equal AND operation is left-associative (-, /)
|
256
|
+
if isinstance(self.right, BinaryOperation):
|
257
|
+
right_prec = precedence.get(self.right.operator, 0)
|
258
|
+
curr_prec = precedence.get(self.operator, 0)
|
259
|
+
|
260
|
+
# Need parentheses if:
|
261
|
+
# - Right has lower precedence, OR
|
262
|
+
# - Same precedence and current operator is left-associative (- or /)
|
263
|
+
if (right_prec < curr_prec or
|
264
|
+
(right_prec == curr_prec and self.operator in ['-', '/'])):
|
265
|
+
right_str = f"({right_str})"
|
254
266
|
|
255
267
|
return f"{left_str} {self.operator} {right_str}"
|
256
268
|
|
qnty/unit.py
CHANGED
@@ -7,7 +7,7 @@ Unit definitions, constants and registry for the high-performance unit system.
|
|
7
7
|
|
8
8
|
from dataclasses import dataclass
|
9
9
|
|
10
|
-
from .dimension import
|
10
|
+
from .dimension import DimensionSignature
|
11
11
|
from .prefixes import SIPrefix, StandardPrefixes
|
12
12
|
|
13
13
|
|
@@ -70,40 +70,8 @@ class HighPerformanceRegistry:
|
|
70
70
|
self.base_units: dict[str, UnitDefinition] = {} # Track base units for prefix generation
|
71
71
|
self.prefixable_units: set[str] = set() # Track which units can have prefixes
|
72
72
|
|
73
|
-
#
|
74
|
-
self._initialize_units()
|
75
|
-
self._precompute_conversions()
|
73
|
+
# Registry starts empty - units are registered via register_all_units() in __init__.py
|
76
74
|
|
77
|
-
def _initialize_units(self):
|
78
|
-
"""Initialize with engineering units."""
|
79
|
-
|
80
|
-
# Length units
|
81
|
-
meter = UnitDefinition("meter", "m", LENGTH, 1.0)
|
82
|
-
millimeter = UnitDefinition("millimeter", "mm", LENGTH, 0.001)
|
83
|
-
centimeter = UnitDefinition("centimeter", "cm", LENGTH, 0.01)
|
84
|
-
inch = UnitDefinition("inch", "in", LENGTH, 0.0254)
|
85
|
-
foot = UnitDefinition("foot", "ft", LENGTH, 0.3048)
|
86
|
-
|
87
|
-
# Pressure units
|
88
|
-
pascal = UnitDefinition("pascal", "Pa", PRESSURE, 1.0)
|
89
|
-
kilopascal = UnitDefinition("kilopascal", "kPa", PRESSURE, 1000.0)
|
90
|
-
megapascal = UnitDefinition("megapascal", "MPa", PRESSURE, 1e6)
|
91
|
-
psi = UnitDefinition("psi", "psi", PRESSURE, 6894.757)
|
92
|
-
bar = UnitDefinition("bar", "bar", PRESSURE, 100000.0)
|
93
|
-
|
94
|
-
# Dimensionless units
|
95
|
-
dimensionless = UnitDefinition("dimensionless", "", DIMENSIONLESS, 1.0)
|
96
|
-
|
97
|
-
# Register all units
|
98
|
-
for unit_def in [meter, millimeter, centimeter, inch, foot,
|
99
|
-
pascal, kilopascal, megapascal, psi, bar, dimensionless]:
|
100
|
-
self.units[unit_def.name] = unit_def
|
101
|
-
|
102
|
-
# Group by dimension
|
103
|
-
dim_sig = unit_def.dimension._signature
|
104
|
-
if dim_sig not in self.dimensional_groups:
|
105
|
-
self.dimensional_groups[dim_sig] = []
|
106
|
-
self.dimensional_groups[dim_sig].append(unit_def)
|
107
75
|
|
108
76
|
def register_unit(self, unit_def: UnitDefinition):
|
109
77
|
"""Register a single unit definition."""
|
qnty/units.py
CHANGED
@@ -3,8 +3,8 @@ Comprehensive Consolidated Units Module
|
|
3
3
|
=======================================
|
4
4
|
|
5
5
|
Auto-generated consolidated unit definitions for all engineering units.
|
6
|
-
Contains
|
7
|
-
across
|
6
|
+
Contains 871 units
|
7
|
+
across 107 fields organized into 107 dimensional groups.
|
8
8
|
|
9
9
|
Generated from the complete NIST unit tables and engineering references.
|
10
10
|
"""
|
@@ -22,9 +22,6 @@ from .dimension import (
|
|
22
22
|
CONCENTRATION,
|
23
23
|
DIMENSIONLESS,
|
24
24
|
DYNAMIC_FLUIDITY,
|
25
|
-
ELECTRICAL_CONDUCTANCE,
|
26
|
-
ELECTRICAL_PERMITTIVITY,
|
27
|
-
ELECTRICAL_RESISTIVITY,
|
28
25
|
ELECTRIC_CAPACITANCE,
|
29
26
|
ELECTRIC_CHARGE,
|
30
27
|
ELECTRIC_CURRENT_INTENSITY,
|
@@ -33,6 +30,9 @@ from .dimension import (
|
|
33
30
|
ELECTRIC_INDUCTANCE,
|
34
31
|
ELECTRIC_POTENTIAL,
|
35
32
|
ELECTRIC_RESISTANCE,
|
33
|
+
ELECTRICAL_CONDUCTANCE,
|
34
|
+
ELECTRICAL_PERMITTIVITY,
|
35
|
+
ELECTRICAL_RESISTIVITY,
|
36
36
|
ENERGY_FLUX,
|
37
37
|
ENERGY_HEAT_WORK,
|
38
38
|
ENERGY_PER_UNIT_AREA,
|
@@ -65,14 +65,14 @@ from .dimension import (
|
|
65
65
|
MASS_FLUX,
|
66
66
|
MASS_TRANSFER_COEFFICIENT,
|
67
67
|
MOLALITY_OF_SOLUTE_I,
|
68
|
-
MOLARITY_OF_I,
|
69
68
|
MOLAR_CONCENTRATION_BY_MASS,
|
70
69
|
MOLAR_FLOW_RATE,
|
71
70
|
MOLAR_FLUX,
|
72
71
|
MOLAR_HEAT_CAPACITY,
|
72
|
+
MOLARITY_OF_I,
|
73
|
+
MOMENT_OF_INERTIA,
|
73
74
|
MOMENTUM_FLOW_RATE,
|
74
75
|
MOMENTUM_FLUX,
|
75
|
-
MOMENT_OF_INERTIA,
|
76
76
|
NORMALITY_OF_SOLUTION,
|
77
77
|
PARTICLE_DENSITY,
|
78
78
|
PERMEABILITY,
|
@@ -109,7 +109,7 @@ from .dimension import (
|
|
109
109
|
VOLUMETRIC_FLOW_RATE,
|
110
110
|
VOLUMETRIC_FLUX,
|
111
111
|
VOLUMETRIC_MASS_FLOW_RATE,
|
112
|
-
WAVENUMBER
|
112
|
+
WAVENUMBER,
|
113
113
|
)
|
114
114
|
|
115
115
|
# Comprehensive unit definitions organized by dimensional signature
|
@@ -843,6 +843,46 @@ UNIT_DEFINITIONS = {
|
|
843
843
|
"aliases": {}
|
844
844
|
},
|
845
845
|
|
846
|
+
"dimensionless": {
|
847
|
+
# Dimensionless - Dimensionless
|
848
|
+
"dimension": DIMENSIONLESS,
|
849
|
+
"units": [
|
850
|
+
{
|
851
|
+
"name": "dimensionless",
|
852
|
+
"symbol": "",
|
853
|
+
"si_factor": 1.0,
|
854
|
+
"full_name": "dimensionless",
|
855
|
+
"notation": "",
|
856
|
+
"aliases": [],
|
857
|
+
},
|
858
|
+
{
|
859
|
+
"name": "parts_per_billion",
|
860
|
+
"symbol": "ppb",
|
861
|
+
"si_factor": 1e-09,
|
862
|
+
"full_name": "parts per billion",
|
863
|
+
"notation": "ppb",
|
864
|
+
"aliases": ['ppb'],
|
865
|
+
},
|
866
|
+
{
|
867
|
+
"name": "parts_per_million",
|
868
|
+
"symbol": "ppm",
|
869
|
+
"si_factor": 1e-06,
|
870
|
+
"full_name": "parts per million",
|
871
|
+
"notation": "ppm",
|
872
|
+
"aliases": ['ppm'],
|
873
|
+
},
|
874
|
+
{
|
875
|
+
"name": "ratio",
|
876
|
+
"symbol": "ratio",
|
877
|
+
"si_factor": 1.0,
|
878
|
+
"full_name": "ratio",
|
879
|
+
"notation": "ratio",
|
880
|
+
"aliases": [],
|
881
|
+
}
|
882
|
+
],
|
883
|
+
"aliases": {}
|
884
|
+
},
|
885
|
+
|
846
886
|
"dynamic_fluidity": {
|
847
887
|
# Dynamic Fluidity - LENGTH MASS^-1 TIME
|
848
888
|
"dimension": DYNAMIC_FLUIDITY,
|
@@ -4979,6 +5019,38 @@ UNIT_DEFINITIONS = {
|
|
4979
5019
|
"aliases": {}
|
4980
5020
|
},
|
4981
5021
|
|
5022
|
+
"percent": {
|
5023
|
+
# Percent - Dimensionless
|
5024
|
+
"dimension": DIMENSIONLESS,
|
5025
|
+
"units": [
|
5026
|
+
{
|
5027
|
+
"name": "basis_point",
|
5028
|
+
"symbol": "bp",
|
5029
|
+
"si_factor": 0.0001,
|
5030
|
+
"full_name": "basis point",
|
5031
|
+
"notation": "bp",
|
5032
|
+
"aliases": ['bp', 'bps'],
|
5033
|
+
},
|
5034
|
+
{
|
5035
|
+
"name": "per_mille",
|
5036
|
+
"symbol": "‰",
|
5037
|
+
"si_factor": 0.001,
|
5038
|
+
"full_name": "per mille",
|
5039
|
+
"notation": "‰",
|
5040
|
+
"aliases": ['‰'],
|
5041
|
+
},
|
5042
|
+
{
|
5043
|
+
"name": "percent",
|
5044
|
+
"symbol": "%",
|
5045
|
+
"si_factor": 0.01,
|
5046
|
+
"full_name": "percent",
|
5047
|
+
"notation": "%",
|
5048
|
+
"aliases": ['%'],
|
5049
|
+
}
|
5050
|
+
],
|
5051
|
+
"aliases": {}
|
5052
|
+
},
|
5053
|
+
|
4982
5054
|
"permeability": {
|
4983
5055
|
# Permeability - LENGTH^2
|
4984
5056
|
"dimension": PERMEABILITY,
|
@@ -5617,10 +5689,10 @@ UNIT_DEFINITIONS = {
|
|
5617
5689
|
},
|
5618
5690
|
{
|
5619
5691
|
"name": "pound_force_per_square_inch",
|
5620
|
-
"symbol": "
|
5621
|
-
"si_factor": 6894.
|
5692
|
+
"symbol": "psi",
|
5693
|
+
"si_factor": 6894.757,
|
5622
5694
|
"full_name": "pound force per square inch",
|
5623
|
-
"notation": "
|
5695
|
+
"notation": "psi",
|
5624
5696
|
"aliases": ['psi'],
|
5625
5697
|
},
|
5626
5698
|
{
|
@@ -6185,10 +6257,10 @@ UNIT_DEFINITIONS = {
|
|
6185
6257
|
},
|
6186
6258
|
{
|
6187
6259
|
"name": "pound_force_per_square_inch",
|
6188
|
-
"symbol": "
|
6189
|
-
"si_factor": 6894.
|
6260
|
+
"symbol": "psi",
|
6261
|
+
"si_factor": 6894.757,
|
6190
6262
|
"full_name": "pound force per square inch",
|
6191
|
-
"notation": "
|
6263
|
+
"notation": "psi",
|
6192
6264
|
"aliases": ['psi'],
|
6193
6265
|
}
|
6194
6266
|
],
|
@@ -7824,8 +7896,8 @@ UNIT_DEFINITIONS = {
|
|
7824
7896
|
|
7825
7897
|
def create_unit_class(class_name: str, dimension_data: dict) -> type:
|
7826
7898
|
"""Dynamically create a unit class with all unit constants as attributes."""
|
7827
|
-
from .unit import UnitConstant, UnitDefinition
|
7828
7899
|
from .prefixes import get_prefix_by_name
|
7900
|
+
from .unit import UnitConstant, UnitDefinition
|
7829
7901
|
|
7830
7902
|
# Create a new class dynamically
|
7831
7903
|
unit_class = type(class_name, (), {})
|
@@ -7878,8 +7950,8 @@ def create_unit_class(class_name: str, dimension_data: dict) -> type:
|
|
7878
7950
|
|
7879
7951
|
def register_all_units(registry):
|
7880
7952
|
"""Register all unit definitions to the given registry with prefix support."""
|
7953
|
+
from .prefixes import PREFIXABLE_UNITS
|
7881
7954
|
from .unit import UnitDefinition
|
7882
|
-
from .prefixes import get_prefix_by_name, StandardPrefixes, PREFIXABLE_UNITS
|
7883
7955
|
|
7884
7956
|
# First pass: register base units with prefixes where applicable
|
7885
7957
|
for dimension_data in UNIT_DEFINITIONS.values():
|
@@ -7944,6 +8016,8 @@ AreaUnits = create_unit_class("AreaUnits", UNIT_DEFINITIONS["area"])
|
|
7944
8016
|
AreaPerUnitVolumeUnits = create_unit_class("AreaPerUnitVolumeUnits", UNIT_DEFINITIONS["area_per_unit_volume"])
|
7945
8017
|
AtomicWeightUnits = create_unit_class("AtomicWeightUnits", UNIT_DEFINITIONS["atomic_weight"])
|
7946
8018
|
ConcentrationUnits = create_unit_class("ConcentrationUnits", UNIT_DEFINITIONS["concentration"])
|
8019
|
+
DimensionlessUnits = create_unit_class("DimensionlessUnits", UNIT_DEFINITIONS["dimensionless"])
|
8020
|
+
PercentUnits = create_unit_class("PercentUnits", UNIT_DEFINITIONS["percent"])
|
7947
8021
|
DynamicFluidityUnits = create_unit_class("DynamicFluidityUnits", UNIT_DEFINITIONS["dynamic_fluidity"])
|
7948
8022
|
ElectricCapacitanceUnits = create_unit_class("ElectricCapacitanceUnits", UNIT_DEFINITIONS["electric_capacitance"])
|
7949
8023
|
ElectricChargeUnits = create_unit_class("ElectricChargeUnits", UNIT_DEFINITIONS["electric_charge"])
|
@@ -8037,56 +8111,3 @@ VolumetricFlowRateUnits = create_unit_class("VolumetricFlowRateUnits", UNIT_DEFI
|
|
8037
8111
|
VolumetricFluxUnits = create_unit_class("VolumetricFluxUnits", UNIT_DEFINITIONS["volumetric_flux"])
|
8038
8112
|
VolumetricMassFlowRateUnits = create_unit_class("VolumetricMassFlowRateUnits", UNIT_DEFINITIONS["volumetric_mass_flow_rate"])
|
8039
8113
|
WavenumberUnits = create_unit_class("WavenumberUnits", UNIT_DEFINITIONS["wavenumber"])
|
8040
|
-
|
8041
|
-
# Create standalone DimensionlessUnits class for backward compatibility
|
8042
|
-
class DimensionlessUnits:
|
8043
|
-
"""Standalone dimensionless units class."""
|
8044
|
-
from .unit import UnitConstant, UnitDefinition
|
8045
|
-
|
8046
|
-
# Standard dimensionless unit
|
8047
|
-
dimensionless_def = UnitDefinition(
|
8048
|
-
name="dimensionless",
|
8049
|
-
symbol="",
|
8050
|
-
dimension=DIMENSIONLESS,
|
8051
|
-
si_factor=1.0,
|
8052
|
-
si_offset=0.0
|
8053
|
-
)
|
8054
|
-
dimensionless = UnitConstant(dimensionless_def)
|
8055
|
-
|
8056
|
-
|
8057
|
-
# Module-level function for compatibility with existing code
|
8058
|
-
def get_consolidated_modules():
|
8059
|
-
"""Return a list of module-like objects for consolidated units."""
|
8060
|
-
class ConsolidatedModule:
|
8061
|
-
"""Mock module object for compatibility with existing registration system."""
|
8062
|
-
|
8063
|
-
def __init__(self, dimension_type: str):
|
8064
|
-
self.dimension_type = dimension_type
|
8065
|
-
self.dimension_data = UNIT_DEFINITIONS[dimension_type]
|
8066
|
-
|
8067
|
-
def register_to_registry(self, registry):
|
8068
|
-
"""Register units for this dimension to the registry."""
|
8069
|
-
from .unit import UnitDefinition
|
8070
|
-
dimension = self.dimension_data["dimension"]
|
8071
|
-
|
8072
|
-
for unit_data in self.dimension_data["units"]:
|
8073
|
-
unit_def = UnitDefinition(
|
8074
|
-
name=unit_data["name"],
|
8075
|
-
symbol=unit_data["symbol"],
|
8076
|
-
dimension=dimension,
|
8077
|
-
si_factor=unit_data["si_factor"],
|
8078
|
-
si_offset=unit_data.get("si_offset", 0.0)
|
8079
|
-
)
|
8080
|
-
|
8081
|
-
if unit_def.name not in registry.units:
|
8082
|
-
registry.register_unit(unit_def)
|
8083
|
-
|
8084
|
-
return [
|
8085
|
-
ConsolidatedModule(dimension_name) for dimension_name in UNIT_DEFINITIONS
|
8086
|
-
]
|
8087
|
-
|
8088
|
-
|
8089
|
-
# Statistics
|
8090
|
-
TOTAL_UNITS = 864
|
8091
|
-
TOTAL_FIELDS = 105
|
8092
|
-
TOTAL_DIMENSIONS = 105
|
qnty/variable.py
CHANGED
@@ -85,6 +85,11 @@ class FastQuantity:
|
|
85
85
|
if isinstance(other, int | float):
|
86
86
|
return FastQuantity(self.value * other, self.unit)
|
87
87
|
|
88
|
+
# Handle TypeSafeVariable objects by using their quantity
|
89
|
+
from .variable_types.typed_variable import TypedVariable # Avoid circular imports
|
90
|
+
if isinstance(other, TypedVariable) and other.quantity is not None:
|
91
|
+
other = other.quantity
|
92
|
+
|
88
93
|
# Fast dimensional analysis using cached signatures
|
89
94
|
result_dimension_sig = self._dimension_sig * other._dimension_sig
|
90
95
|
|
@@ -129,6 +134,7 @@ class FastQuantity:
|
|
129
134
|
|
130
135
|
# Initialize dimension cache if empty
|
131
136
|
if not registry._dimension_cache:
|
137
|
+
from .dimension import ENERGY_PER_UNIT_AREA, SURFACE_TENSION
|
132
138
|
registry._dimension_cache = {
|
133
139
|
DIMENSIONLESS._signature: DimensionlessUnits.dimensionless,
|
134
140
|
LENGTH._signature: LengthUnits.millimeter,
|
@@ -137,17 +143,25 @@ class FastQuantity:
|
|
137
143
|
VOLUME._signature: LengthUnits.millimeter, # mm³
|
138
144
|
FORCE._signature: UnitConstant(UnitDefinition("newton", "N", FORCE, 1.0)),
|
139
145
|
ENERGY._signature: UnitConstant(UnitDefinition("joule", "J", ENERGY, 1.0)),
|
146
|
+
SURFACE_TENSION._signature: UnitConstant(UnitDefinition("newton_per_meter", "N/m", SURFACE_TENSION, 1.0)),
|
147
|
+
ENERGY_PER_UNIT_AREA._signature: UnitConstant(UnitDefinition("joule_per_square_meter", "J/m²", ENERGY_PER_UNIT_AREA, 1.0)),
|
140
148
|
}
|
141
149
|
|
142
150
|
# O(1) lookup for common dimensions
|
143
151
|
if result_dimension_sig in registry._dimension_cache:
|
144
152
|
return registry._dimension_cache[result_dimension_sig]
|
145
153
|
|
146
|
-
# For rare combined dimensions, create
|
154
|
+
# For rare combined dimensions, create SI base unit with descriptive name
|
155
|
+
result_dimension = DimensionSignature(result_dimension_sig)
|
156
|
+
|
157
|
+
# Create descriptive name based on dimensional analysis
|
158
|
+
si_name = self._create_si_unit_name(result_dimension)
|
159
|
+
si_symbol = self._create_si_unit_symbol(result_dimension)
|
160
|
+
|
147
161
|
temp_unit = UnitDefinition(
|
148
|
-
name=
|
149
|
-
symbol=
|
150
|
-
dimension=
|
162
|
+
name=si_name,
|
163
|
+
symbol=si_symbol,
|
164
|
+
dimension=result_dimension,
|
151
165
|
si_factor=1.0
|
152
166
|
)
|
153
167
|
result_unit = UnitConstant(temp_unit)
|
@@ -156,6 +170,17 @@ class FastQuantity:
|
|
156
170
|
registry._dimension_cache[result_dimension_sig] = result_unit
|
157
171
|
return result_unit
|
158
172
|
|
173
|
+
def _create_si_unit_name(self, dimension: DimensionSignature) -> str:
|
174
|
+
"""Create descriptive SI unit name based on dimensional analysis."""
|
175
|
+
# For now, return a generic SI unit name. In the future, this could be enhanced
|
176
|
+
# to parse the dimension signature and create descriptive names like "newton_per_meter"
|
177
|
+
return f"si_derived_unit_{abs(hash(dimension._signature)) % 10000}"
|
178
|
+
|
179
|
+
def _create_si_unit_symbol(self, _dimension: DimensionSignature) -> str:
|
180
|
+
"""Create SI unit symbol based on dimensional analysis."""
|
181
|
+
# For complex units, return descriptive symbol based on common engineering units
|
182
|
+
return "SI_unit"
|
183
|
+
|
159
184
|
def _find_result_unit(self, result_dimension: DimensionSignature,
|
160
185
|
left_qty: FastQuantity, right_qty: FastQuantity) -> UnitConstant:
|
161
186
|
"""Legacy method - kept for compatibility."""
|
@@ -62,27 +62,22 @@ class TypedVariable(ExpressionVariable):
|
|
62
62
|
# Auto-set the value with the specified unit
|
63
63
|
setter = self._setter_class(self, value)
|
64
64
|
|
65
|
-
# Handle special unit aliases
|
66
|
-
if unit == "in": # Handle Python reserved word
|
67
|
-
unit = "inchs" # Match the actual property name
|
68
|
-
elif unit == "inches": # Handle common plural form
|
69
|
-
unit = "inchs" # Match the actual property name
|
70
|
-
|
71
65
|
# Try to find the unit property on the setter
|
72
66
|
if hasattr(setter, unit):
|
73
67
|
getattr(setter, unit)
|
74
68
|
elif hasattr(setter, unit + 's'): # Handle singular/plural
|
75
69
|
getattr(setter, unit + 's')
|
76
|
-
elif
|
77
|
-
|
78
|
-
getattr(setter, self._default_unit_property)
|
70
|
+
elif unit.endswith('s') and hasattr(setter, unit[:-1]): # Handle plural to singular
|
71
|
+
getattr(setter, unit[:-1])
|
79
72
|
else:
|
80
|
-
#
|
81
|
-
# This helps with forward compatibility
|
73
|
+
# Unit not found - provide helpful error with available units
|
82
74
|
unit_properties = [attr for attr in dir(setter)
|
83
75
|
if not attr.startswith('_') and attr != 'value' and attr != 'variable']
|
84
|
-
|
85
|
-
|
76
|
+
available_units = ', '.join(sorted(unit_properties[:10])) # Show first 10 units
|
77
|
+
if len(unit_properties) > 10:
|
78
|
+
available_units += f' ... and {len(unit_properties) - 10} more'
|
79
|
+
raise ValueError(f"Unit '{unit}' not found for {self.__class__.__name__}. "
|
80
|
+
f"Available units: {available_units}")
|
86
81
|
|
87
82
|
else:
|
88
83
|
# More specific error messages matching test expectations
|
qnty/variables.py
CHANGED
@@ -7,8 +7,9 @@ Uses the exact same source of truth and approach as consolidated units system.
|
|
7
7
|
Auto-generated from unit_data.json and dimension_mapping.json.
|
8
8
|
"""
|
9
9
|
|
10
|
-
from typing import Any
|
10
|
+
from typing import Any
|
11
11
|
|
12
|
+
from . import units
|
12
13
|
from .dimension import (
|
13
14
|
ABSORBED_DOSE,
|
14
15
|
ACCELERATION,
|
@@ -24,9 +25,6 @@ from .dimension import (
|
|
24
25
|
CONCENTRATION,
|
25
26
|
DIMENSIONLESS,
|
26
27
|
DYNAMIC_FLUIDITY,
|
27
|
-
ELECTRICAL_CONDUCTANCE,
|
28
|
-
ELECTRICAL_PERMITTIVITY,
|
29
|
-
ELECTRICAL_RESISTIVITY,
|
30
28
|
ELECTRIC_CAPACITANCE,
|
31
29
|
ELECTRIC_CHARGE,
|
32
30
|
ELECTRIC_CURRENT_INTENSITY,
|
@@ -35,6 +33,9 @@ from .dimension import (
|
|
35
33
|
ELECTRIC_INDUCTANCE,
|
36
34
|
ELECTRIC_POTENTIAL,
|
37
35
|
ELECTRIC_RESISTANCE,
|
36
|
+
ELECTRICAL_CONDUCTANCE,
|
37
|
+
ELECTRICAL_PERMITTIVITY,
|
38
|
+
ELECTRICAL_RESISTIVITY,
|
38
39
|
ENERGY_FLUX,
|
39
40
|
ENERGY_HEAT_WORK,
|
40
41
|
ENERGY_PER_UNIT_AREA,
|
@@ -68,17 +69,18 @@ from .dimension import (
|
|
68
69
|
MASS_FRACTION_OF_I,
|
69
70
|
MASS_TRANSFER_COEFFICIENT,
|
70
71
|
MOLALITY_OF_SOLUTE_I,
|
71
|
-
MOLARITY_OF_I,
|
72
72
|
MOLAR_CONCENTRATION_BY_MASS,
|
73
73
|
MOLAR_FLOW_RATE,
|
74
74
|
MOLAR_FLUX,
|
75
75
|
MOLAR_HEAT_CAPACITY,
|
76
|
+
MOLARITY_OF_I,
|
76
77
|
MOLE_FRACTION_OF_I,
|
78
|
+
MOMENT_OF_INERTIA,
|
77
79
|
MOMENTUM_FLOW_RATE,
|
78
80
|
MOMENTUM_FLUX,
|
79
|
-
MOMENT_OF_INERTIA,
|
80
81
|
NORMALITY_OF_SOLUTION,
|
81
82
|
PARTICLE_DENSITY,
|
83
|
+
PERCENT,
|
82
84
|
PERMEABILITY,
|
83
85
|
PHOTON_EMISSION_RATE,
|
84
86
|
POWER_PER_UNIT_MASS,
|
@@ -109,16 +111,14 @@ from .dimension import (
|
|
109
111
|
VISCOSITY_DYNAMIC,
|
110
112
|
VISCOSITY_KINEMATIC,
|
111
113
|
VOLUME,
|
114
|
+
VOLUME_FRACTION_OF_I,
|
112
115
|
VOLUMETRIC_CALORIFIC_HEATING_VALUE,
|
113
116
|
VOLUMETRIC_COEFFICIENT_OF_EXPANSION,
|
114
117
|
VOLUMETRIC_FLOW_RATE,
|
115
118
|
VOLUMETRIC_FLUX,
|
116
119
|
VOLUMETRIC_MASS_FLOW_RATE,
|
117
|
-
|
118
|
-
WAVENUMBER
|
120
|
+
WAVENUMBER,
|
119
121
|
)
|
120
|
-
from .unit import UnitConstant, UnitDefinition
|
121
|
-
from .units import DimensionlessUnits
|
122
122
|
from .variable import FastQuantity, TypeSafeSetter
|
123
123
|
from .variable_types.typed_variable import TypedVariable
|
124
124
|
|
@@ -299,6 +299,18 @@ VARIABLE_DEFINITIONS = {
|
|
299
299
|
"field_name": "concentration",
|
300
300
|
"display_name": "Concentration",
|
301
301
|
},
|
302
|
+
"Dimensionless": {
|
303
|
+
"dimension": DIMENSIONLESS,
|
304
|
+
"default_unit": "dimensionless",
|
305
|
+
"units": [
|
306
|
+
("dimensionless", "dimensionless", 1.0, "", []),
|
307
|
+
("ratio", "ratio", 1.0, "ratio", []),
|
308
|
+
("parts per million", "parts_per_million", 1e-06, "ppm", ['ppm']),
|
309
|
+
("parts per billion", "parts_per_billion", 1e-09, "ppb", ['ppb'])
|
310
|
+
],
|
311
|
+
"field_name": "dimensionless",
|
312
|
+
"display_name": "Dimensionless",
|
313
|
+
},
|
302
314
|
"DynamicFluidity": {
|
303
315
|
"dimension": DYNAMIC_FLUIDITY,
|
304
316
|
"default_unit": "meter_seconds_per_kilogram",
|
@@ -1210,6 +1222,17 @@ VARIABLE_DEFINITIONS = {
|
|
1210
1222
|
"field_name": "particle_density",
|
1211
1223
|
"display_name": "Particle Density",
|
1212
1224
|
},
|
1225
|
+
"Percent": {
|
1226
|
+
"dimension": PERCENT,
|
1227
|
+
"default_unit": "percent",
|
1228
|
+
"units": [
|
1229
|
+
("percent", "percent", 0.01, "%", ['%']),
|
1230
|
+
("per mille", "per_mille", 0.001, "‰", ['‰']),
|
1231
|
+
("basis point", "basis_point", 0.0001, "bp", ['bp', 'bps'])
|
1232
|
+
],
|
1233
|
+
"field_name": "percent",
|
1234
|
+
"display_name": "Percent",
|
1235
|
+
},
|
1213
1236
|
"Permeability": {
|
1214
1237
|
"dimension": PERMEABILITY,
|
1215
1238
|
"default_unit": "square_meters",
|
@@ -1329,7 +1352,7 @@ VARIABLE_DEFINITIONS = {
|
|
1329
1352
|
("pascal", "pascal", 1.0, "Pa", ['Pa']),
|
1330
1353
|
("pièze", "pièze", 1000.0, "pz", ['pz']),
|
1331
1354
|
("pound force per square foot", "pound_force_per_square_foot", 47.880259, "PSF or psf or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{ft}^{2}$", ['psf']),
|
1332
|
-
("pound force per square inch", "pound_force_per_square_inch", 6894.
|
1355
|
+
("pound force per square inch", "pound_force_per_square_inch", 6894.757, "psi", ['psi']),
|
1333
1356
|
("torr", "torr", 133.322, "torr or mm Hg ( $0{ }^{\\circ}$ C)", ['mm Hg ( 0{ ^{circ C)']),
|
1334
1357
|
("kilopascal", "kilopascal", 1000.0, "kPa", ['kPa'])
|
1335
1358
|
],
|
@@ -1485,7 +1508,7 @@ VARIABLE_DEFINITIONS = {
|
|
1485
1508
|
("ounce force per square inch", "ounce_force_per_square_inch", 430.922, "OSI or osi or $\\mathrm{oz}_{\\mathrm{f}} / \\mathrm{in}^{2}$", ['OSI', 'osi', 'oz_{f / in^{2']),
|
1486
1509
|
("pascal", "pascal", 1.0, "Pa", ['Pa']),
|
1487
1510
|
("pound force per square foot", "pound_force_per_square_foot", 47.880259, "PSF or psf or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{ft}^{2}$", ['PSF', 'psf', 'lb_{f / ft^{2']),
|
1488
|
-
("pound force per square inch", "pound_force_per_square_inch", 6894.
|
1511
|
+
("pound force per square inch", "pound_force_per_square_inch", 6894.757, "psi", ['psi'])
|
1489
1512
|
],
|
1490
1513
|
"field_name": "stress",
|
1491
1514
|
"display_name": "Stress",
|
@@ -1830,38 +1853,6 @@ VARIABLE_DEFINITIONS = {
|
|
1830
1853
|
}
|
1831
1854
|
}
|
1832
1855
|
|
1833
|
-
# Special Dimensionless variable - handcrafted for proper behavior
|
1834
|
-
class DimensionlessSetter(TypeSafeSetter):
|
1835
|
-
"""Dimensionless-specific setter with only dimensionless units."""
|
1836
|
-
|
1837
|
-
def __init__(self, variable: 'Dimensionless', value: float):
|
1838
|
-
super().__init__(variable, value)
|
1839
|
-
|
1840
|
-
# Dimensionless units
|
1841
|
-
@property
|
1842
|
-
def dimensionless(self) -> 'Dimensionless':
|
1843
|
-
self.variable.quantity = FastQuantity(self.value, DimensionlessUnits.dimensionless)
|
1844
|
-
return cast('Dimensionless', self.variable)
|
1845
|
-
|
1846
|
-
# Common alias for no units
|
1847
|
-
@property
|
1848
|
-
def unitless(self) -> 'Dimensionless':
|
1849
|
-
self.variable.quantity = FastQuantity(self.value, DimensionlessUnits.dimensionless)
|
1850
|
-
return cast('Dimensionless', self.variable)
|
1851
|
-
|
1852
|
-
|
1853
|
-
class Dimensionless(TypedVariable):
|
1854
|
-
"""Type-safe dimensionless variable with expression capabilities."""
|
1855
|
-
|
1856
|
-
_setter_class = DimensionlessSetter
|
1857
|
-
_expected_dimension = DIMENSIONLESS
|
1858
|
-
_default_unit_property = "dimensionless"
|
1859
|
-
|
1860
|
-
def set(self, value: float) -> DimensionlessSetter:
|
1861
|
-
"""Create a dimensionless setter for this variable with proper type annotation."""
|
1862
|
-
return DimensionlessSetter(self, value)
|
1863
|
-
|
1864
|
-
|
1865
1856
|
|
1866
1857
|
def convert_unit_name_to_property(unit_name: str) -> str:
|
1867
1858
|
"""Convert unit name to property name without automatic pluralization."""
|
@@ -1890,25 +1881,24 @@ def create_setter_class(class_name: str, variable_name: str, definition: dict[st
|
|
1890
1881
|
}
|
1891
1882
|
)
|
1892
1883
|
|
1884
|
+
# Store reference to the unit class at class level
|
1885
|
+
# Remove "Setter" from class_name to get the base variable name
|
1886
|
+
base_class_name = class_name.replace("Setter", "")
|
1887
|
+
units_class = getattr(units, f"{base_class_name}Units")
|
1888
|
+
|
1893
1889
|
# Add properties for each unit using unit data directly
|
1894
1890
|
for unit_name, property_name, si_factor, symbol, aliases in definition["units"]:
|
1895
1891
|
# Create a unit definition from the consolidated data
|
1896
|
-
def make_property(
|
1892
|
+
def make_property(prop_nm, units_cls):
|
1897
1893
|
def getter(self):
|
1898
|
-
#
|
1899
|
-
|
1900
|
-
name=unit_nm,
|
1901
|
-
symbol=sym,
|
1902
|
-
dimension=definition["dimension"],
|
1903
|
-
si_factor=si_fac
|
1904
|
-
)
|
1905
|
-
unit_const = UnitConstant(unit_def)
|
1894
|
+
# Use existing unit constant from units module
|
1895
|
+
unit_const = getattr(units_cls, prop_nm)
|
1906
1896
|
self.variable.quantity = FastQuantity(self.value, unit_const)
|
1907
1897
|
return self.variable # type: ignore
|
1908
1898
|
return property(getter)
|
1909
1899
|
|
1910
1900
|
# Add the primary property to the class
|
1911
|
-
setattr(setter_class, property_name, make_property(
|
1901
|
+
setattr(setter_class, property_name, make_property(property_name, units_class))
|
1912
1902
|
|
1913
1903
|
# Add alias properties
|
1914
1904
|
for alias in aliases:
|
@@ -1916,7 +1906,7 @@ def create_setter_class(class_name: str, variable_name: str, definition: dict[st
|
|
1916
1906
|
alias_property = convert_unit_name_to_property(alias)
|
1917
1907
|
# Only add if it's different from the main property and doesn't already exist
|
1918
1908
|
if alias_property != property_name and not hasattr(setter_class, alias_property):
|
1919
|
-
setattr(setter_class, alias_property, make_property(
|
1909
|
+
setattr(setter_class, alias_property, make_property(property_name, units_class))
|
1920
1910
|
|
1921
1911
|
return setter_class
|
1922
1912
|
|
@@ -1944,8 +1934,7 @@ def create_variable_class(class_name: str, definition: dict[str, Any], setter_cl
|
|
1944
1934
|
|
1945
1935
|
|
1946
1936
|
# Create all variable and setter classes dynamically
|
1947
|
-
for var_name, var_def in VARIABLE_DEFINITIONS.items():
|
1948
|
-
# Create setter class
|
1937
|
+
for var_name, var_def in VARIABLE_DEFINITIONS.items(): # Create setter class
|
1949
1938
|
setter_name = f"{var_name}Setter"
|
1950
1939
|
setter_class = create_setter_class(setter_name, var_name, var_def)
|
1951
1940
|
|
@@ -1957,7 +1946,6 @@ for var_name, var_def in VARIABLE_DEFINITIONS.items():
|
|
1957
1946
|
globals()[var_name] = variable_class
|
1958
1947
|
|
1959
1948
|
# Individual exports for easier import
|
1960
|
-
# Special Dimensionless class is already defined above
|
1961
1949
|
|
1962
1950
|
AbsorbedDoseSetter = globals()['AbsorbedDoseSetter']
|
1963
1951
|
AbsorbedDose = globals()['AbsorbedDose']
|
@@ -1983,6 +1971,8 @@ AtomicWeightSetter = globals()['AtomicWeightSetter']
|
|
1983
1971
|
AtomicWeight = globals()['AtomicWeight']
|
1984
1972
|
ConcentrationSetter = globals()['ConcentrationSetter']
|
1985
1973
|
Concentration = globals()['Concentration']
|
1974
|
+
DimensionlessSetter = globals()['DimensionlessSetter']
|
1975
|
+
Dimensionless = globals()['Dimensionless']
|
1986
1976
|
DynamicFluiditySetter = globals()['DynamicFluiditySetter']
|
1987
1977
|
DynamicFluidity = globals()['DynamicFluidity']
|
1988
1978
|
ElectricCapacitanceSetter = globals()['ElectricCapacitanceSetter']
|
@@ -2095,6 +2085,8 @@ NormalityOfSolutionSetter = globals()['NormalityOfSolutionSetter']
|
|
2095
2085
|
NormalityOfSolution = globals()['NormalityOfSolution']
|
2096
2086
|
ParticleDensitySetter = globals()['ParticleDensitySetter']
|
2097
2087
|
ParticleDensity = globals()['ParticleDensity']
|
2088
|
+
PercentSetter = globals()['PercentSetter']
|
2089
|
+
Percent = globals()['Percent']
|
2098
2090
|
PermeabilitySetter = globals()['PermeabilitySetter']
|
2099
2091
|
Permeability = globals()['Permeability']
|
2100
2092
|
PhotonEmissionRateSetter = globals()['PhotonEmissionRateSetter']
|
@@ -2206,6 +2198,7 @@ def get_consolidated_variable_modules():
|
|
2206
2198
|
ConsolidatedVariableModule("AreaPerUnitVolume"),
|
2207
2199
|
ConsolidatedVariableModule("AtomicWeight"),
|
2208
2200
|
ConsolidatedVariableModule("Concentration"),
|
2201
|
+
ConsolidatedVariableModule("Dimensionless"),
|
2209
2202
|
ConsolidatedVariableModule("DynamicFluidity"),
|
2210
2203
|
ConsolidatedVariableModule("ElectricCapacitance"),
|
2211
2204
|
ConsolidatedVariableModule("ElectricCharge"),
|
@@ -2262,6 +2255,7 @@ def get_consolidated_variable_modules():
|
|
2262
2255
|
ConsolidatedVariableModule("MomentumFlux"),
|
2263
2256
|
ConsolidatedVariableModule("NormalityOfSolution"),
|
2264
2257
|
ConsolidatedVariableModule("ParticleDensity"),
|
2258
|
+
ConsolidatedVariableModule("Percent"),
|
2265
2259
|
ConsolidatedVariableModule("Permeability"),
|
2266
2260
|
ConsolidatedVariableModule("PhotonEmissionRate"),
|
2267
2261
|
ConsolidatedVariableModule("PowerPerUnitMass"),
|
qnty/variables.pyi
CHANGED
@@ -3,52 +3,17 @@ Type stubs for consolidated variables module - Complete Edition.
|
|
3
3
|
|
4
4
|
Provides complete type hints for IDE autocomplete and type checking
|
5
5
|
for the fluent API with dimension-specific unit properties for all
|
6
|
-
|
6
|
+
107 variable types with 871 total units.
|
7
7
|
|
8
8
|
Auto-generated from the same source of truth as consolidated_new.py.
|
9
9
|
"""
|
10
10
|
|
11
11
|
from typing import Any
|
12
|
+
|
12
13
|
from .dimension import DimensionSignature
|
13
14
|
from .variable import TypeSafeSetter
|
14
15
|
from .variable_types.typed_variable import TypedVariable
|
15
16
|
|
16
|
-
# ============================================================================
|
17
|
-
# SPECIAL DIMENSIONLESS VARIABLE
|
18
|
-
# ============================================================================
|
19
|
-
|
20
|
-
class DimensionlessSetter(TypeSafeSetter):
|
21
|
-
"""Dimensionless-specific setter with only dimensionless units."""
|
22
|
-
|
23
|
-
def __init__(self, variable: Dimensionless, value: float) -> None: ...
|
24
|
-
|
25
|
-
@property
|
26
|
-
def dimensionless(self) -> Dimensionless: ...
|
27
|
-
|
28
|
-
@property
|
29
|
-
def unitless(self) -> Dimensionless: ...
|
30
|
-
|
31
|
-
|
32
|
-
class Dimensionless(TypedVariable):
|
33
|
-
"""Type-safe dimensionless variable with expression capabilities."""
|
34
|
-
|
35
|
-
_setter_class: type[TypeSafeSetter] | None
|
36
|
-
_expected_dimension: DimensionSignature | None
|
37
|
-
_default_unit_property: str | None
|
38
|
-
|
39
|
-
def __init__(self, *args, is_known: bool = True) -> None: ...
|
40
|
-
|
41
|
-
def set(self, value: float) -> DimensionlessSetter:
|
42
|
-
"""
|
43
|
-
Create a dimensionless setter for fluent unit assignment.
|
44
|
-
|
45
|
-
Example:
|
46
|
-
dimensionless.set(1.0).dimensionless
|
47
|
-
dimensionless.set(2.5).unitless
|
48
|
-
"""
|
49
|
-
...
|
50
|
-
|
51
|
-
|
52
17
|
# ============================================================================
|
53
18
|
# ABSORBED RADIATION DOSE
|
54
19
|
# ============================================================================
|
@@ -677,6 +642,49 @@ class Concentration(TypedVariable):
|
|
677
642
|
"""
|
678
643
|
...
|
679
644
|
|
645
|
+
# ============================================================================
|
646
|
+
# DIMENSIONLESS
|
647
|
+
# ============================================================================
|
648
|
+
|
649
|
+
class DimensionlessSetter(TypeSafeSetter):
|
650
|
+
"""Dimensionless-specific setter with only dimensionless unit properties."""
|
651
|
+
|
652
|
+
def __init__(self, variable: Dimensionless, value: float) -> None: ...
|
653
|
+
|
654
|
+
# All dimensionless unit properties - provides fluent API with full type hints
|
655
|
+
@property
|
656
|
+
def dimensionless(self) -> Dimensionless: ...
|
657
|
+
@property
|
658
|
+
def ratio(self) -> Dimensionless: ...
|
659
|
+
@property
|
660
|
+
def parts_per_million(self) -> Dimensionless: ...
|
661
|
+
@property
|
662
|
+
def ppm(self) -> Dimensionless: ...
|
663
|
+
@property
|
664
|
+
def parts_per_billion(self) -> Dimensionless: ...
|
665
|
+
@property
|
666
|
+
def ppb(self) -> Dimensionless: ...
|
667
|
+
|
668
|
+
class Dimensionless(TypedVariable):
|
669
|
+
"""Type-safe dimensionless variable with expression capabilities."""
|
670
|
+
|
671
|
+
_setter_class: type[TypeSafeSetter] | None
|
672
|
+
_expected_dimension: DimensionSignature | None
|
673
|
+
_default_unit_property: str | None
|
674
|
+
|
675
|
+
def __init__(self, *args, is_known: bool = True) -> None: ...
|
676
|
+
|
677
|
+
def set(self, value: float) -> DimensionlessSetter:
|
678
|
+
"""
|
679
|
+
Create a dimensionless setter for fluent unit assignment.
|
680
|
+
|
681
|
+
Example:
|
682
|
+
dimensionless.set(100).dimensionless
|
683
|
+
dimensionless.set(100).ratio
|
684
|
+
dimensionless.set(100).parts_per_million
|
685
|
+
"""
|
686
|
+
...
|
687
|
+
|
680
688
|
# ============================================================================
|
681
689
|
# DYNAMIC FLUIDITY
|
682
690
|
# ============================================================================
|
@@ -3782,6 +3790,49 @@ class ParticleDensity(TypedVariable):
|
|
3782
3790
|
"""
|
3783
3791
|
...
|
3784
3792
|
|
3793
|
+
# ============================================================================
|
3794
|
+
# PERCENT
|
3795
|
+
# ============================================================================
|
3796
|
+
|
3797
|
+
class PercentSetter(TypeSafeSetter):
|
3798
|
+
"""Percent-specific setter with only percent unit properties."""
|
3799
|
+
|
3800
|
+
def __init__(self, variable: Percent, value: float) -> None: ...
|
3801
|
+
|
3802
|
+
# All percent unit properties - provides fluent API with full type hints
|
3803
|
+
@property
|
3804
|
+
def percent(self) -> Percent: ...
|
3805
|
+
@property
|
3806
|
+
def unit(self) -> Percent: ...
|
3807
|
+
@property
|
3808
|
+
def per_mille(self) -> Percent: ...
|
3809
|
+
@property
|
3810
|
+
def basis_point(self) -> Percent: ...
|
3811
|
+
@property
|
3812
|
+
def bp(self) -> Percent: ...
|
3813
|
+
@property
|
3814
|
+
def bps(self) -> Percent: ...
|
3815
|
+
|
3816
|
+
class Percent(TypedVariable):
|
3817
|
+
"""Type-safe percent variable with expression capabilities."""
|
3818
|
+
|
3819
|
+
_setter_class: type[TypeSafeSetter] | None
|
3820
|
+
_expected_dimension: DimensionSignature | None
|
3821
|
+
_default_unit_property: str | None
|
3822
|
+
|
3823
|
+
def __init__(self, *args, is_known: bool = True) -> None: ...
|
3824
|
+
|
3825
|
+
def set(self, value: float) -> PercentSetter:
|
3826
|
+
"""
|
3827
|
+
Create a percent setter for fluent unit assignment.
|
3828
|
+
|
3829
|
+
Example:
|
3830
|
+
percent.set(100).percent
|
3831
|
+
percent.set(100).per_mille
|
3832
|
+
percent.set(100).basis_point
|
3833
|
+
"""
|
3834
|
+
...
|
3835
|
+
|
3785
3836
|
# ============================================================================
|
3786
3837
|
# PERMEABILITY
|
3787
3838
|
# ============================================================================
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: qnty
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.6
|
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
|
@@ -0,0 +1,19 @@
|
|
1
|
+
qnty/__init__.py,sha256=o9rtuo8QAdC3ppaD7ev1jqjrWbCvpdFbi_yZwUBuc6Q,5551
|
2
|
+
qnty/dimension.py,sha256=uEm3p03DavihvXH7wnNkTCORZo9wPJihwEqvp4fj2lc,11110
|
3
|
+
qnty/equation.py,sha256=XgzM91jn__GomKbgslncoFJWPDJiMb4_VKD-8sdcFXc,9096
|
4
|
+
qnty/expression.py,sha256=WV0OfZYPZlNf_sBEgOrnG88KKzj-xvTn45YGMoZR1xc,21435
|
5
|
+
qnty/prefixes.py,sha256=bRwcNbE_nX9gXI_WnJZLL-i7kQncIOZivCCSN8dur2g,6000
|
6
|
+
qnty/unit.py,sha256=dTKaQ9gsO1rom0JJJ3MYjVaSCuCEU9018DWClTBv7w4,5569
|
7
|
+
qnty/unit_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
qnty/unit_types/base.py,sha256=Gk2Ab4TE-SfGBn-ko_p0vDI178PG9SyZOJ0cBOYEpdg,1620
|
9
|
+
qnty/units.py,sha256=l1o0B4O19XXVH7hkacZqO1WwqqywYJLXYbGHKTOmXcc,302860
|
10
|
+
qnty/variable.py,sha256=mqWWOqZX67jD1lLSzM-kSpKDP65l2GKcYRTJ5mwpMUA,11389
|
11
|
+
qnty/variable_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
qnty/variable_types/base.py,sha256=Otm7FOvuvi1gdlZm-djlTwqhlsSdzPIL46AXnB2cd18,1735
|
13
|
+
qnty/variable_types/expression_variable.py,sha256=sRQ4VUMLBEh6tPqOwh4gLtGQH9Y3XeeHGdLKo0E6mWc,2997
|
14
|
+
qnty/variable_types/typed_variable.py,sha256=kjw8VOShMaE7iorc3mqCIApYaFgOfvv-jSu_tmT1DgM,4227
|
15
|
+
qnty/variables.py,sha256=amlj4xGMC_nuo9sQtGRMG5SAcX4WKK-YWNyXs1yzNbQ,142668
|
16
|
+
qnty/variables.pyi,sha256=KYPDJ-Qh_LPP4vHsafzrV6ZAUeepErY-RwRpOTieuhk,219690
|
17
|
+
qnty-0.0.6.dist-info/METADATA,sha256=l_noQC2KY0pSXEypKP-WPVe_uPVwMRwa-ECnubcRS-E,12113
|
18
|
+
qnty-0.0.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
19
|
+
qnty-0.0.6.dist-info/RECORD,,
|
qnty-0.0.4.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
qnty/__init__.py,sha256=ath8f_aNoOH_wAXqd71DOZNHckcvjbydSwvlU06VDlY,5491
|
2
|
-
qnty/dimension.py,sha256=PiRpZydh7QifD7YdzGCoJZ9EI1fixDsX5KWZj92IXdo,10826
|
3
|
-
qnty/equation.py,sha256=XgzM91jn__GomKbgslncoFJWPDJiMb4_VKD-8sdcFXc,9096
|
4
|
-
qnty/expression.py,sha256=UM0hCqOVD1gK_O5-Rsq7BWIPDTEL_kFORprV6LIqGcU,20890
|
5
|
-
qnty/prefixes.py,sha256=bRwcNbE_nX9gXI_WnJZLL-i7kQncIOZivCCSN8dur2g,6000
|
6
|
-
qnty/unit.py,sha256=ZTL_H5FqDCnbNRjPbVrIvhvZkyR6qmMatuVwM5OGqmQ,7071
|
7
|
-
qnty/unit_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
-
qnty/unit_types/base.py,sha256=Gk2Ab4TE-SfGBn-ko_p0vDI178PG9SyZOJ0cBOYEpdg,1620
|
9
|
-
qnty/units.py,sha256=d7qzWnD5y25EhGSd3Et-jBwZqJ4E0lO-yLZENHOZytA,302734
|
10
|
-
qnty/variable.py,sha256=YP4zSvsR0JjEGPiMAFuFbj21SPFeSf1YZ0LhJc-t1qI,9850
|
11
|
-
qnty/variable_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
qnty/variable_types/base.py,sha256=Otm7FOvuvi1gdlZm-djlTwqhlsSdzPIL46AXnB2cd18,1735
|
13
|
-
qnty/variable_types/expression_variable.py,sha256=sRQ4VUMLBEh6tPqOwh4gLtGQH9Y3XeeHGdLKo0E6mWc,2997
|
14
|
-
qnty/variable_types/typed_variable.py,sha256=ibmbUh8JCXk2XLc0yvpjMYAtAlpxvia0vB_3xaBqzkc,4345
|
15
|
-
qnty/variables.py,sha256=fc7CPLAgqxI7zjeB22PXrTTA6fnl_1gnaX74MYhXMFM,142963
|
16
|
-
qnty/variables.pyi,sha256=Sm99jqyVQmuxdPhctmxuSvYrMO9vz4XqhMzBDsr2uC8,217982
|
17
|
-
qnty-0.0.4.dist-info/METADATA,sha256=qOxMHMRcnziR5bPNi5QIOZUGShY-0CITI0zsSIqbsvY,12113
|
18
|
-
qnty-0.0.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
19
|
-
qnty-0.0.4.dist-info/RECORD,,
|
File without changes
|