ansys-units 0.5.dev0__tar.gz → 0.6.dev0__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.
Files changed (22) hide show
  1. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/PKG-INFO +4 -4
  2. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/pyproject.toml +4 -4
  3. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/__init__.py +28 -13
  4. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/dimensions.py +1 -4
  5. ansys_units-0.6.dev0/src/ansys/units/quantity_dimensions.py +267 -0
  6. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/systems.py +2 -1
  7. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/unit.py +2 -0
  8. ansys_units-0.6.dev0/src/ansys/units/variable_descriptor/__init__.py +50 -0
  9. ansys_units-0.6.dev0/src/ansys/units/variable_descriptor/strategy.py +240 -0
  10. ansys_units-0.6.dev0/src/ansys/units/variable_descriptor/variable_descriptor.py +270 -0
  11. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/LICENSE +0 -0
  12. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/README.rst +0 -0
  13. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/_constants.py +0 -0
  14. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/_version.py +0 -0
  15. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/base_dimensions.py +0 -0
  16. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/cfg.yaml +0 -0
  17. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/docs/.gitignore +0 -0
  18. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/quantity.py +0 -0
  19. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/quantity_tables/bt_table.yaml +0 -0
  20. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/quantity_tables/cgs_table.yaml +0 -0
  21. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/quantity_tables/si_table.yaml +0 -0
  22. {ansys_units-0.5.dev0 → ansys_units-0.6.dev0}/src/ansys/units/unit_registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ansys-units
3
- Version: 0.5.dev0
3
+ Version: 0.6.dev0
4
4
  Summary: Pythonic interface for units, unit systems, and unit conversions.
5
5
  Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
6
6
  Maintainer-email: "ANSYS, Inc." <pyansys.core@ansys.com>
@@ -19,7 +19,7 @@ Requires-Dist: importlib-metadata>=4.0
19
19
  Requires-Dist: pyyaml>=6.0
20
20
  Requires-Dist: ansys-sphinx-theme==1.3.2 ; extra == "doc"
21
21
  Requires-Dist: pdf2image==1.17.0 ; extra == "doc"
22
- Requires-Dist: quarto-cli==1.6.42 ; extra == "doc"
22
+ Requires-Dist: quarto-cli==1.7.29 ; extra == "doc"
23
23
  Requires-Dist: matplotlib==3.10.1 ; extra == "doc"
24
24
  Requires-Dist: numpydoc==1.8.0 ; extra == "doc"
25
25
  Requires-Dist: Sphinx==7.4.7 ; extra == "doc"
@@ -27,8 +27,8 @@ Requires-Dist: sphinx-copybutton==0.5.2 ; extra == "doc"
27
27
  Requires-Dist: sphinx-gallery==0.19.0 ; extra == "doc"
28
28
  Requires-Dist: sphinx-toggleprompt==0.6.0 ; extra == "doc"
29
29
  Requires-Dist: numpy==2.2.3 ; extra == "lists"
30
- Requires-Dist: pytest==8.3.4 ; extra == "tests"
31
- Requires-Dist: pytest-cov==6.0.0 ; extra == "tests"
30
+ Requires-Dist: pytest==8.3.5 ; extra == "tests"
31
+ Requires-Dist: pytest-cov==6.1.1 ; extra == "tests"
32
32
  Requires-Dist: pytest-mock==3.14.0 ; extra == "tests"
33
33
  Requires-Dist: pint==0.24.4 ; extra == "tests"
34
34
  Project-URL: Documentation, https://ansunits.docs.pyansys.com
@@ -5,7 +5,7 @@ build-backend = "flit_core.buildapi"
5
5
  [project]
6
6
  # Check https://flit.readthedocs.io/en/latest/pyproject_toml.html for all available sections
7
7
  name = "ansys-units"
8
- version = "0.5.dev0"
8
+ version = "0.6.dev0"
9
9
  description = "Pythonic interface for units, unit systems, and unit conversions."
10
10
  readme = "README.rst"
11
11
  requires-python = ">=3.9,<4"
@@ -32,7 +32,7 @@ dependencies = [
32
32
  doc = [
33
33
  "ansys-sphinx-theme==1.3.2",
34
34
  "pdf2image==1.17.0",
35
- "quarto-cli==1.6.42",
35
+ "quarto-cli==1.7.29",
36
36
  "matplotlib==3.10.1",
37
37
  "numpydoc==1.8.0",
38
38
  "Sphinx==7.4.7",
@@ -41,8 +41,8 @@ doc = [
41
41
  "sphinx-toggleprompt==0.6.0",
42
42
  ]
43
43
  tests = [
44
- "pytest==8.3.4",
45
- "pytest-cov==6.0.0",
44
+ "pytest==8.3.5",
45
+ "pytest-cov==6.1.1",
46
46
  "pytest-mock==3.14.0",
47
47
  "pint==0.24.4",
48
48
  ]
@@ -30,20 +30,19 @@ __version__ = importlib_metadata.version(__name__.replace(".", "-"))
30
30
 
31
31
  import os
32
32
 
33
- from ansys.units._constants import ( # noqa: F401
34
- _base_units,
35
- _derived_units,
36
- _multipliers,
37
- _quantity_units_table,
38
- _QuantityType,
39
- _unit_systems,
33
+ from ansys.units.base_dimensions import BaseDimensions
34
+ from ansys.units.dimensions import Dimensions
35
+ from ansys.units.quantity import Quantity, get_si_value
36
+ from ansys.units.quantity_dimensions import QuantityDimensions
37
+ from ansys.units.systems import UnitSystem
38
+ from ansys.units.unit import Unit
39
+ from ansys.units.unit_registry import UnitRegistry
40
+ from ansys.units.variable_descriptor import (
41
+ ConversionStrategy,
42
+ MappingConversionStrategy,
43
+ VariableCatalog,
44
+ VariableDescriptor,
40
45
  )
41
- from ansys.units.base_dimensions import BaseDimensions # noqa: F401
42
- from ansys.units.dimensions import Dimensions # noqa: F401
43
- from ansys.units.quantity import Quantity, get_si_value # noqa: F401
44
- from ansys.units.systems import UnitSystem # noqa: F401
45
- from ansys.units.unit import Unit # noqa: F401
46
- from ansys.units.unit_registry import UnitRegistry # noqa: F401
47
46
 
48
47
  _THIS_DIRNAME = os.path.dirname(__file__)
49
48
  _README_FILE = os.path.normpath(os.path.join(_THIS_DIRNAME, "docs", "README.rst"))
@@ -51,3 +50,19 @@ _README_FILE = os.path.normpath(os.path.join(_THIS_DIRNAME, "docs", "README.rst"
51
50
  if os.path.exists(_README_FILE):
52
51
  with open(_README_FILE, encoding="utf8") as f:
53
52
  __doc__ = f.read()
53
+
54
+
55
+ __all__ = [
56
+ "BaseDimensions",
57
+ "Dimensions",
58
+ "Quantity",
59
+ "get_si_value",
60
+ "UnitSystem",
61
+ "Unit",
62
+ "UnitRegistry",
63
+ "QuantityDimensions",
64
+ "VariableDescriptor",
65
+ "VariableCatalog",
66
+ "ConversionStrategy",
67
+ "MappingConversionStrategy",
68
+ ]
@@ -75,10 +75,7 @@ class Dimensions:
75
75
  str
76
76
  A string version of the dimensions.
77
77
  """
78
- dims = {x.name: y for x, y in self}
79
- if not dims:
80
- dims = ""
81
- return str(dims)
78
+ return str({x.name: y for x, y in self})
82
79
 
83
80
  def __str__(self):
84
81
  return self._to_string()
@@ -0,0 +1,267 @@
1
+ # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2
+
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in all
14
+ # copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ """
24
+ Defines immutable objects representing SI base and derived physical dimensions.
25
+
26
+ This module provides globally defined `Dimensions` instances corresponding to the
27
+ seven SI base quantities (e.g., mass, time) and many commonly used derived quantities
28
+ (e.g., force, energy, pressure). These dimension definitions are used internally
29
+ for dimensional analysis, quantity construction, and validation.
30
+
31
+ Each dimension is represented as a `Dimensions` object, which captures the
32
+ dimensional exponents of base quantities such as mass, length, and time.
33
+
34
+ This module is not intended for direct modification at runtime. All objects
35
+ are statically defined and immutable.
36
+
37
+ Examples
38
+ --------
39
+ >>> from ansys.units.dimensions import Dimensions
40
+ >>> from ansys.units.quantity_dimensions import QuantityDimensions
41
+
42
+ >>> force = QuantityDimensions.FORCE
43
+ >>> pressure = QuantityDimensions.PRESSURE
44
+ >>> pressure == force / QuantityDimensions.AREA
45
+ True
46
+ """
47
+
48
+ from ansys.units.base_dimensions import BaseDimensions
49
+ from ansys.units.dimensions import Dimensions
50
+
51
+ # Local function to convert from a `BaseDimensions` enum object to a `Dimensions` object
52
+
53
+
54
+ def _make_base_dimensions(dimension: BaseDimensions) -> Dimensions:
55
+ return Dimensions(dimensions={dimension: 1.0})
56
+
57
+
58
+ def _expand_vector_quantities(cls):
59
+ angle = _make_base_dimensions(BaseDimensions.ANGLE)
60
+ for name in getattr(cls, "_vector_quantities", []):
61
+ value = getattr(cls, name)
62
+ # cartesian only added by default
63
+ setattr(cls, f"{name}_X", value)
64
+ setattr(cls, f"{name}_Y", value)
65
+ setattr(cls, f"{name}_Z", value)
66
+ # magnitude
67
+ setattr(cls, f"{name}_MAGNITUDE", value)
68
+ return cls
69
+
70
+
71
+ @_expand_vector_quantities
72
+ class QuantityDimensions:
73
+ """
74
+ Defines immutable dimension objects for standard physical quantities.
75
+
76
+ This class encapsulates SI base dimensions (e.g., MASS, TIME) and
77
+ commonly used derived dimensions (e.g., FORCE, PRESSURE, ENERGY),
78
+ using `Dimensions` instances.
79
+
80
+ These objects support arithmetic operations (multiplication, division,
81
+ exponentiation) to enable dimensional composition and analysis.
82
+
83
+ All attributes are class-level constants and should be treated as immutable.
84
+
85
+ Examples
86
+ --------
87
+ >>> QuantityDimensions.VELOCITY
88
+ Dimensions(dimensions={BaseDimensions.LENGTH: 1.0, BaseDimensions.TIME: -1.0})
89
+ """
90
+
91
+ # Dimensions of base quantities
92
+ MASS = _make_base_dimensions(BaseDimensions.MASS)
93
+ LENGTH = _make_base_dimensions(BaseDimensions.LENGTH)
94
+ TIME = _make_base_dimensions(BaseDimensions.TIME)
95
+ TEMPERATURE = _make_base_dimensions(BaseDimensions.TEMPERATURE)
96
+ CHEMICAL_AMOUNT = _make_base_dimensions(BaseDimensions.CHEMICAL_AMOUNT)
97
+ LIGHT = _make_base_dimensions(BaseDimensions.LIGHT)
98
+ CURRENT = _make_base_dimensions(BaseDimensions.CURRENT)
99
+
100
+ # Supplementary base quantities
101
+ ANGLE = _make_base_dimensions(BaseDimensions.ANGLE)
102
+ SOLID_ANGLE = _make_base_dimensions(BaseDimensions.SOLID_ANGLE)
103
+ TEMPERATURE_DIFFERENCE = _make_base_dimensions(
104
+ BaseDimensions.TEMPERATURE_DIFFERENCE
105
+ )
106
+
107
+ # Dimensions of derived quantities
108
+
109
+ # Kinematics and mechanics
110
+ AREA = LENGTH**2
111
+ VOLUME = LENGTH**3
112
+ FREQUENCY = TIME**-1
113
+ VELOCITY = LENGTH / TIME
114
+ ACCELERATION = VELOCITY / TIME
115
+ FORCE = MASS * ACCELERATION
116
+ PRESSURE = FORCE / AREA
117
+ STRESS = PRESSURE
118
+ JERK = LENGTH / TIME**3
119
+ SNAP = LENGTH / TIME**4
120
+ ANGULAR_VELOCITY = TIME**-1
121
+ ANGULAR_ACCELERATION = TIME**-2
122
+ VOLUMETRIC_FLOW = VOLUME / TIME
123
+ MOMENTUM = MASS * VELOCITY
124
+ ANGULAR_MOMENTUM = LENGTH**2 * MASS / TIME
125
+ TORQUE = LENGTH**2 * MASS / TIME**2
126
+ ENERGY = LENGTH**2 * MASS / TIME**2
127
+ POWER = ENERGY / TIME
128
+ DENSITY = MASS / VOLUME
129
+ SPECIFIC_VOLUME = VOLUME / MASS
130
+ ENERGY_DENSITY = ENERGY / VOLUME
131
+ SURFACE_TENSION = FORCE / LENGTH
132
+ KINEMATIC_VISCOSITY = AREA / TIME
133
+ DYNAMIC_VISCOSITY = PRESSURE * TIME
134
+ MASS_FLOW_RATE = MASS / TIME
135
+ MASS_CONTROL = MASS / TIME**3
136
+ FREQUENCY_DRIFT = TIME**-2
137
+ YANK = FORCE / TIME
138
+ WAVENUMBER = LENGTH**-1
139
+ AREA_DENSITY = MASS / AREA
140
+ ACTION = ENERGY * TIME
141
+ SPECIFIC_ENERGY = ENERGY / MASS
142
+ HEAT_FLUX_DENSITY = POWER / AREA
143
+ SURFACE_HEAT_FLUX = HEAT_FLUX_DENSITY
144
+ LINEAR_MASS_DENSITY = MASS / LENGTH
145
+ RADIANCE = POWER / (SOLID_ANGLE * AREA)
146
+ SPECTRAL_POWER = POWER / LENGTH
147
+ ABSORBED_DOSE_RATE = AREA / TIME**3
148
+ FUEL_EFFICIENCY = LENGTH**-2
149
+ SPECTRAL_IRRADIANCE = POWER / VOLUME
150
+ ENERGY_FLUX_DENSITY = POWER / AREA
151
+ COMPRESSIBILITY = PRESSURE**-1
152
+ RADIANT_EXPOSURE = ENERGY / AREA
153
+ MOMENT_OF_INERTIA = MASS * AREA
154
+ SPECIFIC_ANGULAR_MOMENTUM = ANGULAR_MOMENTUM / MASS
155
+ RADIANT_INTENSITY = POWER / SOLID_ANGLE
156
+ SPECTRAL_INTENSITY = POWER / (SOLID_ANGLE * LENGTH)
157
+ STRAIN_RATE = TIME**-1
158
+ AXIAL_VELOCITY = VELOCITY
159
+ RADIAL_VELOCITY = VELOCITY
160
+ TANGENTIAL_VELOCITY = VELOCITY
161
+
162
+ # Electromagnetics
163
+ ELECTRIC_CHARGE = TIME * CURRENT
164
+ ELECTRICAL_POTENTIAL = MASS * AREA * TIME**-3 / CURRENT
165
+ ELECTRICAL_CAPACITANCE = MASS**-1 * LENGTH**-2 * TIME**4 * CURRENT**2
166
+ ELECTRICAL_INDUCTANCE = MASS * LENGTH**2 * TIME**-2 * CURRENT**-2
167
+ ELECTRICAL_RESISTANCE = ELECTRICAL_POTENTIAL / CURRENT
168
+ ELECTRICAL_IMPEDANCE = ELECTRICAL_RESISTANCE
169
+ ELECTRICAL_REACTANCE = ELECTRICAL_RESISTANCE
170
+ ELECTRICAL_CONDUCTANCE = ELECTRICAL_RESISTANCE**-1
171
+ MAGNETIC_FLUX = MASS * LENGTH**2 * TIME**-2 * CURRENT**-1
172
+ MAGNETIC_INDUCTION = MASS * TIME**-2 * CURRENT**-1
173
+ MAGNETIC_PERMEABILITY = LENGTH * MASS * TIME**-2 * CURRENT**-2
174
+ MAGNETIC_VECTOR_POTENTIAL = LENGTH * MASS * TIME**-2 * CURRENT**-1
175
+
176
+ # Chemistry
177
+ MOLARITY = CHEMICAL_AMOUNT / VOLUME
178
+ MOLAR_VOLUME = VOLUME / CHEMICAL_AMOUNT
179
+ MOLAR_HEAT_CAPACITY = ENERGY / (TEMPERATURE * CHEMICAL_AMOUNT)
180
+ MOLAR_ENTROPY = MOLAR_HEAT_CAPACITY
181
+ MOLAR_ENERGY = ENERGY / CHEMICAL_AMOUNT
182
+ MOLALITY = CHEMICAL_AMOUNT / MASS
183
+ MOLAR_MASS = MASS / CHEMICAL_AMOUNT
184
+ CATALYTIC_EFFICIENCY = VOLUME / (CHEMICAL_AMOUNT * TIME)
185
+
186
+ # Photometry
187
+ LUMINOUS_FLUX = LIGHT * SOLID_ANGLE
188
+ ILLUMINANCE = LUMINOUS_FLUX / AREA
189
+ LUMINANCE = LIGHT / AREA
190
+ LUMINOUS_EXPOSURE = ILLUMINANCE * TIME
191
+
192
+ # Thermodynamics
193
+ HEAT_CAPACITY = ENERGY / TEMPERATURE
194
+ SPECIFIC_HEAT_CAPACITY = HEAT_CAPACITY / MASS
195
+ THERMAL_CONDUCTIVITY = POWER / (LENGTH * TEMPERATURE)
196
+ THERMAL_RESISTANCE = TEMPERATURE / POWER
197
+ TEMPERATURE_GRADIENT = TEMPERATURE / LENGTH
198
+ ENTHALPY = ENERGY
199
+ ENTROPY = ENERGY / TEMPERATURE
200
+ SPECIFIC_ENTHALPY = ENTHALPY / MASS
201
+ SPECIFIC_ENTROPY = ENTROPY / MASS
202
+ INTERNAL_ENERGY = ENERGY
203
+ TOTAL_ENERGY = ENERGY
204
+ SPECIFIC_TOTAL_ENERGY = TOTAL_ENERGY / MASS
205
+ SPECIFIC_INTERNAL_ENERGY = INTERNAL_ENERGY / MASS
206
+ TOTAL_TEMPERATURE = TEMPERATURE
207
+
208
+ # Turbulence
209
+ PRANDTL_NUMBER = Dimensions()
210
+ EFFECTIVE_PRANDTL_NUMBER = Dimensions()
211
+ EFFECTIVE_THERMAL_CONDUCTIVITY = THERMAL_CONDUCTIVITY
212
+ EFFECTIVE_VISCOSITY = DYNAMIC_VISCOSITY
213
+ PRODUCTION_OF_TURBULENT_KINETIC_ENERGY = MASS * LENGTH**-1 * TIME**-3
214
+ SPECIFIC_DISSIPATION_RATE = TIME**-1
215
+ TURBULENT_DISSIPATION_RATE = LENGTH**2 * TIME**3
216
+ TURBULENT_INTENSITY = Dimensions()
217
+ TURBULENT_VISCOSITY = DYNAMIC_VISCOSITY
218
+ TURBULENT_VISCOSITY_RATIO = Dimensions()
219
+ TURBULENT_REYNOLDS_NUMBER = Dimensions()
220
+ TURBULENT_KINETIC_ENERGY = LENGTH**2 * TIME**-2
221
+ WALL_Y_PLUS = Dimensions()
222
+ WALL_Y_STAR = Dimensions()
223
+
224
+ # Fluid pressure
225
+ STATIC_PRESSURE = PRESSURE # equivalent
226
+ ABSOLUTE_PRESSURE = PRESSURE
227
+ DYNAMIC_PRESSURE = PRESSURE
228
+ TOTAL_PRESSURE = PRESSURE
229
+ PRESSURE_COEFFICIENT = Dimensions()
230
+
231
+ # Other fluid and related quantities
232
+ VORTICITY = TIME**-1
233
+ CONVECTIVE_COURANT_NUMBER = Dimensions()
234
+ CELL_REYNOLDS_NUMBER = Dimensions()
235
+ Q_CRITERION = TIME**-2
236
+ NORMALIZED_Q_CRITERION = Dimensions()
237
+ MESH_VELOCITY = VELOCITY
238
+ SPECIFIC_TOTAL_ENTHALPY = SPECIFIC_ENTHALPY
239
+ SURFACE_NUSSELT_NUMBER = Dimensions()
240
+ SURFACE_STANTON_NUMBER = Dimensions()
241
+ SKIN_FRICTION_COEFFICIENT = Dimensions()
242
+ SURFACE_HEAT_TRANSFER_COEFFICIENT = POWER * LENGTH**-2 * TEMPERATURE**-1
243
+ WALL_ADJACENT_HEAT_TRANSFER_COEFFICIENT = POWER * LENGTH**-2 * TEMPERATURE**-1
244
+ WALL_TEMPERATURE = TEMPERATURE
245
+ WALL_ADJACENT_TEMPERATURE = TEMPERATURE
246
+ WALL_TEMPERATURE_THIN = TEMPERATURE
247
+ WALL_SHEAR_STRESS = STRESS
248
+
249
+ # Coordinates
250
+ POSITION = LENGTH
251
+
252
+ # List of vector quantities. The only impact
253
+ # of not adding a vector quantity here is that
254
+ # the code will not autogenerate attributes
255
+ # <quantity>_X etc for that quantity.
256
+ _vector_quantities = [
257
+ "VELOCITY",
258
+ "ACCELERATION",
259
+ "VORTICITY",
260
+ "POSITION",
261
+ "MESH_VELOCITY",
262
+ "MOMENTUM",
263
+ "ANGULAR_MOMENTUM",
264
+ "TORQUE",
265
+ "FORCE",
266
+ "WALL_SHEAR_STRESS",
267
+ ]
@@ -23,7 +23,8 @@
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
- from ansys.units import BaseDimensions, _base_units, _unit_systems
26
+ from ansys.units import BaseDimensions
27
+ from ansys.units._constants import _base_units, _unit_systems
27
28
 
28
29
 
29
30
  class UnitSystem:
@@ -28,6 +28,8 @@ from typing import Optional, Union
28
28
  from ansys.units import (
29
29
  BaseDimensions,
30
30
  Dimensions,
31
+ )
32
+ from ansys.units._constants import (
31
33
  _base_units,
32
34
  _derived_units,
33
35
  _multipliers,
@@ -0,0 +1,50 @@
1
+ # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+ """
23
+ Package initialization for ansys.units.variable_descriptor.
24
+
25
+ Exports the VariableCatalog catalog and core interfaces for working with
26
+ physical quantities in a product-agnostic way.
27
+
28
+ Example
29
+ -------
30
+
31
+ .. code-block:: python
32
+
33
+ from ansys.units.variable_descriptor import VariableCatalog
34
+ from ansys.fluent.core.variable_strategies import FluentSVarNamingStrategy
35
+
36
+ quantity = VariableCatalog.PRESSURE
37
+ strategy = FluentSVarNamingStrategy()
38
+
39
+ print(strategy.to_string(quantity))
40
+ """
41
+
42
+ from .strategy import ConversionStrategy, MappingConversionStrategy
43
+ from .variable_descriptor import VariableCatalog, VariableDescriptor
44
+
45
+ __all__ = [
46
+ "VariableDescriptor",
47
+ "VariableCatalog",
48
+ "ConversionStrategy",
49
+ "MappingConversionStrategy",
50
+ ]
@@ -0,0 +1,240 @@
1
+ # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+ """
23
+ Defines the ConversionStrategy base class for translating `VariableDescriptor` objects.
24
+
25
+ Each concrete strategy maps `VariableDescriptor` instances to the string representations
26
+ required by a specific system.
27
+ """
28
+
29
+ from abc import ABC, abstractmethod
30
+ import types
31
+
32
+ from ansys.units.variable_descriptor.variable_descriptor import VariableDescriptor
33
+
34
+
35
+ class ConversionStrategy(ABC):
36
+ """
37
+ Abstract base class for `VariableDescriptor` conversion strategies.
38
+
39
+ This class defines the interface for all conversion strategies. Derived classes
40
+ must implement the methods defined here to handle the conversion of `VariableDescriptor`
41
+ objects to and from their string representations, as well as to check if a
42
+ `VariableDescriptor` is supported.
43
+ """
44
+
45
+ @abstractmethod
46
+ def to_string(self, variable: VariableDescriptor | str | None) -> str | None:
47
+ """
48
+ Convert a `VariableDescriptor` to its string representation.
49
+
50
+ Parameters
51
+ ----------
52
+ variable : VariableDescriptor | str
53
+ The `VariableDescriptor` to convert, or a string representation.
54
+
55
+ Returns
56
+ -------
57
+ str
58
+ The string representation of the `VariableDescriptor`.
59
+
60
+ Raises
61
+ ------
62
+ NotImplementedError
63
+ If the method is not implemented in a derived class.
64
+ """
65
+ pass
66
+
67
+ @abstractmethod
68
+ def to_variable_descriptor(
69
+ self, variable: VariableDescriptor | str
70
+ ) -> VariableDescriptor:
71
+ """
72
+ Convert a string to its corresponding `VariableDescriptor`.
73
+
74
+ Parameters
75
+ ----------
76
+ variable : VariableDescriptor | str
77
+ The string representation to convert, or a `VariableDescriptor`.
78
+
79
+ Returns
80
+ -------
81
+ VariableDescriptor
82
+ The corresponding `VariableDescriptor` instance.
83
+
84
+ Raises
85
+ ------
86
+ NotImplementedError
87
+ If the method is not implemented in a derived class.
88
+ """
89
+ pass
90
+
91
+ @abstractmethod
92
+ def supports(self, variable: VariableDescriptor) -> bool:
93
+ """
94
+ Check if the given `VariableDescriptor` is supported by the strategy.
95
+
96
+ Parameters
97
+ ----------
98
+ variable : VariableDescriptor
99
+ The `VariableDescriptor` to check.
100
+
101
+ Returns
102
+ -------
103
+ bool
104
+ `True` if the `VariableDescriptor` is supported, `False` otherwise.
105
+
106
+ Raises
107
+ ------
108
+ NotImplementedError
109
+ If the method is not implemented in a derived class.
110
+ """
111
+ pass
112
+
113
+
114
+ class MappingConversionStrategy(ConversionStrategy):
115
+ """
116
+ Intermediate base class for implementing VariableDescriptor conversion strategies.
117
+
118
+ This class simplifies the creation of concrete strategy classes by providing
119
+ default implementations for common methods. Classes inheriting from this base
120
+ class only need to define a `_mapping` dictionary that maps `VariableDescriptor`
121
+ instances to their corresponding string representations.
122
+
123
+ Attributes
124
+ ----------
125
+ _reverse_mapping : dict
126
+ A lazily initialized reverse mapping of `_mapping`, used for converting
127
+ strings back to `VariableDescriptor` instances.
128
+
129
+ Methods
130
+ -------
131
+ to_string(variable: VariableDescriptor | str) -> str
132
+ Converts a `VariableDescriptor` to its string representation. Raises a
133
+ `ValueError` if the variable is not supported.
134
+ to_variable_descriptor(variable: VariableDescriptor | str) -> VariableDescriptor
135
+ Converts a string to its corresponding `VariableDescriptor`.
136
+ supports(variable: VariableDescriptor) -> bool
137
+ Checks if the given `VariableDescriptor` is supported by the strategy.
138
+
139
+ Raises
140
+ ------
141
+ ValueError
142
+ If a `VariableDescriptor` is not supported during conversion to a string.
143
+ """
144
+
145
+ def __init__(self):
146
+ """
147
+ Initialize the MappingConversionStrategy.
148
+
149
+ This constructor initializes the reverse mapping attribute to `None`.
150
+ The reverse mapping is lazily initialized when accessed via the `_reverse_mapping` property.
151
+ """
152
+ self.__reverse_mapping = None
153
+
154
+ @property
155
+ def _reverse_mapping(self):
156
+ """
157
+ Get the reverse mapping of `_mapping`.
158
+
159
+ The reverse mapping is a dictionary that maps string representations
160
+ back to their corresponding `VariableDescriptor` instances. It is lazily
161
+ initialized on first access.
162
+
163
+ Returns
164
+ -------
165
+ dict
166
+ A dictionary mapping string representations to `VariableDescriptor` instances.
167
+ """
168
+ if self.__reverse_mapping is None:
169
+ self.__reverse_mapping = {x: y for y, x in self._mapping.items()}
170
+ return self.__reverse_mapping
171
+
172
+ def to_string(self, variable: VariableDescriptor | str | None) -> str | None:
173
+ """
174
+ Convert a `VariableDescriptor` to its string representation.
175
+
176
+ If the input is already a string, it is returned as-is. If the input
177
+ is a `VariableDescriptor` and is supported by the strategy, its string
178
+ representation is returned. Otherwise, a `ValueError` is raised.
179
+
180
+ Parameters
181
+ ----------
182
+ variable : VariableDescriptor | str
183
+ The `VariableDescriptor` to convert, or a string representation.
184
+
185
+ Returns
186
+ -------
187
+ str
188
+ The string representation of the `VariableDescriptor`.
189
+
190
+ Raises
191
+ ------
192
+ ValueError
193
+ If the `VariableDescriptor` is not supported by the strategy.
194
+ """
195
+ if isinstance(variable, (str, types.NoneType)):
196
+ return variable
197
+ if not self.supports(variable):
198
+ raise ValueError(f"{variable.name} not supported.")
199
+ return self._mapping[variable]
200
+
201
+ def to_variable_descriptor(
202
+ self, variable: VariableDescriptor | str
203
+ ) -> VariableDescriptor:
204
+ """
205
+ Convert a string to its corresponding `VariableDescriptor`.
206
+
207
+ If the input is already a `VariableDescriptor`, it is returned as-is.
208
+ Otherwise, the string is converted to a `VariableDescriptor` using the
209
+ reverse mapping.
210
+
211
+ Parameters
212
+ ----------
213
+ variable : VariableDescriptor | str
214
+ The string representation to convert, or a `VariableDescriptor`.
215
+
216
+ Returns
217
+ -------
218
+ VariableDescriptor
219
+ The corresponding `VariableDescriptor` instance, or `None` if the
220
+ string is not found in the reverse mapping.
221
+ """
222
+ if isinstance(variable, VariableDescriptor):
223
+ return variable
224
+ return self._reverse_mapping.get(variable)
225
+
226
+ def supports(self, variable: VariableDescriptor) -> bool:
227
+ """
228
+ Check if the given `VariableDescriptor` is supported by the strategy.
229
+
230
+ Parameters
231
+ ----------
232
+ variable : VariableDescriptor
233
+ The `VariableDescriptor` to check.
234
+
235
+ Returns
236
+ -------
237
+ bool
238
+ `True` if the `VariableDescriptor` is supported, `False` otherwise.
239
+ """
240
+ return variable in self._mapping
@@ -0,0 +1,270 @@
1
+ # Copyright (C) 2023 - 2025 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: MIT
3
+ #
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+ """
23
+ Defines the core VariableDescriptor class and predefined quantities.
24
+
25
+ This module provides a structured, extensible way to represent variables based on
26
+ physical quantities (e.g., pressure, velocity) independently of any product-specific
27
+ naming conventions.
28
+ """
29
+
30
+ from dataclasses import dataclass
31
+
32
+ from ansys.units.dimensions import Dimensions
33
+ from ansys.units.quantity_dimensions import QuantityDimensions
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class VariableDescriptor:
38
+ """Defines a physical quantity variable descriptor."""
39
+
40
+ name: str
41
+ dimension: Dimensions
42
+
43
+ def __hash__(self):
44
+ return hash((self.name, str(self.dimension)))
45
+
46
+
47
+ def _validate_and_transform_variable(variable: str) -> str:
48
+ """
49
+ Validate and transform a variable name.
50
+
51
+ Ensures the variable name is uppercase and returns its lowercase equivalent.
52
+
53
+ Parameters
54
+ ----------
55
+ variable : str
56
+ The name of the variable.
57
+
58
+ Returns
59
+ -------
60
+ str
61
+ The lowercase equivalent of the variable name.
62
+
63
+ Raises
64
+ ------
65
+ ValueError
66
+ If the variable name is not uppercase.
67
+ """
68
+ if not variable.isupper():
69
+ raise ValueError(
70
+ f"Variable names in VariableCatalog must be uppercase. "
71
+ f"Invalid name: '{variable}'."
72
+ )
73
+ return variable.lower()
74
+
75
+
76
+ def _build_variable_descriptors_from_dimensions() -> dict[str, VariableDescriptor]:
77
+ """
78
+ Generate a dictionary of variable descriptors from QuantityDimensions.
79
+
80
+ This function iterates over all uppercase attributes in QuantityDimensions and
81
+ creates VariableDescriptor instances for each valid dimension.
82
+ """
83
+ catalog = {}
84
+ for attr_name in dir(QuantityDimensions):
85
+ dimension = getattr(QuantityDimensions, attr_name)
86
+ if isinstance(dimension, Dimensions):
87
+ catalog[attr_name] = VariableDescriptor(
88
+ name=_validate_and_transform_variable(attr_name), dimension=dimension
89
+ )
90
+ return catalog
91
+
92
+
93
+ class VariableCatalog:
94
+ """A catalog of variable descriptors."""
95
+
96
+ # Load from generator
97
+ _generated = _build_variable_descriptors_from_dimensions()
98
+
99
+ # Inject generated descriptors as class attributes
100
+ for _key, _descriptor in _generated.items():
101
+ locals()[_key] = _descriptor
102
+
103
+ @classmethod
104
+ def all(cls) -> dict[str, list[VariableDescriptor]]:
105
+ """
106
+ Return all defined
107
+ :class:`~ansys.units.variable_descriptor.variable_descriptor.VariableDescriptor`
108
+ objects, organized by subcategory.
109
+
110
+ Returns
111
+ -------
112
+ dict[str, list[VariableDescriptor]]
113
+ A dictionary where keys are subcategory names (or "main" for the top-level catalog)
114
+ and values are lists of :class:`~ansys.units.variable_descriptor.variable_descriptor.VariableDescriptor` instances.
115
+ """ # noqa: E501
116
+ result = {"main": []}
117
+
118
+ for key, value in cls.__dict__.items():
119
+ if isinstance(value, VariableDescriptor):
120
+ result["main"].append(value)
121
+ elif (
122
+ isinstance(value, type)
123
+ and issubclass(value, object)
124
+ and value is not object
125
+ ):
126
+ # Collect descriptors from subcategories
127
+ subcategory_name = key
128
+ result[subcategory_name] = [
129
+ v
130
+ for k, v in value.__dict__.items()
131
+ if isinstance(v, VariableDescriptor)
132
+ ]
133
+
134
+ return result
135
+
136
+ @classmethod
137
+ def add(
138
+ cls, variable: str, dimension: Dimensions, subcategory: str | None = None
139
+ ) -> None:
140
+ """
141
+ Add a variable to the catalog.
142
+
143
+ Parameters
144
+ ----------
145
+ variable : str
146
+ The name of the variable (must be uppercase).
147
+ dimension : Dimensions
148
+ The dimension of the variable.
149
+ subcategory: str|None
150
+ The optional subcategory for the variable.
151
+
152
+ Raises
153
+ ------
154
+ ValueError
155
+ The variable name is not uppercase or already exists.
156
+ """
157
+ # Validate and transform the variable name
158
+ transformed_name = _validate_and_transform_variable(variable)
159
+
160
+ # Determine the target category (main catalog or subcategory)
161
+ target = cls
162
+ if subcategory:
163
+ # Ensure the subcategory exists as an attribute
164
+ if not hasattr(cls, subcategory):
165
+ # Create the subcategory and assign a docstring
166
+ subcategory_class = type(
167
+ subcategory,
168
+ (object,),
169
+ {
170
+ "__doc__": (
171
+ f"Dictionary of variable descriptors for {subcategory}-related "
172
+ f"quantities."
173
+ )
174
+ },
175
+ )
176
+ setattr(cls, subcategory, subcategory_class)
177
+ target = getattr(cls, subcategory)
178
+
179
+ # Check if the variable already exists in the target category
180
+ if hasattr(target, variable):
181
+ raise ValueError(
182
+ f"Variable name '{variable}' already exists in the '{subcategory or 'main'}' "
183
+ "catalog. Please choose a unique name."
184
+ )
185
+
186
+ # Add the variable to the target category
187
+ setattr(target, variable, VariableDescriptor(transformed_name, dimension))
188
+
189
+
190
+ # Add custom descriptors
191
+ _D = QuantityDimensions
192
+ fluent_variables = [
193
+ # velocity
194
+ # The dominant meaning of helicity comes from
195
+ # particle physics where it is dimensionless.
196
+ # In CFD, where it has a different
197
+ # meaning, it usually has dimension L^2 T^-2.
198
+ # In Fluent it is L T^-2.
199
+ ("HELICITY", _D.ACCELERATION),
200
+ # Lambda 2 criterion is documented as dimensionless
201
+ # but is T^-2 in Fluent.
202
+ ("LAMBDA_2_CRITERION", _D.TIME**-2),
203
+ ("DENSITY_ALL", _D.DENSITY),
204
+ (
205
+ "Y_PLUS_BASED_HEAT_TRANSFER_COEFFICIENT",
206
+ _D.POWER * _D.LENGTH**-2 * _D.TEMPERATURE**-1,
207
+ ),
208
+ ("TOTAL_ENTHALPY_DEVIATION", _D.SPECIFIC_ENTHALPY),
209
+ # residuals
210
+ ("MASS_IMBALANCE", _D.MASS * _D.TIME**-1),
211
+ # derivatives
212
+ ("PRESSURE_HESSIAN_INDICATOR", Dimensions()),
213
+ ("VELOCITY_ANGLE", _D.ANGLE),
214
+ ("DVELOCITY_DX", _D.TIME**-1),
215
+ ("DVELOCITY_DX_MAGNITUDE", _D.TIME**-1),
216
+ ("DVELOCITY_DX_X", _D.TIME**-1),
217
+ ("DVELOCITY_DX_Y", _D.TIME**-1),
218
+ ("DVELOCITY_DX_Z", _D.TIME**-1),
219
+ ("DVELOCITY_DY", _D.TIME**-1),
220
+ ("DVELOCITY_DY_MAGNITUDE", _D.TIME**-1),
221
+ ("DVELOCITY_DY_X", _D.TIME**-1),
222
+ ("DVELOCITY_DY_Y", _D.TIME**-1),
223
+ ("DVELOCITY_DY_Z", _D.TIME**-1),
224
+ ("DVELOCITY_DZ", _D.TIME**-1),
225
+ ("DVELOCITY_DZ_MAGNITUDE", _D.TIME**-1),
226
+ ("DVELOCITY_DZ_X", _D.TIME**-1),
227
+ ("DVELOCITY_DZ_Y", _D.TIME**-1),
228
+ ("DVELOCITY_DZ_Z", _D.TIME**-1),
229
+ ]
230
+
231
+ for name, dimension in fluent_variables:
232
+ VariableCatalog.add(name, dimension, "fluent")
233
+
234
+
235
+ mesh_variables = [
236
+ # mesh
237
+ ("ANISOTROPIC_ADAPTION_CELLS", Dimensions()),
238
+ ("BOUNDARY_CELL_DISTANCE", Dimensions()),
239
+ ("BOUNDARY_LAYER_CELLS", Dimensions()),
240
+ ("BOUNDARY_NORMAL_DISTANCE", Dimensions()),
241
+ ("BOUNDARY_VOLUME_DISTANCE", Dimensions()),
242
+ ("CELL_EQUIANGLE_SKEW", Dimensions()),
243
+ ("CELL_EQUIVOLUME_SKEW", Dimensions()),
244
+ ("CELL_PARENT_INDEX", Dimensions()),
245
+ ("CELL_REFINE_LEVEL", Dimensions()),
246
+ ("CELL_VOLUME", _D.VOLUME),
247
+ ("CELL_VOLUME_CHANGE", Dimensions()),
248
+ ("ELEMENT_ASPECT_RATIO", Dimensions()),
249
+ ("ELEMENT_WALL_DISTANCE", _D.LENGTH),
250
+ ("FACE_AREA_MAGNITUDE", _D.AREA),
251
+ ("FACE_HANDEDNESS", Dimensions()),
252
+ ("INTERFACE_OVERLAP_FRACTION", Dimensions()),
253
+ ("MARK_POOR_ELEMENTS", Dimensions()),
254
+ ("SMOOTHED_CELL_REFINE_LEVEL", Dimensions()),
255
+ ("X_FACE_AREA", _D.AREA),
256
+ ("Y_FACE_AREA", _D.AREA),
257
+ ("Z_FACE_AREA", _D.AREA),
258
+ # cell info
259
+ ("ACTIVE_CELL_PARTITION", Dimensions()),
260
+ ("CELL_ELEMENT_TYPE", Dimensions()),
261
+ ("CELL_ID", Dimensions()),
262
+ ("CELL_WEIGHT", Dimensions()),
263
+ ("CELL_ZONE_INDEX", Dimensions()),
264
+ ("CELL_ZONE_TYPE", Dimensions()),
265
+ ("PARTITION_NEIGHBOURS", Dimensions()),
266
+ ("STORED_CELL_PARTITIION", Dimensions()),
267
+ ]
268
+
269
+ for name, dimension in mesh_variables:
270
+ VariableCatalog.add(name, dimension, "mesh")
File without changes