TemperatureOfTwoMassesTest
Test component for simulating heat exchange between two masses and verifying temperature sensor readings.
This component models the thermal interaction of two distinct masses, mass1
and mass2
, each represented by a HeatCapacitor
with specified heat capacities (C
) and initial temperatures (T0
). A ThermalConductor
facilitates heat transfer between the two masses. The system's behavior is monitored using a RelativeTemperatureSensor
to measure the temperature difference across the masses, and two TemperatureSensor
s to read the absolute temperature of each mass individually. The component also calculates the theoretical final equilibrium temperature, final_T
, that the combined system will reach, assuming no heat loss to the environment. This final temperature is determined by the conservation of energy, expressed as:
The connect
statements define the acausal thermal connections between the masses, the conductor, and the sensors. The metadata reveals this component is intended for testing the functionality of the temperature sensors and the heat exchange model under defined conditions.
Usage
TemperatureOfTwoMassesTest()
Variables
Name | Description | Units |
---|---|---|
final_T | Projected final equilibrium temperature of the two masses. | K |
Behavior
using ThermalComponents #hide
using ModelingToolkit #hide
@named sys = TemperatureOfTwoMassesTest() #hide
full_equations(sys) #hide
<< @example-block not executed in draft mode >>
Source
# Test component for simulating heat exchange between two masses and verifying temperature sensor readings.
#
# This component models the thermal interaction of two distinct masses, `mass1` and `mass2`, each represented by a `HeatCapacitor` with
# specified heat capacities (`C`) and initial temperatures (`T0`). A `ThermalConductor` facilitates heat transfer between the two masses.
# The system's behavior is monitored using a `RelativeTemperatureSensor` to measure the temperature difference across the masses,
# and two `TemperatureSensor`s to read the absolute temperature of each mass individually. The component also calculates the theoretical
# final equilibrium temperature, `final_T`, that the combined system will reach, assuming no heat loss to the environment. This final
# temperature is determined by the conservation of energy, expressed as:
# ```math
# final\_T = \frac{mass1.T0 \cdot mass1.C + mass2.T0 \cdot mass2.C}{mass1.C + mass2.C}
# ```
# The `connect` statements define the acausal thermal connections between the masses, the conductor, and the sensors. The metadata reveals this component is intended for testing the functionality
# of the temperature sensors and the heat exchange model under defined conditions.
test component TemperatureOfTwoMassesTest
# First heat capacitor with initial temperature T0 = 373.15 K and heat capacity C = 15 J/K.
mass1 = HeatCapacitor(C=15, T0=373.15)
# Second heat capacitor with initial temperature T0 = 273.15 K and heat capacity C = 15 J/K.
mass2 = HeatCapacitor(C=15, T0=273.15)
# Thermal conductor with thermal conductance G = 10 W/K connecting the two masses.
conduction = ThermalConductor(G=10)
# Sensor to measure the relative temperature difference between mass1 and mass2.
relative_temperature_sensor = RelativeTemperatureSensor()
# Sensor to measure the absolute temperature of mass1.
temperature_sensor1 = TemperatureSensor()
# Sensor to measure the absolute temperature of mass2.
temperature_sensor2 = TemperatureSensor()
# Projected final equilibrium temperature of the two masses.
variable final_T::Temperature
relations
final_T = (mass1.T0*mass1.C+mass2.T0*mass2.C)/(mass1.C+mass2.C)
connect(mass1.node, conduction.node_a)
connect(conduction.node_b, mass2.node)
connect(mass1.node, relative_temperature_sensor.node_a)
connect(mass2.node, relative_temperature_sensor.node_b)
connect(mass1.node, temperature_sensor1.node)
connect(mass2.node, temperature_sensor2.node)
metadata {
"Dyad": {
"tests": {
"case1": {
"stop": 10,
"atol": {
"mass1.T": 0.001,
"mass2.T": 0.001,
"temperature_sensor1.T": 0.001,
"temperature_sensor2.T": 0.001,
"relative_temperature_sensor.T_rel": 0.001
},
"expect": {
"initial": {
"temperature_sensor1.T": 373.15,
"temperature_sensor2.T": 273.15,
"relative_temperature_sensor.T_rel": 100
},
"signals": [
"mass1.node.T",
"mass2.node.T",
"temperature_sensor1.T",
"temperature_sensor2.T",
"relative_temperature_sensor.T_rel"
]
}
}
}
}
}
end
Flattened Source
# Test component for simulating heat exchange between two masses and verifying temperature sensor readings.
#
# This component models the thermal interaction of two distinct masses, `mass1` and `mass2`, each represented by a `HeatCapacitor` with
# specified heat capacities (`C`) and initial temperatures (`T0`). A `ThermalConductor` facilitates heat transfer between the two masses.
# The system's behavior is monitored using a `RelativeTemperatureSensor` to measure the temperature difference across the masses,
# and two `TemperatureSensor`s to read the absolute temperature of each mass individually. The component also calculates the theoretical
# final equilibrium temperature, `final_T`, that the combined system will reach, assuming no heat loss to the environment. This final
# temperature is determined by the conservation of energy, expressed as:
# ```math
# final\_T = \frac{mass1.T0 \cdot mass1.C + mass2.T0 \cdot mass2.C}{mass1.C + mass2.C}
# ```
# The `connect` statements define the acausal thermal connections between the masses, the conductor, and the sensors. The metadata reveals this component is intended for testing the functionality
# of the temperature sensors and the heat exchange model under defined conditions.
test component TemperatureOfTwoMassesTest
# First heat capacitor with initial temperature T0 = 373.15 K and heat capacity C = 15 J/K.
mass1 = HeatCapacitor(C=15, T0=373.15)
# Second heat capacitor with initial temperature T0 = 273.15 K and heat capacity C = 15 J/K.
mass2 = HeatCapacitor(C=15, T0=273.15)
# Thermal conductor with thermal conductance G = 10 W/K connecting the two masses.
conduction = ThermalConductor(G=10)
# Sensor to measure the relative temperature difference between mass1 and mass2.
relative_temperature_sensor = RelativeTemperatureSensor()
# Sensor to measure the absolute temperature of mass1.
temperature_sensor1 = TemperatureSensor()
# Sensor to measure the absolute temperature of mass2.
temperature_sensor2 = TemperatureSensor()
# Projected final equilibrium temperature of the two masses.
variable final_T::Temperature
relations
final_T = (mass1.T0*mass1.C+mass2.T0*mass2.C)/(mass1.C+mass2.C)
connect(mass1.node, conduction.node_a)
connect(conduction.node_b, mass2.node)
connect(mass1.node, relative_temperature_sensor.node_a)
connect(mass2.node, relative_temperature_sensor.node_b)
connect(mass1.node, temperature_sensor1.node)
connect(mass2.node, temperature_sensor2.node)
metadata {
"Dyad": {
"tests": {
"case1": {
"stop": 10,
"atol": {
"mass1.T": 0.001,
"mass2.T": 0.001,
"temperature_sensor1.T": 0.001,
"temperature_sensor2.T": 0.001,
"relative_temperature_sensor.T_rel": 0.001
},
"expect": {
"initial": {
"temperature_sensor1.T": 373.15,
"temperature_sensor2.T": 273.15,
"relative_temperature_sensor.T_rel": 100
},
"signals": [
"mass1.node.T",
"mass2.node.T",
"temperature_sensor1.T",
"temperature_sensor2.T",
"relative_temperature_sensor.T_rel"
]
}
}
}
}
}
end
Test Cases
using ThermalComponents
using ModelingToolkit, OrdinaryDiffEqDefault
using Plots
using CSV, DataFrames
snapshotsdir = joinpath(dirname(dirname(pathof(ThermalComponents))), "test", "snapshots")
<< @setup-block not executed in draft mode >>
Test Case case1
@mtkbuild model_case1 = TemperatureOfTwoMassesTest()
u0_case1 = []
prob_case1 = ODEProblem(model_case1, u0_case1, (0, 10))
sol_case1 = solve(prob_case1)
<< @setup-block not executed in draft mode >>
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.mass1.node.T])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TemperatureOfTwoMassesTest_case1_sig0.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.mass1.node.T], width=2, label="Actual value of mass1.node.T")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of mass1.node.T")
end
<< @setup-block not executed in draft mode >>
plt
<< @example-block not executed in draft mode >>
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.mass2.node.T])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TemperatureOfTwoMassesTest_case1_sig1.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.mass2.node.T], width=2, label="Actual value of mass2.node.T")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of mass2.node.T")
end
<< @setup-block not executed in draft mode >>
plt
<< @example-block not executed in draft mode >>
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.temperature_sensor1.T])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TemperatureOfTwoMassesTest_case1_sig2.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.temperature_sensor1.T], width=2, label="Actual value of temperature_sensor1.T")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of temperature_sensor1.T")
end
scatter!(plt, [df_case1.t[1]], [373.15], label="Initial Condition for `temperature_sensor1.T`")
<< @setup-block not executed in draft mode >>
plt
<< @example-block not executed in draft mode >>
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.temperature_sensor2.T])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TemperatureOfTwoMassesTest_case1_sig3.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.temperature_sensor2.T], width=2, label="Actual value of temperature_sensor2.T")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of temperature_sensor2.T")
end
scatter!(plt, [df_case1.t[1]], [273.15], label="Initial Condition for `temperature_sensor2.T`")
<< @setup-block not executed in draft mode >>
plt
<< @example-block not executed in draft mode >>
df_case1 = DataFrame(:t => sol_case1[:t], :actual => sol_case1[model_case1.relative_temperature_sensor.T_rel])
dfr_case1 = try CSV.read(joinpath(snapshotsdir, "TemperatureOfTwoMassesTest_case1_sig4.ref"), DataFrame); catch e; nothing; end
plt = plot(sol_case1, idxs=[model_case1.relative_temperature_sensor.T_rel], width=2, label="Actual value of relative_temperature_sensor.T_rel")
if !isnothing(dfr_case1)
scatter!(plt, dfr_case1.t, dfr_case1.expected, mc=:red, ms=3, label="Expected value of relative_temperature_sensor.T_rel")
end
scatter!(plt, [df_case1.t[1]], [100], label="Initial Condition for `relative_temperature_sensor.T_rel`")
<< @setup-block not executed in draft mode >>
plt
<< @example-block not executed in draft mode >>
Related
Examples
Experiments
Analyses
Tests