qnty 0.0.1__py3-none-any.whl → 0.0.2__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/expression.py ADDED
@@ -0,0 +1,480 @@
1
+ """
2
+ Expression System
3
+ =================
4
+
5
+ Mathematical expressions for building equation trees with qnty variables.
6
+ """
7
+
8
+ import math
9
+ from abc import ABC, abstractmethod
10
+ from typing import Union, cast
11
+
12
+ from .units import DimensionlessUnits
13
+
14
+ # if TYPE_CHECKING:
15
+ from .variable import FastQuantity, TypeSafeVariable
16
+
17
+
18
+ def wrap_operand(operand: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
19
+ """
20
+ Wrap non-Expression operands in appropriate Expression subclasses.
21
+
22
+ This function handles type conversion without circular imports by using
23
+ duck typing and delayed imports where necessary.
24
+ """
25
+ # Type guard for Expression types
26
+ if hasattr(operand, 'evaluate') and hasattr(operand, 'get_variables'):
27
+ # Already an Expression
28
+ return cast('Expression', operand)
29
+ elif hasattr(operand, 'name') and hasattr(operand, 'quantity') and hasattr(operand, 'is_known'):
30
+ # TypeSafeVariable-like object
31
+ return VariableReference(cast('TypeSafeVariable', operand))
32
+ elif hasattr(operand, 'value') and hasattr(operand, 'unit') and hasattr(operand, '_dimension_sig'):
33
+ # FastQuantity-like object
34
+ return Constant(cast('FastQuantity', operand))
35
+ elif isinstance(operand, int | float):
36
+ # Numeric value - create dimensionless quantity
37
+
38
+ return Constant(FastQuantity(float(operand), DimensionlessUnits.dimensionless))
39
+ else:
40
+ raise TypeError(f"Cannot convert {type(operand)} to Expression")
41
+
42
+
43
+ class Expression(ABC):
44
+ """Abstract base class for mathematical expressions."""
45
+
46
+ @abstractmethod
47
+ def evaluate(self, variable_values: dict[str, 'TypeSafeVariable']) -> 'FastQuantity':
48
+ """Evaluate the expression given variable values."""
49
+ pass
50
+
51
+ @abstractmethod
52
+ def get_variables(self) -> set[str]:
53
+ """Get all variable symbols used in this expression."""
54
+ pass
55
+
56
+ @abstractmethod
57
+ def simplify(self) -> 'Expression':
58
+ """Simplify the expression."""
59
+ pass
60
+
61
+ @abstractmethod
62
+ def __str__(self) -> str:
63
+ pass
64
+
65
+ def __add__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
66
+ return BinaryOperation('+', self, wrap_operand(other))
67
+
68
+ def __radd__(self, other: Union['TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
69
+ return BinaryOperation('+', wrap_operand(other), self)
70
+
71
+ def __sub__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
72
+ return BinaryOperation('-', self, wrap_operand(other))
73
+
74
+ def __rsub__(self, other: Union['TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
75
+ return BinaryOperation('-', wrap_operand(other), self)
76
+
77
+ def __mul__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
78
+ return BinaryOperation('*', self, wrap_operand(other))
79
+
80
+ def __rmul__(self, other: Union['TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
81
+ return BinaryOperation('*', wrap_operand(other), self)
82
+
83
+ def __truediv__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
84
+ return BinaryOperation('/', self, wrap_operand(other))
85
+
86
+ def __rtruediv__(self, other: Union['TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
87
+ return BinaryOperation('/', wrap_operand(other), self)
88
+
89
+ def __pow__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
90
+ return BinaryOperation('**', self, wrap_operand(other))
91
+
92
+ def __rpow__(self, other: Union['TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
93
+ return BinaryOperation('**', wrap_operand(other), self)
94
+
95
+ # Comparison operators for conditional expressions
96
+ def __lt__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'ComparisonExpression':
97
+ return ComparisonExpression('<', self, self._wrap_operand(other))
98
+
99
+ def __le__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'ComparisonExpression':
100
+ return ComparisonExpression('<=', self, self._wrap_operand(other))
101
+
102
+ def __gt__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'ComparisonExpression':
103
+ return ComparisonExpression('>', self, self._wrap_operand(other))
104
+
105
+ def __ge__(self, other: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'ComparisonExpression':
106
+ return ComparisonExpression('>=', self, self._wrap_operand(other))
107
+
108
+ @staticmethod
109
+ def _wrap_operand(operand: Union['Expression', 'TypeSafeVariable', 'FastQuantity', int, float]) -> 'Expression':
110
+ """Wrap non-Expression operands in appropriate Expression subclasses."""
111
+ return wrap_operand(operand)
112
+
113
+
114
+ class VariableReference(Expression):
115
+ """Reference to a variable in an expression with performance optimizations."""
116
+
117
+ def __init__(self, variable: 'TypeSafeVariable'):
118
+ self.variable = variable
119
+ # Cache the name resolution to avoid repeated lookups
120
+ self._cached_name = None
121
+ self._last_symbol = None
122
+
123
+ @property
124
+ def name(self) -> str:
125
+ """Get variable name with caching for performance."""
126
+ current_symbol = self.variable.symbol
127
+ if self._cached_name is None or self._last_symbol != current_symbol:
128
+ # Use symbol for optinova compatibility, fall back to name if symbol not set
129
+ self._cached_name = current_symbol if current_symbol else self.variable.name
130
+ self._last_symbol = current_symbol
131
+ return self._cached_name
132
+
133
+ def evaluate(self, variable_values: dict[str, 'TypeSafeVariable']) -> 'FastQuantity':
134
+ try:
135
+ if self.name in variable_values:
136
+ var = variable_values[self.name]
137
+ if var.quantity is not None:
138
+ return var.quantity
139
+ elif self.variable.quantity is not None:
140
+ return self.variable.quantity
141
+
142
+ # If we reach here, no valid quantity was found
143
+ available_vars = list(variable_values.keys()) if variable_values else []
144
+ raise ValueError(
145
+ f"Cannot evaluate variable '{self.name}' without value. "
146
+ f"Available variables: {available_vars}"
147
+ )
148
+ except Exception as e:
149
+ if isinstance(e, ValueError):
150
+ raise
151
+ raise ValueError(f"Error evaluating variable '{self.name}': {e}") from e
152
+
153
+ def get_variables(self) -> set[str]:
154
+ return {self.name}
155
+
156
+ def simplify(self) -> 'Expression':
157
+ return self
158
+
159
+ def __str__(self) -> str:
160
+ return self.name
161
+
162
+
163
+ class Constant(Expression):
164
+ """Constant value in an expression."""
165
+
166
+ def __init__(self, value: 'FastQuantity'):
167
+ self.value = value
168
+
169
+ def evaluate(self, variable_values: dict[str, 'TypeSafeVariable']) -> 'FastQuantity':
170
+ return self.value
171
+
172
+ def get_variables(self) -> set[str]:
173
+ return set()
174
+
175
+ def simplify(self) -> 'Expression':
176
+ return self
177
+
178
+ def __str__(self) -> str:
179
+ return str(self.value.value)
180
+
181
+
182
+ class BinaryOperation(Expression):
183
+ """Binary operation between two expressions."""
184
+
185
+ def __init__(self, operator: str, left: Expression, right: Expression):
186
+ self.operator = operator
187
+ self.left = left
188
+ self.right = right
189
+
190
+ def evaluate(self, variable_values: dict[str, 'TypeSafeVariable']) -> 'FastQuantity':
191
+ try:
192
+ left_val = self.left.evaluate(variable_values)
193
+ right_val = self.right.evaluate(variable_values)
194
+
195
+ if self.operator == '+':
196
+ return left_val + right_val
197
+ elif self.operator == '-':
198
+ return left_val - right_val
199
+ elif self.operator == '*':
200
+ return left_val * right_val
201
+ elif self.operator == '/':
202
+ # Check for division by zero
203
+ if abs(right_val.value) < 1e-15:
204
+ raise ValueError(f"Division by zero in expression: {self}")
205
+ return left_val / right_val
206
+ elif self.operator == '**':
207
+ # For power, right side should be dimensionless
208
+ if isinstance(right_val.value, int | float):
209
+ if right_val.value < 0 and left_val.value < 0:
210
+ raise ValueError(f"Negative base with negative exponent: {left_val.value}^{right_val.value}")
211
+ result_value = left_val.value ** right_val.value
212
+ # For power operations, we need to handle units carefully
213
+ # This is a simplified implementation
214
+ return FastQuantity(result_value, left_val.unit)
215
+ else:
216
+ raise ValueError("Exponent must be dimensionless number")
217
+ else:
218
+ raise ValueError(f"Unknown operator: {self.operator}")
219
+ except Exception as e:
220
+ if isinstance(e, ValueError):
221
+ raise
222
+ raise ValueError(f"Error evaluating binary operation '{self}': {e}") from e
223
+
224
+ def get_variables(self) -> set[str]:
225
+ return self.left.get_variables() | self.right.get_variables()
226
+
227
+ def simplify(self) -> Expression:
228
+ left_simplified = self.left.simplify()
229
+ right_simplified = self.right.simplify()
230
+
231
+ # Basic simplification rules
232
+ if isinstance(left_simplified, Constant) and isinstance(right_simplified, Constant):
233
+ # Evaluate constant expressions
234
+ dummy_vars = {}
235
+ try:
236
+ result = BinaryOperation(self.operator, left_simplified, right_simplified).evaluate(dummy_vars)
237
+ return Constant(result)
238
+ except (ValueError, TypeError, ArithmeticError):
239
+ pass
240
+
241
+ return BinaryOperation(self.operator, left_simplified, right_simplified)
242
+
243
+ def __str__(self) -> str:
244
+ # Handle operator precedence for cleaner string representation
245
+ precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '**': 3}
246
+ left_str = str(self.left)
247
+ right_str = str(self.right)
248
+
249
+ # Add parentheses only when needed based on precedence
250
+ if isinstance(self.left, BinaryOperation) and precedence.get(self.left.operator, 0) < precedence.get(self.operator, 0):
251
+ left_str = f"({left_str})"
252
+ if isinstance(self.right, BinaryOperation) and precedence.get(self.right.operator, 0) < precedence.get(self.operator, 0):
253
+ right_str = f"({right_str})"
254
+
255
+ return f"{left_str} {self.operator} {right_str}"
256
+
257
+
258
+ class ComparisonExpression(Expression):
259
+ """Comparison expression for conditional logic."""
260
+
261
+ def __init__(self, operator: str, left: Expression, right: Expression):
262
+ self.operator = operator
263
+ self.left = left
264
+ self.right = right
265
+
266
+ def evaluate(self, variable_values: dict[str, 'TypeSafeVariable']) -> 'FastQuantity':
267
+ """Evaluate comparison and return dimensionless result (1.0 for True, 0.0 for False)."""
268
+
269
+ left_val = self.left.evaluate(variable_values)
270
+ right_val = self.right.evaluate(variable_values)
271
+
272
+ # Convert to same units for comparison if possible
273
+ try:
274
+ if left_val._dimension_sig == right_val._dimension_sig and left_val.unit != right_val.unit:
275
+ right_val = right_val.to(left_val.unit)
276
+ except (ValueError, TypeError, AttributeError):
277
+ pass
278
+
279
+ if self.operator == '<':
280
+ result = left_val.value < right_val.value
281
+ elif self.operator == '<=':
282
+ result = left_val.value <= right_val.value
283
+ elif self.operator == '>':
284
+ result = left_val.value > right_val.value
285
+ elif self.operator == '>=':
286
+ result = left_val.value >= right_val.value
287
+ elif self.operator == '==':
288
+ result = abs(left_val.value - right_val.value) < 1e-10
289
+ elif self.operator == '!=':
290
+ result = abs(left_val.value - right_val.value) >= 1e-10
291
+ else:
292
+ raise ValueError(f"Unknown comparison operator: {self.operator}")
293
+
294
+ return FastQuantity(1.0 if result else 0.0, DimensionlessUnits.dimensionless)
295
+
296
+ def get_variables(self) -> set[str]:
297
+ return self.left.get_variables() | self.right.get_variables()
298
+
299
+ def simplify(self) -> Expression:
300
+ return ComparisonExpression(self.operator, self.left.simplify(), self.right.simplify())
301
+
302
+ def __str__(self) -> str:
303
+ return f"({self.left} {self.operator} {self.right})"
304
+
305
+
306
+ class UnaryFunction(Expression):
307
+ """Unary mathematical function expression."""
308
+
309
+ def __init__(self, function_name: str, operand: Expression):
310
+ self.function_name = function_name
311
+ self.operand = operand
312
+
313
+ def evaluate(self, variable_values: dict[str, 'TypeSafeVariable']) -> 'FastQuantity':
314
+
315
+ operand_val = self.operand.evaluate(variable_values)
316
+
317
+ if self.function_name == 'sin':
318
+ # Assume input is in radians, result is dimensionless
319
+ result_value = math.sin(operand_val.value)
320
+ return FastQuantity(result_value, DimensionlessUnits.dimensionless)
321
+ elif self.function_name == 'cos':
322
+ result_value = math.cos(operand_val.value)
323
+ return FastQuantity(result_value, DimensionlessUnits.dimensionless)
324
+ elif self.function_name == 'tan':
325
+ result_value = math.tan(operand_val.value)
326
+ return FastQuantity(result_value, DimensionlessUnits.dimensionless)
327
+ elif self.function_name == 'sqrt':
328
+ # For sqrt, we need to handle units carefully
329
+ result_value = math.sqrt(operand_val.value)
330
+ # This is simplified - proper unit handling would need dimensional analysis
331
+ return FastQuantity(result_value, operand_val.unit)
332
+ elif self.function_name == 'abs':
333
+ return FastQuantity(abs(operand_val.value), operand_val.unit)
334
+ elif self.function_name == 'ln':
335
+ # Natural log - input should be dimensionless
336
+ result_value = math.log(operand_val.value)
337
+ return FastQuantity(result_value, DimensionlessUnits.dimensionless)
338
+ elif self.function_name == 'log10':
339
+ result_value = math.log10(operand_val.value)
340
+ return FastQuantity(result_value, DimensionlessUnits.dimensionless)
341
+ elif self.function_name == 'exp':
342
+ # Exponential - input should be dimensionless
343
+ result_value = math.exp(operand_val.value)
344
+ return FastQuantity(result_value, DimensionlessUnits.dimensionless)
345
+ else:
346
+ raise ValueError(f"Unknown function: {self.function_name}")
347
+
348
+ def get_variables(self) -> set[str]:
349
+ return self.operand.get_variables()
350
+
351
+ def simplify(self) -> Expression:
352
+ simplified_operand = self.operand.simplify()
353
+ if isinstance(simplified_operand, Constant):
354
+ # Evaluate constant functions at compile time
355
+ try:
356
+ dummy_vars = {}
357
+ result = UnaryFunction(self.function_name, simplified_operand).evaluate(dummy_vars)
358
+ return Constant(result)
359
+ except (ValueError, TypeError, ArithmeticError):
360
+ pass
361
+ return UnaryFunction(self.function_name, simplified_operand)
362
+
363
+ def __str__(self) -> str:
364
+ return f"{self.function_name}({self.operand})"
365
+
366
+
367
+ class ConditionalExpression(Expression):
368
+ """Conditional expression: if condition then true_expr else false_expr."""
369
+
370
+ def __init__(self, condition: Expression, true_expr: Expression, false_expr: Expression):
371
+ self.condition = condition
372
+ self.true_expr = true_expr
373
+ self.false_expr = false_expr
374
+
375
+ def evaluate(self, variable_values: dict[str, 'TypeSafeVariable']) -> 'FastQuantity':
376
+ condition_val = self.condition.evaluate(variable_values)
377
+ # Consider non-zero as True
378
+ if abs(condition_val.value) > 1e-10:
379
+ return self.true_expr.evaluate(variable_values)
380
+ else:
381
+ return self.false_expr.evaluate(variable_values)
382
+
383
+ def get_variables(self) -> set[str]:
384
+ return (self.condition.get_variables() |
385
+ self.true_expr.get_variables() |
386
+ self.false_expr.get_variables())
387
+
388
+ def simplify(self) -> Expression:
389
+ simplified_condition = self.condition.simplify()
390
+ simplified_true = self.true_expr.simplify()
391
+ simplified_false = self.false_expr.simplify()
392
+
393
+ # If condition is constant, choose the appropriate branch
394
+ if isinstance(simplified_condition, Constant):
395
+ try:
396
+ dummy_vars = {}
397
+ condition_val = simplified_condition.evaluate(dummy_vars)
398
+ if abs(condition_val.value) > 1e-10:
399
+ return simplified_true
400
+ else:
401
+ return simplified_false
402
+ except (ValueError, TypeError, ArithmeticError):
403
+ pass
404
+
405
+ return ConditionalExpression(simplified_condition, simplified_true, simplified_false)
406
+
407
+ def __str__(self) -> str:
408
+ return f"if({self.condition}, {self.true_expr}, {self.false_expr})"
409
+
410
+
411
+ # Convenience functions for mathematical operations
412
+ def sin(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
413
+ """Sine function."""
414
+ return UnaryFunction('sin', Expression._wrap_operand(expr))
415
+
416
+ def cos(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
417
+ """Cosine function."""
418
+ return UnaryFunction('cos', Expression._wrap_operand(expr))
419
+
420
+ def tan(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
421
+ """Tangent function."""
422
+ return UnaryFunction('tan', Expression._wrap_operand(expr))
423
+
424
+ def sqrt(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
425
+ """Square root function."""
426
+ return UnaryFunction('sqrt', Expression._wrap_operand(expr))
427
+
428
+ def abs_expr(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
429
+ """Absolute value function."""
430
+ return UnaryFunction('abs', Expression._wrap_operand(expr))
431
+
432
+ def ln(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
433
+ """Natural logarithm function."""
434
+ return UnaryFunction('ln', Expression._wrap_operand(expr))
435
+
436
+ def log10(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
437
+ """Base-10 logarithm function."""
438
+ return UnaryFunction('log10', Expression._wrap_operand(expr))
439
+
440
+ def exp(expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> UnaryFunction:
441
+ """Exponential function."""
442
+ return UnaryFunction('exp', Expression._wrap_operand(expr))
443
+
444
+ def cond_expr(condition: Union[Expression, 'ComparisonExpression'],
445
+ true_expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float],
446
+ false_expr: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> ConditionalExpression:
447
+ """Conditional expression: if condition then true_expr else false_expr."""
448
+ return ConditionalExpression(
449
+ condition if isinstance(condition, Expression) else condition,
450
+ Expression._wrap_operand(true_expr),
451
+ Expression._wrap_operand(false_expr)
452
+ )
453
+
454
+ def min_expr(*expressions: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> Expression:
455
+ """Minimum of multiple expressions."""
456
+ if len(expressions) < 2:
457
+ raise ValueError("min_expr requires at least 2 arguments")
458
+
459
+ wrapped_expressions = [Expression._wrap_operand(expr) for expr in expressions]
460
+ result = wrapped_expressions[0]
461
+
462
+ for expr in wrapped_expressions[1:]:
463
+ # min(a, b) = if(a < b, a, b)
464
+ result = cond_expr(result < expr, result, expr)
465
+
466
+ return result
467
+
468
+ def max_expr(*expressions: Union[Expression, 'TypeSafeVariable', 'FastQuantity', int, float]) -> Expression:
469
+ """Maximum of multiple expressions."""
470
+ if len(expressions) < 2:
471
+ raise ValueError("max_expr requires at least 2 arguments")
472
+
473
+ wrapped_expressions = [Expression._wrap_operand(expr) for expr in expressions]
474
+ result = wrapped_expressions[0]
475
+
476
+ for expr in wrapped_expressions[1:]:
477
+ # max(a, b) = if(a > b, a, b)
478
+ result = cond_expr(result > expr, result, expr)
479
+
480
+ return result
qnty/unit.py CHANGED
@@ -5,9 +5,9 @@ Unit System
5
5
  Unit definitions, constants and registry for the high-performance unit system.
6
6
  """
7
7
 
8
- from typing import Dict, Tuple, List
9
8
  from dataclasses import dataclass
10
- from .dimension import DimensionSignature, LENGTH, PRESSURE, DIMENSIONLESS
9
+
10
+ from .dimension import DIMENSIONLESS, LENGTH, PRESSURE, DimensionSignature
11
11
 
12
12
 
13
13
  @dataclass(frozen=True)
@@ -46,11 +46,11 @@ class HighPerformanceRegistry:
46
46
  """Ultra-fast registry with pre-computed conversion tables."""
47
47
 
48
48
  def __init__(self):
49
- self.units: Dict[str, UnitDefinition] = {}
50
- self.conversion_table: Dict[Tuple[str, str], float] = {} # (from_unit, to_unit) -> factor
51
- self.dimensional_groups: Dict[int, List[UnitDefinition]] = {}
52
- self._dimension_cache: Dict[int, UnitConstant] = {} # Cache for common dimension mappings
53
-
49
+ self.units: dict[str, UnitDefinition] = {}
50
+ self.conversion_table: dict[tuple[str, str], float] = {} # (from_unit, to_unit) -> factor
51
+ self.dimensional_groups: dict[int, list[UnitDefinition]] = {}
52
+ self._dimension_cache: dict[int, UnitConstant] = {} # Cache for common dimension mappings
53
+
54
54
  self._initialize_units()
55
55
  self._precompute_conversions()
56
56
 
@@ -110,4 +110,4 @@ class HighPerformanceRegistry:
110
110
 
111
111
 
112
112
  # Global high-performance registry
113
- registry = HighPerformanceRegistry()
113
+ registry = HighPerformanceRegistry()
qnty/units.py CHANGED
@@ -7,7 +7,6 @@ Type-safe unit constants for common engineering units.
7
7
 
8
8
  from .unit import UnitConstant, registry
9
9
 
10
-
11
10
  # =====================================================================
12
11
  # Type-Safe Unit Constants (No More Strings!)
13
12
  # =====================================================================
@@ -31,7 +30,7 @@ class LengthUnits:
31
30
  class PressureUnits:
32
31
  """Type-safe pressure unit constants."""
33
32
  pascal = UnitConstant(registry.units["pascal"])
34
- kilopascal = UnitConstant(registry.units["kilopascal"])
33
+ kilopascal = UnitConstant(registry.units["kilopascal"])
35
34
  megapascal = UnitConstant(registry.units["megapascal"])
36
35
  psi = UnitConstant(registry.units["psi"])
37
36
  bar = UnitConstant(registry.units["bar"])
@@ -44,4 +43,4 @@ class PressureUnits:
44
43
 
45
44
  class DimensionlessUnits:
46
45
  """Type-safe dimensionless unit constants."""
47
- dimensionless = UnitConstant(registry.units["dimensionless"])
46
+ dimensionless = UnitConstant(registry.units["dimensionless"])