Switch between ONAN and ONAF¶
In this example we show how to use a conditional ONAF transformer that switches between ONAN and ONAF.
Warning: The ONAN/ONAF switch feature has been validated only against a single measurement dataset. When running the model, temperatures near the activation or deactivation threshold may cause oscillating switching between cooling modes. This behavior does not necessarily reflect real transformer operation. Therefore, it is advised to not use the results on a timestep basis.
Warning: The ONAN/ONAF switch feature has been validated only against a single measurement dataset. When running the model, temperatures near the activation or deactivation threshold may cause oscillating switching between cooling modes. This behavior does not necessarily reflect real transformer operation. Therefore, it is advised to not use the results on a timestep basis.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from transformer_thermal_model.cooler import CoolerType
from transformer_thermal_model.model import Model
from transformer_thermal_model.schemas import (
InputProfile,
ThreeWindingInputProfile,
UserThreeWindingTransformerSpecifications,
UserTransformerSpecifications,
WindingSpecifications,
)
from transformer_thermal_model.schemas.thermal_model import CoolingSwitchConfig, CoolingSwitchSettings, ONANParameters
from transformer_thermal_model.transformer import PowerTransformer, ThreeWindingTransformer
top_oil_label = "Top-oil temperature"
hot_spot_label = "Hot-spot temperature"
ambient_temp_label = "Ambient temperature"
temperature_label = "Temperature [°C]"
fan_switch_label = "Fan switch"
We create an input profile similar to a regular simulation:
datetime_index = [pd.to_datetime("2025-01-01 00:00:00") + pd.Timedelta(minutes=15 * i) for i in np.arange(0, 288)]
load_series = pd.Series(data=np.sin(np.arange(0, 288) * 900 * 2 * np.pi * 1 / 43200) * 500 + 1200, index=datetime_index)
ambient_series = pd.Series(data=20, index=datetime_index)
# Create an input object with the profiles
my_profile_input = InputProfile.create(
datetime_index=datetime_index, load_profile=load_series, ambient_temperature_profile=ambient_series
)
Create the additional CoolingSwitchSettings object¶
To create a transformer with configurable fans, we create a 'normal' ONAF transformer, but with some extra input parameters. There are two ways the ONAN-ONAF switch can be configured:
- With an historical profile
- With a threhshold temperature, at witch the fans turn on and off.
Case 1: With an historical fan profile¶
TO make a transformer with an ONAN/ONAF configuration we create the parameter class "CoolingSwitchSettings", this has two parameters:
- onan_specs: ONANParameters. Her we pass along all ONAN paramters that are different when the fans are turned off.
- fan_on: List[bool]. A list with booleans that indicate whether the fans are ON(ONAF) or off(ONAN).
my_transformer_specifications = UserTransformerSpecifications(
load_loss=160000, # Transformer load loss [W]
nom_load_sec_side=3000, # Transformer nominal current secondary side [A]
no_load_loss=70000, # Transformer no-load loss [W]
amb_temp_surcharge=10, # Ambient temperature surcharge [K]
time_const_oil=150, # Time constant oil [min]
time_const_windings=7, # Time constant windings [min]
top_oil_temp_rise=50.5, # Top-oil temperature rise [K]
winding_oil_gradient=23, # Winding oil gradient (worst case) [K]
end_temp_reduction=0, # Lowering of the end temperature with respect to the current specification [K]
hot_spot_fac=1.2, # Hot-spot factor [-]
oil_const_k11=0.5, # Oil constant k11 [-]
winding_const_k21=2.0, # Winding constant k21 [-]
winding_const_k22=2.0, # Winding constant k22 [-]
oil_exp_x=0.8, # Oil exponent x [-]
winding_exp_y=1.3, # Winding exponent x [-]
)
onan_specs = ONANParameters(
top_oil_temp_rise=50.5,
time_const_oil=150,
time_const_windings=7,
load_loss=160000,
nom_load_sec_side=1600, # Lower nominal current for ONAN mode
winding_oil_gradient=23,
hot_spot_fac=1.2,
)
# Create a fan schedule where the fans are off for the first half of the time and on for the second half
is_on = [False] * len(datetime_index)
for i in range(len(is_on) // 2, len(is_on)):
is_on[i] = True
onaf_switch = CoolingSwitchSettings(fan_on=np.array(is_on), onan_parameters=onan_specs)
We then define a PowerTransformer with the additional parameter onaf_switch:
my_transformer = PowerTransformer(
user_specs=my_transformer_specifications, cooling_type=CoolerType.ONAF, cooling_switch_settings=onaf_switch
)
my_model = Model(temperature_profile=my_profile_input, transformer=my_transformer)
results = my_model.run()
You can clearly see that both the hotspot and topoil temperature drop when the fans turn on:
start_time = datetime_index[0] + pd.Timedelta(my_transformer.specs.time_const_oil * 5, "m")
fig = plt.figure()
ax = results.top_oil_temp_profile.loc[start_time::].plot(label=top_oil_label, color="green")
results.hot_spot_temp_profile.loc[start_time::].plot(label=hot_spot_label, color="blue")
ambient_series.loc[start_time::].plot(label=ambient_temp_label, color="red")
ax.set_ylabel(temperature_label)
ax.hlines(120, datetime_index[0], datetime_index[-1], linestyles="dashed", label="Hot-spot limit", color="blue")
ax.hlines(105, datetime_index[0], datetime_index[-1], linestyles="dashed", label="Top-oil limit", color="green")
ax.legend(loc="lower left")
plt.show()
Case 2: With a threshold temperature¶
To use a temperature threshold to turn on and off the fans we use the same CoolingSwitchSettings class, but instead off a fan_on list,
we provide a temperature_threshold, which should be contructed with the CoolingSwitchConfig class:
onaf_switch = CoolingSwitchSettings(
temperature_threshold=CoolingSwitchConfig(activation_temp=75, deactivation_temp=65),
onan_parameters=onan_specs,
)
my_transformer = PowerTransformer(
user_specs=my_transformer_specifications, cooling_type=CoolerType.ONAF, cooling_switch_settings=onaf_switch
)
my_model = Model(temperature_profile=my_profile_input, transformer=my_transformer)
results = my_model.run()
In the plots we can see that, as soon as the top-oil temperature reaches 75 degrees, the fans turn on and the temperature drops. Note that at a top-oil temperature of 65, the fans turn off again, and the temperature rises again.
start_time = datetime_index[0] + pd.Timedelta(my_transformer.specs.time_const_oil * 5, "m")
fig = plt.figure()
ax = results.top_oil_temp_profile.loc[start_time::].plot(label=top_oil_label, color="green")
results.hot_spot_temp_profile.loc[start_time::].plot(label="Hot-spot temperature", color="blue")
ambient_series.loc[start_time::].plot(label="Ambient temperature", color="red")
ax.set_ylabel("Temperature [C]")
ax.hlines(120, datetime_index[0], datetime_index[-1], linestyles="dashed", label="Hot-spot limit", color="blue")
ax.hlines(105, datetime_index[0], datetime_index[-1], linestyles="dashed", label="Top-oil limit", color="green")
ax.legend(loc="lower left")
plt.show()
ONAN ONAF for a three winding transformer¶
For three winding transformers, the principle is the same, but since the onan parameters are different, we have to use different schemas.
Create the load and the specs the same way as for a normal ONAF transformer:
# Define the time range for your simulation
datetime_index = [pd.to_datetime("2025-07-01 00:00:00") + pd.Timedelta(minutes=15 * i) for i in np.arange(0, 288)]
load_series_high = pd.Series(
data=np.sin(np.arange(0, 288) * 900 * 2 * np.pi * 1 / 43200) * 500 + 500, index=datetime_index
)
load_series_middle = pd.Series(
data=np.sin(np.arange(0, 288) * 900 * 2 * np.pi * 1 / 43200) * 500 + 500, index=datetime_index
)
load_series_low = pd.Series(
data=np.sin(np.arange(0, 288) * 900 * 2 * np.pi * 1 / 43200) * 500 + 500, index=datetime_index
)
ambient_series = pd.Series(data=20, index=datetime_index)
# Create the input profile for the three-winding transformer
profile_input = ThreeWindingInputProfile.create(
datetime_index=datetime_index,
ambient_temperature_profile=ambient_series,
load_profile_high_voltage_side=load_series_high,
load_profile_middle_voltage_side=load_series_middle,
load_profile_low_voltage_side=load_series_low,
)
# Define the transformer specifications for each winding
user_specs = UserThreeWindingTransformerSpecifications(
no_load_loss=20,
amb_temp_surcharge=10,
lv_winding=WindingSpecifications(
nom_load=1000, winding_oil_gradient=20, hot_spot_fac=1.2, time_const_winding=1, nom_power=1000
),
mv_winding=WindingSpecifications(
nom_load=1000, winding_oil_gradient=20, hot_spot_fac=1.2, time_const_winding=1, nom_power=1000
),
hv_winding=WindingSpecifications(
nom_load=2000, winding_oil_gradient=20, hot_spot_fac=1.2, time_const_winding=1, nom_power=2000
),
load_loss_hv_lv=100,
load_loss_hv_mv=100,
load_loss_mv_lv=100,
)
from transformer_thermal_model.schemas.thermal_model.onaf_switch import (
ThreeWindingCoolingSwitchSettings,
ThreeWindingONANParameters,
)
onan_parameters = ThreeWindingONANParameters(
lv_winding=WindingSpecifications(
time_const_winding=1, nom_load=900, winding_oil_gradient=20, hot_spot_fac=1.2, nom_power=1000
),
mv_winding=WindingSpecifications(
time_const_winding=1, nom_load=900, winding_oil_gradient=20, hot_spot_fac=1.2, nom_power=1000
),
hv_winding=WindingSpecifications(
time_const_winding=1, nom_load=1800, winding_oil_gradient=20, hot_spot_fac=1.2, nom_power=2000
),
top_oil_temp_rise=60,
time_const_oil=150,
load_loss_mv_lv=100,
load_loss_hv_lv=100,
load_loss_hv_mv=100,
)
# split in three parts: off, on, off
point_1 = 70
point_2 = 70
is_on = np.array(
[False] * point_1 + [True] * (len(profile_input.datetime_index) - point_1 - point_2) + [False] * point_2
)
onaf_switch = ThreeWindingCoolingSwitchSettings(
fan_on=is_on, temperature_threshold=None, onan_parameters=onan_parameters
)
transformer = ThreeWindingTransformer(
user_specs=user_specs, cooling_type=CoolerType.ONAF, cooling_switch_settings=onaf_switch
)
model = Model(transformer=transformer, temperature_profile=profile_input)
results = model.run()
full_onaf_transformer = ThreeWindingTransformer(user_specs=user_specs, cooling_type=CoolerType.ONAF)
full_onaf_model = Model(transformer=full_onaf_transformer, temperature_profile=profile_input)
full_onaf_results = full_onaf_model.run()
# Plot the top-oil temperature profile
fig1, ax1 = plt.subplots()
results.top_oil_temp_profile.plot(ax=ax1, label=top_oil_label)
full_onaf_results.top_oil_temp_profile.plot(ax=ax1, label="Full ONAF top-oil temperature", linestyle="dashed")
ax1.axvline(profile_input.datetime_index[point_1], color="black", linestyle="dotted", label=fan_switch_label)
ax1.axvline(
profile_input.datetime_index[len(profile_input.datetime_index) - point_2],
color="black",
linestyle="dotted",
label=fan_switch_label,
)
ax1.set_title(top_oil_label)
ax1.set_ylabel(temperature_label)
ax1.legend()
plt.show()
# Plot the hot-spot temperature profiles
fig2, ax2 = plt.subplots()
results.hot_spot_temp_profile.plot(ax=ax2)
ax2.axvline(profile_input.datetime_index[point_1], color="black", linestyle="dotted", label=fan_switch_label)
ax2.axvline(
profile_input.datetime_index[len(profile_input.datetime_index) - point_2],
color="black",
linestyle="dotted",
label=fan_switch_label,
)
ax2.set_title("Hot-spot Temperatures")
ax2.set_ylabel(temperature_label)
ax2.legend()
plt.show()
Three winding transformers with an ONAN/ONAF switch that uses threshold temperature:¶
# Create ONAF switch configuration with temperature thresholds
# Fans activate when top-oil temperature reaches 60°C, deactivate at 50°C
onaf_switch = ThreeWindingCoolingSwitchSettings(
temperature_threshold=CoolingSwitchConfig(activation_temp=60, deactivation_temp=50), onan_parameters=onan_parameters
)
# Create transformer with automatic ONAN/ONAF switching capability
# Starts in ONAF mode but will switch to ONAN if temperature drops below threshold
transformer = ThreeWindingTransformer(
user_specs=user_specs, cooling_type=CoolerType.ONAF, cooling_switch_settings=onaf_switch
)
# Run thermal model with automatic cooling mode switching
model = Model(transformer=transformer, temperature_profile=profile_input)
results = model.run()
# Create comparison transformer that stays in ONAF mode throughout the simulation
# This shows the difference between automatic switching and constant ONAF operation
full_onaf_transformer = ThreeWindingTransformer(user_specs=user_specs, cooling_type=CoolerType.ONAF)
full_onaf_model = Model(transformer=full_onaf_transformer, temperature_profile=profile_input)
full_onaf_results = full_onaf_model.run()
# Plot comparison of top-oil temperatures: automatic switching vs. constant ONAF
fig1, ax1 = plt.subplots()
# Transformer with automatic ONAN/ONAF switching based on temperature thresholds
results.top_oil_temp_profile.plot(ax=ax1, label=top_oil_label)
# Reference transformer that stays in ONAF mode throughout simulation
full_onaf_results.top_oil_temp_profile.plot(ax=ax1, label="Full ONAF top-oil temperature", linestyle="dashed")
ax1.set_title(top_oil_label)
ax1.set_ylabel(temperature_label)
ax1.legend()
plt.show()
# Plot hot-spot temperature profiles for all three windings
# Shows how each winding responds to the automatic cooling mode switching
fig2, ax2 = plt.subplots()
results.hot_spot_temp_profile.plot(ax=ax2)
ax2.set_title(hot_spot_label)
ax2.set_ylabel(temperature_label)
ax2.legend()
plt.show()