Parameter Unit System Overview
Introduction
The REFA module implements a comprehensive unit conversion system using three key objects:
- UnitSystem: Definition of the unit system to be used for input/output interaction with the module
- CF (Conversion Factors): Numerical conversion factors between metric and imperial units
- LB (Labels): Human-readable unit labels for display in results
This document explains how these are used throughout the codebase for seamless metric/imperial interoperability.
UnitSystem
The UnitSystem class provides a global mechanism for managing the active unit system throughout the REFA module. It acts as a singleton that controls which unit system is used for all parameter conversions, function signatures, and user-facing outputs.
Purpose
UnitSystem serves as the single source of truth for:
1. Active unit system - Whether the module operates in metric or imperial mode
2. Configuration persistence - Reading the default unit system from config.toml
4. Global state - Ensuring all components use the same unit system
Definition
Located in refa/system_parameters/parameter_access.py
Core Methods
is_imperial()
Checks if the current unit system is imperial.
Returns:
- bool - True if in imperial mode, False otherwise
is_metric()
Checks if the current unit system is metric.
Returns:
- bool - True if in metric mode, False otherwise
Configuration File
The default unit system is read from refa/system_parameters/config.toml:
set(system)
Sets the active unit system globally.
The new system will be effective only when all imported models from REFA module are reloaded. Therefore, the recommended way to change the system of units is to directly update config.toml file before any imports.
Parameters:
- system (str): Target unit system - 'metric' or 'imperial'
Raises:
- ValueError if system is not 'metric' or 'imperial'
Conversion Factors (CF)
Purpose
CF is a frozen dataclass containing all conversion factors needed to convert between metric and imperial units. It serves as the single source of truth for all unit conversions.
Definition
Located in refa/system_parameters/parameter_access.py:
@dataclass(frozen=True)
class _CF:
# Length conversions
m_to_mile: float = 0.000621371
mile_to_m: float = 1 / 0.000621371
km_to_mile: float = 0.621371
mile_to_km: float = 1 / 0.621371
m_to_ft: float = 3.28084
ft_to_m: float = 0.3048
# ... and many more
# Temperature conversions (functions)
@staticmethod
def c_to_f(v: float) -> float:
return v * 9 / 5 + 32
@staticmethod
def f_to_c(v: float) -> float:
return (v - 32) * 5 / 9
CF = _CF() # Singleton instance
Available Conversion Factors
Length
CF.m_to_mile # 0.000621371
CF.mile_to_m # 1609.344
CF.km_to_mile # 0.621371
CF.mile_to_km # 1.60934
CF.m_to_ft # 3.28084
CF.ft_to_m # 0.3048
CF.m_to_in # 39.3701
CF.in_to_m # 0.0254
CF.mm_to_in # 0.0393701
CF.in_to_mm # 25.4
Area
Mass & Weight
CF.kg_to_lb # 2.20462
CF.lb_to_kg # 0.453592
CF.n_per_m_to_lbs_per_kft # 0.0685
CF.lbs_per_kft_to_n_per_m # 14.593
Force
Pressure & Stress
CF.gpa_to_ksi # 145.0377378
CF.ksi_to_gpa # 0.00689476
CF.pa_to_lb_per_ft2 # 0.020885434
CF.lb_per_ft2_to_pa # 47.8803
Density
Speed
Temperature (Functions)
CF.c_to_f(celsius_value) # Convert Celsius to Fahrenheit
CF.f_to_c(fahrenheit_value) # Convert Fahrenheit to Celsius
Usage in Parameter Access
The ParameterAccess class uses CF internally for automatic conversions:
def _convert_value(self, metric_value: Any, param_info: dict, target_unit: str) -> Any:
"""Convert metric value to target unit using CF."""
# Special handling for temperature
if target_unit in ('f', 'fahrenheit'):
return CF.c_to_f(metric_value)
# Special handling for costs
if target_unit == 'dol_per_kft':
return metric_value * CF.ft_to_m
# General linear conversions
cf_key = f"{param_info['metric_unit']}_to_{target_unit}"
if hasattr(CF, cf_key):
return metric_value * getattr(CF, cf_key)
Usage in validate_args Decorator
The validate_args decorator uses CF for imperial parameter conversion:
@validate_args(
max_sag_m=param(
">", 0,
imperial=("max_sag_ft", CF.ft_to_m), # Imperial name and conversion
to_imperial=CF.m_to_ft # Reverse conversion for display
)
)
def is_sag_feasible(self, max_sag_m, ...):
# When in imperial mode, user passes max_sag_ft
# Decorator converts to max_sag_m using CF.ft_to_m
pass
Method param() in parameter_access.py helps formatting validation and conversion rules for each considedred parameter.
Unit Labels (LB)
Purpose
LB is a frozen dataclass containing human-readable labels for all units. These are used when displaying results to users.
Definition
Located in refa/system_parameters/parameter_access.py:
@dataclass(frozen=True)
class _Labels:
# Length
m: str = "m"
km: str = "km"
mm: str = "mm"
ft: str = "ft"
mile: str = "mile"
inch: str = "in"
# Temperature
celsius: str = "°C"
fahrenheit: str = "°F"
# Area
mm2: str = "mm²"
kcmil: str = "kcmil"
# Force
kn: str = "kN"
kip: str = "kip"
# ... and many more
LB = _Labels() # Singleton instance
Available Labels
Length
Temperature
Speed
Area
Force
Linear Weight
Pressure & Stress
Density
Electrical
Dimensionless
Usage in Results
When returning results, the module includes unit labels:
# Example from get_parameter()
value, unit_label = conductor.get_parameter('area', unit_system='imperial')
# Returns: (987, 'kcmil')
# Display to user
print(f"Conductor area: {value} {unit_label}")
# Output: Conductor area: 987 kcmil
Conversion Factor Naming Convention
All conversion factors follow a consistent naming pattern:
Examples:
- CF.mm2_to_kcmil - Convert square millimeters to kcmil
- CF.m_to_ft - Convert meters to feet
- CF.c_to_f - Convert Celsius to Fahrenheit (function)
This makes it easy to discover and use the correct conversion factor.
Temperature Conversions
Temperature conversions are special because they're non-linear (involve offset):
# Celsius to Fahrenheit
CF.c_to_f(0) # 32
CF.c_to_f(100) # 212
# Fahrenheit to Celsius
CF.f_to_c(32) # 0
CF.f_to_c(212) # 100
Thermal expansion coefficients (per degree) use linear conversions:
Precision Handling
All conversions in ParameterAccess apply rounding to avoid floating-point precision issues:
def _convert_value(self, metric_value, param_info, target_unit):
# ... conversion logic ...
return math.ceil(round(metric_value * conversion_factor, 10) * 1e6) / 1e6
This ensures results are clean and readable while maintaining precision.
Adding New Conversions
To add a new conversion factor:
- Add the factor to the
_CFdataclass inparameter_access.py - Add the corresponding label to the
_Labelsdataclass - Update the
PARAMETER_REGISTRYif adding a new parameter - Update this documentation
Example: