Spatially Distributed Heat Transfer

Our next example of creating reusable subsystems will introduce a slight twist. In this section we will not only demonstrate how to create a reusable subsystem model, as in the previous examples from this chapter, but that subsystem model will use an array of component instances where the size of that array can be used to control the spatial resolution of the results.

Flat System

Let’s start, as usual, with a flat system level model like the one shown below:

Flat version of our heat transfer system

This model consists of a collection of thermal capacitances with thermal conductors in between them. In this case, there are 3 thermal capacitances and 5 thermal conductors. On the left side, heat is applied to the system and on the right side a temperature sensor measures how the temperature of the rightmost thermal capacitance changes.

When implemented in Modelica, the model looks like this:

within ModelicaByExample.Subsystems.HeatTransfer.Examples;
model FlatRod "Modeling a heat transfer in a rod in a without subsystems"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow heating
    "Heating actuator"
    annotation (Placement(transformation(extent={{-60,50},{-40,70}})));
  Modelica.Blocks.Sources.Step bc(height=10, startTime=0.5) "Heat profile"
    annotation (Placement(transformation(extent={{-90,50},{-70,70}})));
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor C1(C=0.1, T(fixed=true))
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        origin={-60,2})));
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor G1(G=1.2)
    annotation (Placement(transformation(extent={{-40,-30},{-20,-10}})));
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor C2(C=0.1, T(fixed=true))
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        origin={0,2})));
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor G2(G=1.2)
    annotation (Placement(transformation(extent={{20,-30},{40,-10}})));
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor C3(C=0.1, T(fixed=true))
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        origin={60,2})));
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor sensor
    annotation (Placement(transformation(extent={{80,-30},{100,-10}})));
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor wall1(G=0.9)
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90, origin={-60,-40})));
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature ambient(T=293.15)
    "Ambient temperature" annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90, origin={0,-80})));
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor wall2(G=0.9)
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90, origin={0,-40})));
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor wall3(G=0.9)
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90, origin={60,-40})));
equation
  connect(bc.y, heating.Q_flow) annotation (Line(
      points={{-69,60},{-60,60}},
      color={0,0,127}, smooth=Smooth.None));
  connect(C1.port, G1.port_a) annotation (Line(
      points={{-60,-8},{-60,-20},{-40,-20}},
      color={191,0,0}, smooth=Smooth.None));
  connect(C2.port, G2.port_a) annotation (Line(
      points={{0,-8},{0,-20},{20,-20}},
      color={191,0,0}, smooth=Smooth.None));
  connect(G2.port_b, C3.port) annotation (Line(
      points={{40,-20},{60,-20},{60,-8}},
      color={191,0,0}, smooth=Smooth.None));
  connect(G1.port_b, C2.port) annotation (Line(
      points={{-20,-20},{0,-20},{0,-8}},
      color={191,0,0}, smooth=Smooth.None));
  connect(heating.port, C1.port) annotation (Line(
      points={{-40,60},{-30,60},{-30,20},{-80,20},{-80,-20},{-60,-20},{-60,-8}},
      color={191,0,0}, smooth=Smooth.None));
  connect(wall1.port_b, C1.port) annotation (Line(
      points={{-60,-30},{-60,-8}},
      color={191,0,0}, smooth=Smooth.None));
  connect(wall1.port_a, ambient.port) annotation (Line(
      points={{-60,-50},{-60,-60},{0,-60},{0,-70}},
      color={191,0,0}, smooth=Smooth.None));
  connect(wall2.port_a, ambient.port) annotation (Line(
      points={{0,-50},{0,-50},{0,-70}},
      color={191,0,0}, smooth=Smooth.None));
  connect(wall3.port_a, ambient.port) annotation (Line(
      points={{60,-50},{60,-60},{0,-60},{0,-70}},
      color={191,0,0}, smooth=Smooth.None));
  connect(wall3.port_b, C3.port) annotation (Line(
      points={{60,-30},{60,-8}},
      color={191,0,0}, smooth=Smooth.None));
  connect(sensor.port, C3.port) annotation (Line(
      points={{80,-20},{60,-20},{60,-8}},
      color={191,0,0}, smooth=Smooth.None));
  connect(C2.port, wall2.port_b) annotation (Line(
      points={{0,-8},{0,-19},{0,-30},{0,-30}},
      color={191,0,0},
      smooth=Smooth.None));
end FlatRod;

Simulating this system, we can see the temperature response of the rightmost thermal capacitance in the following plot:

../../../_images/FR.png

Segmented Rod Subsystem

In our flat system model, we have 3 thermal capacitances and 5 conductances. This configuration represents a rod that has been divided into 3 equal segments and the conductance that occurs between those segments as well as between each segment and some ambient conditions. In theory, we can divide a rod into \(N\) equal segments with \(N-1\) conduction paths between them and \(N\) conduction paths between each segment and the ambient conditions.

Our particular configuration was for \(N=3\). But we can create a subsystem model where \(N\) is a parameter of the subsystem. In other words, we can create a subsystem model that is divided into \(N\) equal segments. But to do so, we cannot simply drag and drop the capacitances into the model and connect them with conductances because we don’t know the exact number.

Instead, we’ll use an array of components to represent this collection of capacitances and conductances. The resulting Rod model can be written in Modelica as follows:

within ModelicaByExample.Subsystems.HeatTransfer.Components;
model Rod "Modeling discretized rod"
  import HTC=Modelica.Thermal.HeatTransfer.Components;

  parameter Integer n(start=2,min=2) "Number of rod segments";
  parameter Modelica.SIunits.Temperature T0 "Initial rod temperature";
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_a
    "Thermal connector to ambient"
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_b port_b
    "Thermal connector for rod end 'b'"
    annotation (Placement(transformation(extent={{90,-10},{110,10}})));
  parameter Modelica.SIunits.HeatCapacity C
    "Total heat capacity of element (= cp*m)";
  parameter Modelica.SIunits.ThermalConductance G_wall
    "Thermal conductivity of wall";
  parameter Modelica.SIunits.ThermalConductance G_rod
    "Thermal conductivity of rod";
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a ambient
    "Thermal connector for rod end 'a'"
    annotation (Placement(transformation(extent={{-10,-110},{10,-90}})));
protected
  HTC.HeatCapacitor capacitance[n](each final C=C/n, each T(start=T0, fixed=true))
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        origin={-30,20})));
  HTC.ThermalConductor wall[n](each final G=G_wall/n)
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90, origin={-30,-20})));
  HTC.ThermalConductor rod_conduction[n-1](each final G=G_rod)
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));
equation
  for i in 1:n loop
    connect(capacitance[i].port, wall[i].port_b) "Capacitance to walls";
    connect(wall[i].port_a, ambient) "Walls to ambient";
  end for;
  for i in 1:n-1 loop
    connect(capacitance[i].port, rod_conduction[i].port_a)
      "Capacitance to next conduction";
    connect(capacitance[i+1].port, rod_conduction[i].port_b)
      "Capacitance to prev conduction";
  end for;
  connect(capacitance[1].port, port_a) "First capacitance to rod end";
  connect(capacitance[n].port, port_b) "Last capacitance to (other) rod end";
end Rod;

There are several interesting things to note about this model. First, the number of segments the rod will be divided into is represented by the n parameter:

  parameter Integer n(start=2,min=2) "Number of rod segments";

The parameter n is then used in the following declarations to specify the number of capacitance and conductance elements in the rod:

  HTC.HeatCapacitor capacitance[n](each final C=C/n, each T(start=T0, fixed=true))
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        origin={-30,20})));
  HTC.ThermalConductor wall[n](each final G=G_wall/n)
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90, origin={-30,-20})));
  HTC.ThermalConductor rod_conduction[n-1](each final G=G_rod)
    annotation (Placement(transformation(extent={{-10,-10},{10,10}})));

Note that if we wish to apply a modification, e.g., G=G_rod to every component in an array of components, we can use the each qualifier on the modification. We’ll discuss the each qualifier and how to apply modifications to arrays of components later in this chapter in the section on Modifications.

Now that we’ve declared our component arrays, we can then wire together the capacitances and conductances using for loops in an equation section:

  for i in 1:n loop
    connect(capacitance[i].port, wall[i].port_b) "Capacitance to walls";
    connect(wall[i].port_a, ambient) "Walls to ambient";
  end for;
  for i in 1:n-1 loop
    connect(capacitance[i].port, rod_conduction[i].port_a)
      "Capacitance to next conduction";
    connect(capacitance[i+1].port, rod_conduction[i].port_b)
      "Capacitance to prev conduction";
  end for;

We also need to connect the ends of the rod to the external connectors so that the rod can be connected to other models:

  connect(capacitance[1].port, port_a) "First capacitance to rod end";
  connect(capacitance[n].port, port_b) "Last capacitance to (other) rod end";

In this way, we are able to create a segmented rod model with an arbitrary number of equally divided segments.

Spatial Resolution

Now that we have our parameterized Rod model, we can look at how the number of segments in the rod impacts the response we see. Ultimately, what we should see is that as the number of segments gets larger (or, as the size of the segments gets smaller), we should converge on a solution.

We’ll start by considering a model where n=3, i.e.,

within ModelicaByExample.Subsystems.HeatTransfer.Examples;
model ThreeSegmentRod "Modeling a heat transfer using 3 segment rod subsystem"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow heating
    "Heating actuator"
    annotation (Placement(transformation(extent={{-60,50},{-40,70}})));
  Modelica.Blocks.Sources.Step bc(height=10, startTime=0.5) "Heat profile"
    annotation (Placement(transformation(extent={{-90,50},{-70,70}})));
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor sensor
    annotation (Placement(transformation(extent={{80,-10},{100,10}})));
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature ambient(T=293.15)
    "Ambient temperature" annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90, origin={0,-80})));
  Components.Rod rod(n=3, C=0.3, G_wall=2.7, T0=293.15, G_rod=1.2)
    annotation (Placement(transformation(extent={{-20,-20},{20,20}})));
equation
  connect(bc.y, heating.Q_flow) annotation (Line(
      points={{-69,60},{-60,60}},
      color={0,0,127}, smooth=Smooth.None));
  connect(heating.port, rod.port_a) annotation (Line(
      points={{-40,60},{-30,60},{-30,0},{-20,0}},
      color={191,0,0},
      smooth=Smooth.None));
  connect(rod.ambient, ambient.port) annotation (Line(
      points={{0,-20},{0,-70},{0,-70}},
      color={191,0,0},
      smooth=Smooth.None));
  connect(rod.port_b, sensor.port) annotation (Line(
      points={{20,0},{80,0}},
      color={191,0,0},
      smooth=Smooth.None));
end ThreeSegmentRod;

We can then extend this model to create additional models with more segments, e.g.,

within ModelicaByExample.Subsystems.HeatTransfer.Examples;
model SixSegmentRod "Rod divided into 6 pieces"
  extends ThreeSegmentRod(rod(n=6));
end SixSegmentRod;
within ModelicaByExample.Subsystems.HeatTransfer.Examples;
model TenSegmentRod
  extends SixSegmentRod(rod(n=10));
end TenSegmentRod;
within ModelicaByExample.Subsystems.HeatTransfer.Examples;
model OneHundredSegmentRod "Rod divided into 100 pieces"
  extends ThreeSegmentRod(rod(n=100));
end OneHundredSegmentRod;
within ModelicaByExample.Subsystems.HeatTransfer.Examples;
model TwoHundredSegmentRod
  extends OneHundredSegmentRod(rod(n=200));
end TwoHundredSegmentRod;

If we simulate all of these cases, we see that as n gets larger, they appear to converge to a common solution and that n=10 seems to provide a reasonable solution without the need to introduce a large number of superfluous components:

../../../_images/SegC.png

Conclusion

In this section, we’ve seen how we can build assemblies of arbitrary size using arrays of components and for loops to connect them together.