Units and Quantities in CIM¶
Power system analysis requires precise handling of physical quantities with proper units. A voltage might be expressed in volts, kilovolts, or megavolts. Power could be in watts, kilowatts, or megawatts. Mixing units or losing track of unit conversions can lead to catastrophic errors in power system analysis.
The CIM addresses this by defining strictly typed physical quantities with automatic unit conversion. CIMantic Graphs integrates with Python's Pint library to provide seamless, type-safe unit handling throughout your CIM models.
CIM Datatypes and Units¶
All quantities in CIM have a strictly defined datatype, which is a UML Primitive or SimpleType.
CIM Datatypes:¶
Primitives - Basic types like String, Boolean, Integer, Float, DateTime
SimpleTypes - Physical quantities with units:
Voltage(V, kV, MV)ActivePower(W, kW, MW, GW)ReactivePower(VAr, kVAr, MVAr)Resistance(Ω, mΩ)Reactance(Ω, mΩ)Length(m, km, ft, mi)Temperature(°C, °F, K)Frequency(Hz)- And many more...
Enumerations - Fixed sets of values (PhaseCode, UnitSymbol, etc.)
Each SimpleType includes:
- A base unit (e.g., volts for Voltage)
- A multiplier (e.g., k for kilo, M for mega)
- Conversion rules between units
Units Implementation in CIM-Graph¶
As part of the CIMTool data profile creation process, dataclasses for all relevant units are created for the custom profile and mapped to Python Pint quantity classes.
Pint Integration¶
Python Pint is a package that defines, operates, and manipulates physical quantities. Key features:
- Unit-aware arithmetic - Automatically handles unit conversions in calculations
- Dimensional analysis - Prevents invalid operations (e.g., adding volts to amperes)
- Multiple unit systems - Support for SI, imperial, and custom units
- String parsing - Create quantities from strings like "230 kV"
- Output formatting - Display in any compatible unit
How It Works in CIMantic Graphs:¶
- CIMTool builder generates unit dataclasses (e.g.,
Voltage,ActivePower) - Each unit class wraps a Pint
Quantity - You can create quantities using convenient constructors
- Values are automatically converted to base SI units internally
- You can retrieve values in any compatible unit
from mermaid import Mermaid
from cimgraph import utils
import cimgraph.data_profile.cim17v40 as cim
Mermaid(utils.get_mermaid(cim.Voltage))
cim.Voltage(value=230.0, input_unit='kV')
230000.0 volt
# Create a line with resistance and reactance
line = cim.ACLineSegment(
mRID='_line-001',
name='Feeder_Line_1',
r=cim.Resistance(value=0.185, input_unit='ohm'), # 0.185 Ω
x=cim.Reactance(value=0.382, input_unit='ohm'), # 0.382 Ω
length=cim.Length(value=2.5, input_unit='km') # 2.5 km
)
print("Line created with units:")
print(f" Resistance: {line.r.to('ohm')}")
print(f" Reactance: {line.x.to('ohm')}")
print(f" Length: {line.length.to('km')}")
print(f" Length in miles: {line.length.to('mi')}")
# Create a transformer with rated values
transformer = cim.PowerTransformer(
mRID='_xfmr-001',
name='Substation_Transformer',
ratedU=cim.Voltage(value=138.0, input_unit='kV'),
ratedS=cim.ApparentPower(value=50.0, input_unit='MVA')
)
print(f"\\nTransformer ratings:")
print(f" Voltage: {transformer.ratedU.to('kV')}")
print(f" Power: {transformer.ratedS.to('MVA')}")
Using Units in CIM Objects¶
When creating CIM equipment objects, use unit quantities for physical attributes:
# Addition with automatic unit conversion
v1 = cim.Voltage(value=12.47, input_unit='kV')
v2 = cim.Voltage(value=7200, input_unit='V') # 7.2 kV
v_total = v1 + v2
print(f"{v1} + {v2} = {v_total}")
print(f"Total in kV: {v_total.to('kV')}")
# Multiplication (Ohm's law: V = I * R)
current = cim.CurrentFlow(value=100.0, input_unit='A')
resistance = cim.Resistance(value=0.5, input_unit='ohm')
voltage_drop = current * resistance
print(f"\\nVoltage drop: {current} × {resistance} = {voltage_drop}")
# Division
power = cim.ActivePower(value=100.0, input_unit='kW')
voltage = cim.Voltage(value=480.0, input_unit='V')
# Note: CIM has separate Power and Current types
# This shows how Pint handles dimensional analysis
print(f"\\n{power} ÷ {voltage} = {(power / voltage).to('A')}")
Unit-Aware Arithmetic¶
Pint automatically handles unit conversions in arithmetic operations:
# Create a voltage in kV
voltage = cim.Voltage(value=138.0, input_unit='kV')
# Convert to different units
print(f"Original: {voltage}")
print(f"In volts: {voltage.to('V')}")
print(f"In megavolts: {voltage.to('MV')}")
print(f"In kilovolts: {voltage.to('kV')}")
# Get numeric value in specific unit
kv_value = voltage.to('kV')
print(f"\\nNumeric value in kV: {kv_value}")
# Create power quantities
power = cim.ActivePower(value=50.0, input_unit='MW')
print(f"\\nPower: {power}")
print(f"Power in kW: {power.to('kW')}")
print(f"Power in GW: {power.to('GW')}")
Unit Conversion¶
Convert between compatible units easily:
# Method 1: Specify value and input unit
v1 = cim.Voltage(value=230.0, input_unit='kV')
print(f"Method 1: {v1}")
# Method 2: Just provide value (assumes base unit - volts)
v2 = cim.Voltage(value=230000.0)
print(f"Method 2: {v2}")
# Method 3: From string
v3 = cim.Voltage(value=12.47, input_unit='kV')
print(f"Method 3: {v3}")
# All are equivalent when compared
print(f"\\nAll represent the same voltage: {v1.to('kV') == v3}")
Creating Quantities¶
There are multiple ways to create unit quantities:
The CIMUnit Base Class¶
All unit quantities in CIMantic Graphs inherit from the CIMUnit base class, which provides sophisticated Pint-based unit handling with CIM-specific extensions.
CIMUnit Architecture:¶
class CIMUnit:
value: float # Magnitude in base unit
unit: UnitSymbol # CIM unit symbol (V, W, Hz, etc.)
multiplier: UnitMultiplier # k, M, G, etc.
quantity: pint.Quantity # Full Pint quantity object
Key Features:¶
Custom CIM Unit Registry - Extends Pint with power engineering units:
VAr(Volt-Ampere Reactive) for reactive powerVArh(VAr-hours) for reactive energyQ(reactive power using imaginary notation)- Custom compound units for power systems
Automatic Conversion - The
__pint__method handles:- Input unit to base unit conversion
- Unit multiplier application (k, M, G, etc.)
- CIMUnit to CIMUnit conversion
- Pint Quantity to magnitude extraction
Flexible Initialization - Create units from:
- Value + input_unit:
Voltage(230.0, 'kV') - Value + multiplier + unit:
Voltage(230.0, input_multiplier='k', input_unit='V') - Another CIMUnit:
Voltage(other_voltage) - Pint Quantity:
Voltage(ureg.Quantity(230, 'kV'))
- Value + input_unit:
Type Conversion - Supports Python numeric types:
float(voltage)→ magnitude as floatint(voltage)→ magnitude as intstr(voltage)→ formatted with unitrepr(voltage)→ Pint representation
Key Patterns for Unit Calculations¶
The examples above demonstrate common patterns for working with CIM units:
Create CIM quantities with appropriate units
power = cim.ActivePower(value=50.0, input_unit='MW') voltage = cim.Voltage(value=138.0, input_unit='kV')
Access
.quantityfor Pint arithmeticresult = power.quantity / voltage.quantity
Pint handles dimensional analysis automatically
- MVAr / kV² → Siemens (susceptance)
- MVA / kV → Amperes (current)
- Ω / km → Ω/km (per-unit-length impedance)
- MW × h → MWh (energy)
Wrap calculated results back in CIMUnit
susceptance = cim.Susceptance(calculated_value) current = cim.CurrentFlow(calculated_value)
Convert results to desired display units
print(f"Current: {current.to('kA'):.2f} kA") print(f"Energy: {energy.to('MWh'):.1f} MWh")
This approach ensures:
- Type safety - Wrong dimensions caught at calculation time
- Unit consistency - Automatic conversion prevents errors
- Code clarity - Units explicit in calculations
- Flexibility - Easy conversion for display or export
# Example: Calculate energy consumption and costs
# Load data
load_power_mw = 25.0 # MW average load
duration_hours = 24.0 # hours (one day)
cost_per_kwh = 0.12 # $/kWh
# Create CIM quantities
power = cim.ActivePower(value=load_power_mw, input_unit='MW')
time = cim.Time(value=duration_hours, input_unit='h')
# Calculate energy: E = P × t
energy = power.quantity * time.quantity
energy_cim = cim.RealEnergy(energy)
print(f"Load Profile:")
print(f" Average Power: {power.to('MW')} MW")
print(f" Duration: {time.to('h')} hours")
print(f"\\nEnergy Consumption:")
print(f" {energy_cim.to('MWh'):.1f} MWh")
print(f" {energy_cim.to('kWh'):.0f} kWh")
print(f" {energy_cim.to('J'):.2e} J (Joules)")
# Calculate cost
energy_kwh = energy_cim.to('kWh')
cost = energy_kwh * cost_per_kwh
print(f"\\nCost: ${cost:,.2f}")
# Reactive energy example
reactive_power = cim.ReactivePower(value=10.0, input_unit='MVAr')
reactive_energy = reactive_power.quantity * time.quantity
reactive_energy_cim = cim.ReactiveEnergy(reactive_energy)
print(f"\\nReactive Energy:")
print(f" {reactive_energy_cim.to('MVArh'):.1f} MVArh")
print(f" {reactive_energy_cim.to('kVArh'):.0f} kVArh")
Energy Calculations and Time Integration¶
Calculate energy from power over time, or convert between energy units:
Formula: E = P × t
where E is energy (Wh), P is power (W), and t is time (h)
# Example: Convert transformer impedance to per-unit
# System base values
s_base_mva = 100.0 # MVA
v_base_kv = 138.0 # kV
# Transformer data
transformer_z_ohm = 15.0 # Ω (actual impedance)
# Create CIM base quantities
s_base = cim.ApparentPower(value=s_base_mva, input_unit='MVA')
v_base = cim.Voltage(value=v_base_kv, input_unit='kV')
# Calculate base impedance: Z_base = V² / S
z_base = (v_base.quantity ** 2) / s_base.quantity
z_base_cim = cim.Resistance(z_base)
print(f"System Base Values:")
print(f" S_base = {s_base.to('MVA')} MVA")
print(f" V_base = {v_base.to('kV')} kV")
print(f" Z_base = {z_base_cim.to('ohm'):.2f} Ω")
# Transformer impedance
z_actual = cim.Resistance(value=transformer_z_ohm, input_unit='ohm')
# Calculate per-unit impedance (dimensionless)
z_pu = z_actual.quantity / z_base
print(f"\\nTransformer Impedance:")
print(f" Z_actual = {z_actual.to('ohm')} Ω")
print(f" Z_pu = {z_pu.magnitude:.4f} per-unit")
# Can also go backwards: convert per-unit to ohms
z_pu_value = 0.1 # 10% impedance
z_ohms = z_pu_value * z_base
z_ohms_cim = cim.Resistance(z_ohms)
print(f"\\nReverse Calculation:")
print(f" Z_pu = {z_pu_value} per-unit")
print(f" Z_ohms = {z_ohms_cim.to('ohm'):.2f} Ω")
Per-Unit System Conversions¶
Convert physical quantities to per-unit (dimensionless) values:
Formula: X_pu = X_actual / X_base
Common base quantities:
- S_base: Base apparent power (VA)
- V_base: Base voltage (V)
- Z_base = V_base² / S_base: Base impedance (Ω)
- I_base = S_base / V_base: Base current (A)
# Example: Calculate per-unit-length impedance for a line segment
# Line segment data
total_resistance_ohm = 1.85 # Ω
total_reactance_ohm = 3.82 # Ω
line_length_km = 10.0 # km
# Create CIM quantities
total_r = cim.Resistance(value=total_resistance_ohm, input_unit='ohm')
total_x = cim.Reactance(value=total_reactance_ohm, input_unit='ohm')
length = cim.Length(value=line_length_km, input_unit='km')
# Calculate per-unit-length values
# Division of Ω by meters gives Ω/m
r_per_length = total_r.quantity / length.quantity
x_per_length = total_x.quantity / length.quantity
# Wrap results in ResistancePerLength CIMUnit
r_per_unit = cim.ResistancePerLength(r_per_length)
x_per_unit = cim.ReactancePerLength(x_per_length)
print(f"Total Line Impedance:")
print(f" R = {total_r.to('ohm')} Ω")
print(f" X = {total_x.to('ohm')} Ω")
print(f" Length = {length.to('km')} km")
print(f"\\nPer-Unit-Length Impedance:")
print(f" r = {r_per_unit.to('ohm/km'):.4f} Ω/km")
print(f" x = {x_per_unit.to('ohm/km'):.4f} Ω/km")
Calculating Line Impedance Per Unit Length¶
Convert total line impedance to per-unit-length values:
Formula: r = R_total / length, x = X_total / length
where r, x are resistance and reactance per unit length (Ω/m)
# Example: Calculate rated current for a transformer
import math
# Transformer ratings
transformer_mva = 50.0 # MVA
voltage_kv = 138.0 # kV line-to-line
# Create CIM quantities
apparent_power = cim.ApparentPower(value=transformer_mva, input_unit='MVA')
voltage = cim.Voltage(value=voltage_kv, input_unit='kV')
# Calculate current: I = S / (√3 × V)
# For three-phase systems
sqrt3 = math.sqrt(3)
i = apparent_power.quantity / (sqrt3 * voltage.quantity)
# Wrap result in CurrentFlow CIMUnit
rated_current = cim.CurrentFlow(i)
print(f"Transformer Rating: {apparent_power.to('MVA')} MVA")
print(f"Voltage: {voltage.to('kV')} kV")
print(f"Rated Current: {rated_current.to('A'):.1f} A")
print(f"Rated Current: {rated_current.to('kA'):.3f} kA")
Calculating Current from Power and Voltage¶
For three-phase systems, calculate current from apparent power:
Formula: I = S / (√3 × V)
where I is current (A), S is apparent power (VA), and V is line-to-line voltage (V)
# Example: Calculate capacitor susceptance from reactive power rating
# Typical shunt capacitor bank data
capacitor_mvar = 1.2 # MVAr rating
base_voltage_kv = 12.47 # kV nominal voltage
# Create CIM quantities
reactive_power = cim.ReactivePower(value=capacitor_mvar, input_unit='MVAr')
base_voltage = cim.Voltage(value=base_voltage_kv, input_unit='kV')
# Calculate susceptance: B = Q / V²
# Access .quantity to perform Pint arithmetic
b = reactive_power.quantity / (base_voltage.quantity ** 2)
# Wrap result in Susceptance CIMUnit
capacitor_susceptance = cim.Susceptance(b)
print(f"Capacitor Rating: {reactive_power}")
print(f"Base Voltage: {base_voltage.to('kV')} kV")
print(f"Calculated Susceptance: {capacitor_susceptance}")
print(f"Susceptance in microsiemens: {capacitor_susceptance.to('microsiemens'):.2f} µS")
Practical Power System Calculations¶
Real power system analysis often requires converting between different quantity types through calculations. Here are common examples:
Calculating Susceptance from Reactive Power¶
Shunt capacitor susceptance is calculated from reactive power and voltage:
Formula: B = Q / V²
where B is susceptance (Siemens), Q is reactive power (VAr), and V is voltage (V)
Summary¶
CIMantic Graphs provides sophisticated, type-safe unit handling for power system modeling:
Key Features:¶
- CIMUnit Base Class - All unit quantities inherit from CIMUnit with Pint integration
- Custom CIM Units - Extended Pint registry with power engineering units (VAr, VAh, Q, etc.)
- Automatic Conversion - Seamless conversion between compatible units
- Dimensional Analysis - Prevents mixing incompatible quantities (e.g., watts + vars)
- XML Serialization - Exports only magnitude in base units per CIM standard
- Type Safety - Full Python type hints and IDE support
Unit Architecture:¶
CIMUnit (base class)
├── Pint UnitRegistry (custom CIM units)
├── UnitSymbol enum (V, W, Hz, VAr, etc.)
├── UnitMultiplier enum (k, M, G, etc.)
└── Generated unit classes (Voltage, ActivePower, etc.)
Common Unit Types:¶
- Voltage: V, kV, MV
- Active Power: W, kW, MW, GW
- Reactive Power: VAr, kVAr, MVAr
- Apparent Power: VA, kVA, MVA
- Current: A, kA
- Resistance/Reactance: ohm, mΩ
- Length: m, km, ft, mi
- Frequency: Hz, kHz
Benefits:¶
- Eliminates Unit Errors - Automatic conversion prevents manual mistakes
- Improves Code Clarity - Units are explicit in code:
Voltage(230, 'kV') - Maintains CIM Compliance - Follows CIM standards for unit representation
- Enables Validation - Dimensional analysis catches logic errors early
- Supports Analysis - Proper handling of complex power (P, Q, S)
Going Forward:¶
With comprehensive unit support, you can now:
- Build accurate power system models
- Perform calculations with automatic unit conversion
- Exchange models with other CIM-compliant systems
- Avoid common unit conversion errors
- Leverage Pint's full capabilities for advanced analysis
The combination of CIM profiles, incremental updates, and type-safe units provides a robust foundation for sophisticated power system applications!
Best Practices for Units¶
1. Always Use Appropriate Units for Input¶
Choose input units that match your data source:
# Good - matches typical utility data
voltage = cim.Voltage(value=12.47, input_unit='kV')
power = cim.ActivePower(value=5.5, input_unit='MW')
# Avoid - requires unnecessary scaling
voltage = cim.Voltage(value=12470.0, input_unit='V') # Less readable
2. Let Pint Handle Conversions¶
Don't manually convert units - let the library handle it:
# Good - automatic conversion
v_kV = cim.Voltage(value=138.0, input_unit='kV')
v_V = v_kV.to('V') # Pint handles conversion
# Avoid - manual conversion prone to errors
v_kV = 138.0
v_V = v_kV * 1000 # Error-prone!
3. Use Correct Power Types¶
Use the appropriate power type for your application:
# Active power (real power) - watts
p = cim.ActivePower(value=100.0, input_unit='MW')
# Reactive power - VARs
q = cim.ReactivePower(value=50.0, input_unit='MVAr')
# Apparent power - VA
s = cim.ApparentPower(value=111.8, input_unit='MVA')
4. Document Unit Assumptions¶
When importing data, document unit assumptions:
# Document expected units when importing external data
def import_transformer_data(kV_rating, MVA_rating):
"""
Import transformer ratings.
Args:
kV_rating: Voltage rating in kilovolts
MVA_rating: Power rating in megavolt-amperes
"""
return cim.PowerTransformer(
ratedU=cim.Voltage(value=kV_rating, input_unit='kV'),
ratedS=cim.ApparentPower(value=MVA_rating, input_unit='MVA')
)
5. Validate Unit Compatibility¶
When combining quantities, ensure dimensional compatibility:
# Good - compatible dimensions
v1 = cim.Voltage(value=12.47, input_unit='kV')
v2 = cim.Voltage(value=4.16, input_unit='kV')
v_total = cim.Voltage(value=v1.quantity + v2.quantity)
# Pint will raise error for incompatible dimensions
# This would fail: v1.quantity + active_power.quantity
6. Access Magnitude for Pure Calculations¶
For numerical computations, extract magnitude:
voltage = cim.Voltage(value=230.0, input_unit='kV')
current = cim.CurrentFlow(value=100.0, input_unit='A')
# Extract magnitudes for calculation
V = voltage.quantity.to('V').magnitude
I = current.quantity.to('A').magnitude
# Calculate (example)
power_loss = V * I * 0.01 # 1% loss
# Wrap result back in CIMUnit
loss = cim.ActivePower(value=power_loss, input_unit='W')
Advanced Unit Operations¶
Accessing the Underlying Pint Quantity¶
For advanced operations, access the Pint quantity attribute directly:
voltage = cim.Voltage(value=138.0, input_unit='kV')
# Access Pint quantity for full Pint capabilities
pint_qty = voltage.quantity
# Pint operations
print(pint_qty.dimensionality) # [length]^2 * [mass] / [current] / [time]^3
print(pint_qty.to_base_units()) # Convert to base SI
print(pint_qty.to_compact()) # Use most compact representation
Converting Between CIMUnit Types¶
You can convert between compatible CIMUnit types:
# Create a voltage from another voltage
v1 = cim.Voltage(value=12.47, input_unit='kV')
v2 = cim.Voltage(value=v1) # Copy with conversion
# Convert units during copy
v3 = cim.Voltage(value=v1.to('V'), input_unit='V')
Using Multipliers Explicitly¶
CIM multipliers can be specified separately:
# These are equivalent:
v1 = cim.Voltage(value=138.0, input_unit='kV')
v2 = cim.Voltage(value=138.0, input_unit='V', input_multiplier='k')
# Multiplier is stored in the object
print(v2.multiplier) # UnitMultiplier.k
print(v2.unit) # UnitSymbol.V
Type Checking and Validation¶
Use isinstance to check if a value is a unit:
from cimgraph.data_profile.units.units import CIMUnit
value = cim.Voltage(value=230.0, input_unit='kV')
if isinstance(value, CIMUnit):
print(f"Unit value: {value}")
print(f"Magnitude: {value.quantity.magnitude}")
print(f"Base unit: {value.unit}")
XML Serialization and Units¶
When exporting CIM models to XML, units are handled specially to maintain CIM compliance and minimize file size.
How Units Are Serialized:¶
When a CIMUnit attribute is encountered during XML export, only the magnitude in base units is written to the XML file:
# In cimgraph/utils/write_xml.py (line 102-103)
elif isinstance(edge, CIMUnit):
row = f'<{ns_prefix}:{parent.__name__}.{attribute}>{str(edge.quantity.magnitude)}</{ns_prefix}:{parent.__name__}.{attribute}>\n'
f.write(row)
Example:¶
# Python object
line = cim.ACLineSegment(
mRID='_line-001',
r=cim.Resistance(value=0.185, input_unit='ohm'),
length=cim.Length(value=2.5, input_unit='km')
)
# XML output
<cim:ACLineSegment rdf:ID="_line-001">
<cim:ACLineSegment.r>0.185</cim:ACLineSegment.r>
<cim:ACLineSegment.length>2500.0</cim:ACLineSegment.length>
</cim:ACLineSegment>
Notice:
- Resistance is written as
0.185(in base ohms) - Length is written as
2500.0(converted from 2.5 km to base meters) - No unit labels in XML - the CIM profile schema defines the expected units
Why Only Magnitude?¶
- CIM Standard Compliance - CIM XML schema specifies units in the profile, not in instance data
- File Size - Avoids repetitive unit labels for every attribute
- Consistency - All values in a given attribute must use the same unit
- Simplicity - Parsers don't need to handle unit conversion during import
Import Behavior:¶
When importing XML, CIMantic Graphs assumes values are in base SI units:
# XML contains: <cim:ACLineSegment.r>0.185</cim:ACLineSegment.r>
# Imported as:
line.r = cim.Resistance(value=0.185, input_unit='ohm') # Base unit
If your XML uses different units, you must specify the import unit mapping or convert during import.
# Create active and reactive power quantities
active_power = cim.ActivePower(value=10.0, input_unit='MW')
reactive_power = cim.ReactivePower(value=5.0, input_unit='MVAr')
print(f"Active Power: {active_power}")
print(f"Reactive Power: {reactive_power}")
# Calculate apparent power using Pythagorean theorem: S = sqrt(P² + Q²)
# Note: Direct arithmetic between P and Q requires accessing the quantity
import math
P_magnitude = active_power.quantity.magnitude
Q_magnitude = reactive_power.quantity.magnitude
S_magnitude = math.sqrt(P_magnitude**2 + Q_magnitude**2)
apparent_power = cim.ApparentPower(value=S_magnitude, input_unit='MVA')
print(f"\\nApparent Power: {apparent_power}")
# Calculate power factor: PF = P / S
power_factor = P_magnitude / S_magnitude
print(f"Power Factor: {power_factor:.3f}")
# Convert to different units
print(f"\\nActive Power in kW: {active_power.to('kW')} kW")
print(f"Reactive Power in kVAr: {reactive_power.to('kVAr')} kVAr")
print(f"Apparent Power in VA: {apparent_power.to('VA')} VA")
Working with Reactive Power¶
Let's see how the custom VAr units work in practice:
Custom CIM Units for Power Systems¶
CIMantic Graphs extends Pint with custom power engineering units defined in cim_units/units.txt. These units are essential for power system analysis but not included in standard Pint.
Key Custom Units:¶
Reactive Power and Energy:¶
VAr = watt * imaginary # Volt-Ampere Reactive
VArh = watt * hour * imaginary # VAr-hours (reactive energy)
Q = imaginary * watt # Reactive power (Q notation)
Qh = imaginary * watt * hour # Reactive energy
The imaginary dimension enables proper dimensional analysis for reactive power, treating it as distinct from active power while maintaining mathematical relationships.
Apparent Power:¶
VA = volt * amp # Volt-Ampere (apparent power)
VAh = volt * amp * hour # VA-hours (apparent energy)
Complex Power Relationships:¶
S = P + jQ
where:
S = Apparent power (VA)
P = Active power (W)
Q = Reactive power (VAr)
j = imaginary unit
Pint's dimensional analysis ensures:
- You can't accidentally add watts to VARs
- Power factor calculations use correct units
- Energy conversions maintain proper dimensions
Other Power Engineering Units:¶
- Voltage Squared:
V2 = volt * volt(for regulation calculations) - Voltage-Hours:
Vh = volt * hour(for energy storage) - Per-Unit Quantities:
VPerV = volt / volt,WPerW = watt / watt - Power Density:
WPerm2 = watt / meter / meter - Various Ratios:
APerA,HzPerHz, etc.
Unit Dataclass Structure¶
The CIMTool builder generates unit dataclasses for each physical quantity type in your profile. Here's the structure:
@stereotype(CIMStereotype.CIMDatatype)
@dataclass(repr=False)
class Voltage(CIMUnit):
'''
Electrical voltage, can be both AC and DC.
'''
value: float = field(default=None)
multiplier: UnitMultiplier = field(default=UnitMultiplier.none)
@property # read-only
def unit(self):
return UnitSymbol.V
def __init__(self, value, input_unit: str='V', input_multiplier: str=None):
self.__pint__(value=value, input_unit=input_unit, input_multiplier=input_multiplier)
Key Components:¶
- @stereotype(CIMStereotype.CIMDatatype) - Marks this as a CIM datatype (not a class)
- @dataclass(repr=False) - Python dataclass with custom
__repr__ - value field - Stores the magnitude in base unit
- multiplier field - Stores the unit multiplier (k, M, G, etc.)
- unit property - Returns the base UnitSymbol (read-only)
- init - Calls
__pint__from CIMUnit to initialize the Pint quantity
Unit Symbols and Multipliers:¶
CIM defines standard enumerations for units and multipliers:
UnitSymbol - Base units like V, W, Hz, ohm, A, VAr, etc.
UnitMultiplier - SI multipliers: k, M, G, T, m, micro, n, etc.