qnty 0.0.4__tar.gz → 0.0.5__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.4
3
+ Version: 0.0.5
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.4"
3
+ version = "0.0.5"
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" }
@@ -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", "Volume", "Area",
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 (95 additional 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",
@@ -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.create() # Dimensionless
82
- ANGLE_SOLID = DimensionSignature.create() # Dimensionless
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.create() # Dimensionless
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.create() # Dimensionless
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.create() # Dimensionless
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.create() # Dimensionless
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.create() # Dimensionless
185
+ VOLUME_FRACTION_OF_I = DimensionSignature(BaseDimension.DIMENSIONLESS) # Dimensionless
184
186
  WAVENUMBER = DimensionSignature.create(length=-1) # L^-1
@@ -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 DIMENSIONLESS, LENGTH, PRESSURE, DimensionSignature
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
- # Keep legacy initialization for backward compatibility
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."""
@@ -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 864 units
7
- across 105 fields organized into 105 dimensional groups.
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": "PSI or psi or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{in}^{2}$",
5621
- "si_factor": 6894.8,
5692
+ "symbol": "psi",
5693
+ "si_factor": 6894.757,
5622
5694
  "full_name": "pound force per square inch",
5623
- "notation": "PSI or psi or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{in}^{2}$",
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": "PSI or psi or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{in}^{2}$",
6189
- "si_factor": 6894.8,
6260
+ "symbol": "psi",
6261
+ "si_factor": 6894.757,
6190
6262
  "full_name": "pound force per square inch",
6191
- "notation": "PSI or psi or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{in}^{2}$",
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
@@ -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 temporary unit
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=f"combined_{result_dimension_sig}",
149
- symbol="combined",
150
- dimension=DimensionSignature(result_dimension_sig),
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 self._default_unit_property and hasattr(setter, self._default_unit_property):
77
- # Fall back to default unit
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
- # Last resort - try to find any valid unit property
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
- if unit_properties:
85
- getattr(setter, unit_properties[0])
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
@@ -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, cast
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
- VOLUME_FRACTION_OF_I,
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.8, "PSI or psi or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{in}^{2}$", ['psi']),
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.8, "PSI or psi or $\\mathrm{lb}_{\\mathrm{f}} / \\mathrm{in}^{2}$", ['psi'])
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(unit_nm, si_fac, sym):
1892
+ def make_property(prop_nm, units_cls):
1897
1893
  def getter(self):
1898
- # Create unit definition and constant from consolidated unit data
1899
- unit_def = UnitDefinition(
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(unit_name, si_factor, symbol))
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(unit_name, si_factor, symbol))
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"),
@@ -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
- 105 variable types with 864 total units.
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
  # ============================================================================
File without changes
File without changes
File without changes
File without changes
File without changes