diff --git a/IBPSA/Fluid/Sensors/Examples/HeatMeter.mo b/IBPSA/Fluid/Sensors/Examples/HeatMeter.mo
new file mode 100644
index 0000000000..ec1257f602
--- /dev/null
+++ b/IBPSA/Fluid/Sensors/Examples/HeatMeter.mo
@@ -0,0 +1,77 @@
+within IBPSA.Fluid.Sensors.Examples;
+model HeatMeter
+ extends Modelica.Icons.Example;
+
+ package Medium = IBPSA.Media.Water
+ "Medium model";
+
+ parameter Modelica.Units.SI.MassFlowRate m_flow_nominal = 10 "Nominal mass flow rate";
+
+ Sources.MassFlowSource_T sou(
+ redeclare package Medium = Medium,
+ m_flow=m_flow_nominal,
+ nPorts=1) "Mass flow source"
+ annotation (Placement(transformation(extent={{-70,-10},{-50,10}})));
+ Sources.Boundary_pT sin(
+ redeclare package Medium = Medium,
+ nPorts=1)
+ "Sink"
+ annotation (Placement(transformation(extent={{70,-10},{50,10}})));
+ Modelica.Thermal.HeatTransfer.Sources.FixedHeatFlow heaFloRat(Q_flow=
+ m_flow_nominal*10*4200) "Heat flow rate"
+ annotation (Placement(transformation(extent={{-50,30},{-30,50}})));
+ MixingVolumes.MixingVolume vol(
+ redeclare package Medium = Medium,
+ energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
+ m_flow_nominal=m_flow_nominal,
+ V=0.1,
+ nPorts=2)
+ "Mixing volume"
+ annotation (Placement(transformation(extent={{-10,30},{10,50}})));
+ TemperatureTwoPort senT(
+ redeclare package Medium = Medium,
+ m_flow_nominal=m_flow_nominal)
+ annotation (Placement(transformation(extent={{-30,-10},{-10,10}})));
+ IBPSA.Fluid.Sensors.HeatMeter senHeaFlo(
+ redeclare package Medium = Medium,
+ m_flow_nominal=m_flow_nominal)
+ annotation (Placement(transformation(extent={{10,-10},{30,10}})));
+equation
+ connect(sou.ports[1], senT.port_a)
+ annotation (Line(points={{-50,0},{-30,0}}, color={0,127,255}));
+ connect(senT.port_b, vol.ports[1]) annotation (Line(points={{-10,0},{-2,0},{
+ -2,30},{-1,30}}, color={0,127,255}));
+ connect(vol.ports[2], senHeaFlo.port_a) annotation (Line(points={{1,30},{2,30},
+ {2,0},{10,0}}, color={0,127,255}));
+ connect(senHeaFlo.port_b, sin.ports[1])
+ annotation (Line(points={{30,0},{50,0}}, color={0,127,255}));
+ connect(senT.T, senHeaFlo.TExt) annotation (Line(points={{-20,11},{-20,18},{
+ -6,18},{-6,6},{8,6}}, color={0,0,127}));
+ connect(heaFloRat.port, vol.heatPort)
+ annotation (Line(points={{-30,40},{-10,40}}, color={191,0,0}));
+ annotation (
+experiment(Tolerance=1e-6, StopTime=60),
+__Dymola_Commands(file="modelica://IBPSA/Resources/Scripts/Dymola/Fluid/Sensors/Examples/HeatMeter.mos"
+ "Simulate and plot"),
+ Documentation(info="
+
+This example tests the heat meter sensor for the situation where heat is exchanged with a control volume.
+
+",
+revisions="
+
+-
+February 1, 2024, by Michael Wetter:
+Revised implementation.
+This is for
+IBPSA, #1831.
+
+-
+February 1, 2024, by Jan Gall:
+First implementation.
+This is for
+IBPSA, #1831.
+
+
+"));
+end HeatMeter;
diff --git a/IBPSA/Fluid/Sensors/Examples/package.order b/IBPSA/Fluid/Sensors/Examples/package.order
index 6e4c242734..9288f23614 100644
--- a/IBPSA/Fluid/Sensors/Examples/package.order
+++ b/IBPSA/Fluid/Sensors/Examples/package.order
@@ -1,6 +1,7 @@
Density
EnthalpyFlowRate
EntropyFlowRate
+HeatMeter
MassFraction
MoistAirEnthalpyFlowRate
PPM
diff --git a/IBPSA/Fluid/Sensors/HeatMeter.mo b/IBPSA/Fluid/Sensors/HeatMeter.mo
new file mode 100644
index 0000000000..4ef9689fab
--- /dev/null
+++ b/IBPSA/Fluid/Sensors/HeatMeter.mo
@@ -0,0 +1,126 @@
+within IBPSA.Fluid.Sensors;
+model HeatMeter "Measures thermal energy provided between supply and return pipes"
+ extends BaseClasses.PartialDynamicFlowSensor;
+ extends Modelica.Icons.RoundSensor;
+ Modelica.Blocks.Interfaces.RealInput TExt(
+ final unit="K",
+ displayUnit="degC") "External temperature measurement to calculate temperature difference"
+ annotation (Placement(transformation(extent={{-140,40},{-100,80}}),
+ iconTransformation(extent={{-140,40},{-100,80}})));
+ Modelica.Blocks.Interfaces.RealOutput Q_flow(
+ final quantity="HeatFlowRate",
+ final unit="W") "Heat flow rate"
+ annotation (Placement(
+ transformation(
+ extent={{-10,-10},{10,10}},
+ rotation=90,
+ origin={0,110})));
+ parameter Modelica.Units.SI.Temperature T_start=Medium.T_default
+ "Initial or guess value of temperature"
+ annotation (Dialog(group="Initialization"));
+ Medium.Temperature T(start=T_start) "Temperature of the passing fluid";
+protected
+ Medium.Temperature TMed(start=T_start) "Medium temperature to which the sensor is exposed";
+ Medium.Temperature T_a_inflow "Temperature of inflowing fluid at port_a";
+ Medium.Temperature T_b_inflow "Temperature of inflowing fluid at port_b, or T_a_inflow if uni-directional flow";
+initial equation
+ if dynamic then
+ if initType == Modelica.Blocks.Types.Init.SteadyState then
+ der(T) = 0;
+ elseif initType == Modelica.Blocks.Types.Init.InitialState or
+ initType == Modelica.Blocks.Types.Init.InitialOutput then
+ T = T_start;
+ end if;
+ end if;
+equation
+ if allowFlowReversal then
+ T_a_inflow = Medium.temperature(state=
+ Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
+ T_b_inflow = Medium.temperature(state=
+ Medium.setState_phX(p=port_a.p, h=port_a.h_outflow, X=port_a.Xi_outflow));
+ TMed = Modelica.Fluid.Utilities.regStep(
+ x=port_a.m_flow,
+ y1=T_a_inflow,
+ y2=T_b_inflow,
+ x_small=m_flow_small);
+ else
+ TMed = Medium.temperature(state=
+ Medium.setState_phX(p=port_b.p, h=port_b.h_outflow, X=port_b.Xi_outflow));
+ T_a_inflow = TMed;
+ T_b_inflow = TMed;
+ end if;
+ if dynamic then
+ der(T) = (TMed-T)*k*tauInv;
+ else
+ T = TMed;
+ end if;
+ Q_flow = port_a.m_flow * Medium.cp_const * (T - TExt);
+
+annotation (defaultComponentName="senHeaFlo",
+ Icon(graphics={
+ Ellipse(
+ fillColor={245,222,222},
+ fillPattern=FillPattern.Solid,
+ extent={{-70,-70},{70,70}}),
+ Line(points={{-100,0},{-70,0}}, color={0,128,255}),
+ Line(points={{70,0},{100,0}}, color={0,128,255}),
+ Line(points={{0,100},{0,70}}, color={0,0,127}),
+ Text(
+ extent={{180,151},{20,99}},
+ textColor={0,0,0},
+ textString="Q_flow"),
+ Text(
+ extent={{-20,120},{-140,70}},
+ textColor={0,0,0},
+ textString=DynamicSelect("", String(Q_flow, leftJustified=false, significantDigits=3))),
+ Text(
+ extent={{-22,120},{-100,48}},
+ textColor={0,0,127},
+ textString="TExt"),
+ Line(points={{37.6,13.7},{65.8,23.9}}),
+ Line(points={{22.9,32.8},{40.2,57.3}}),
+ Line(points={{0,70},{0,40}}),
+ Line(points={{-22.9,32.8},{-40.2,57.3}}),
+ Line(points={{-37.6,13.7},{-65.8,23.9}}),
+ Ellipse(
+ fillColor={64,64,64},
+ pattern=LinePattern.None,
+ fillPattern=FillPattern.Solid,
+ extent={{-7,-7},{7,7}}),
+ Polygon(
+ rotation=-17.5,
+ fillColor={64,64,64},
+ pattern=LinePattern.None,
+ fillPattern=FillPattern.Solid,
+ points={{-5.0,0.0},{-2.0,60.0},{0.0,65.0},{2.0,60.0},{5.0,0.0}}),
+ Ellipse(
+ lineColor={64,64,64},
+ fillColor={255,255,255},
+ extent={{-12,-12},{12,12}})}),
+Documentation(info="
+
+This model measures thermal energy provided between a supply and return pipe.
+It measures the capacity flow rate of the heat transfer fluid and the change in its
+temperature compared to an external temperature measurement that is input into the port
+TExt
. The sensor does not influence the fluid.
+
+
+The rate of heat flow is calculated as Q̇ = ṁ cp (T - TExt).
+",
+revisions="
+
+-
+February 1, 2024, by Michael Wetter:
+Revised implementation.
+This is for
+IBPSA, #1831.
+
+-
+February 1, 2024, by Jan Gall:
+First implementation.
+This is for
+IBPSA, #1831.
+
+
+"));
+end HeatMeter;
diff --git a/IBPSA/Fluid/Sensors/UsersGuide.mo b/IBPSA/Fluid/Sensors/UsersGuide.mo
index 2518136956..ec8aa7162f 100644
--- a/IBPSA/Fluid/Sensors/UsersGuide.mo
+++ b/IBPSA/Fluid/Sensors/UsersGuide.mo
@@ -60,7 +60,7 @@ Sensors for quantities that depend on the direction of the mass flow rate but
not of its magnitude:
Such quantities include density, mass fraction, PPM, relative humidity, specific enthalpy, specific entropy and trace substances.
-Not that these are all quantities that are carried by the fluid that flows through the sensor.
+Note that these are all quantities that are carried by the fluid that flows through the sensor.
For these sensors, if the parameter allowFlowReversal=true
is set (which is the default setting),
then it is strongly recommended to configure them
as a dynamic sensor. This is the default setting.
diff --git a/IBPSA/Fluid/Sensors/VolumeFlowRate.mo b/IBPSA/Fluid/Sensors/VolumeFlowRate.mo
index 98ba6ef348..c77f073454 100644
--- a/IBPSA/Fluid/Sensors/VolumeFlowRate.mo
+++ b/IBPSA/Fluid/Sensors/VolumeFlowRate.mo
@@ -25,7 +25,7 @@ model VolumeFlowRate "Ideal sensor for volume flow rate"
rotation=270)));
protected
Medium.Density dMed(start=d_start)
- "Medium temperature to which the sensor is exposed";
+ "Medium density to which the sensor is exposed";
Medium.Density d_a_inflow(start=d_start)
"Density of inflowing fluid at port_a";
diff --git a/IBPSA/Fluid/Sensors/package.order b/IBPSA/Fluid/Sensors/package.order
index e7e7c9bff9..fa943ff00f 100644
--- a/IBPSA/Fluid/Sensors/package.order
+++ b/IBPSA/Fluid/Sensors/package.order
@@ -3,6 +3,7 @@ Density
DensityTwoPort
EnthalpyFlowRate
EntropyFlowRate
+HeatMeter
LatentEnthalpyFlowRate
MassFlowRate
MassFraction
diff --git a/IBPSA/Resources/ReferenceResults/Dymola/IBPSA_Fluid_Sensors_Examples_HeatMeter.txt b/IBPSA/Resources/ReferenceResults/Dymola/IBPSA_Fluid_Sensors_Examples_HeatMeter.txt
new file mode 100644
index 0000000000..e6493018ae
--- /dev/null
+++ b/IBPSA/Resources/ReferenceResults/Dymola/IBPSA_Fluid_Sensors_Examples_HeatMeter.txt
@@ -0,0 +1,11 @@
+last-generated=2024-02-01
+statistics-simulation=
+{
+ "linear": " ",
+ "nonlinear": " ",
+ "number of continuous time states": "3",
+ "numerical Jacobians": "0"
+}
+time=[0e+00, 6e+01]
+senHeaFlo.Q_flow=[0e+00, 6.135302814887254e+03, 2.024211004943313e+04, 3.806596635045912e+04, 5.735886017640069e+04, 7.69003203125e+04, 9.605505767047344e+04, 1.145098494566929e+05, 1.321029788968033e+05, 1.487847005315746e+05, 1.645585e+05, 1.794507366602567e+05, 1.934949730579872e+05, 2.067290271429792e+05, 2.191943982694409e+05, 2.30933234375e+05, 2.41987868139305e+05, 2.523970031914593e+05, 2.621994657085459e+05, 2.714292556591234e+05, 2.8011971875e+05, 2.883016199230112e+05, 2.960048461113893e+05, 3.032570914998232e+05, 3.100849416862036e+05, 3.16513375e+05, 3.225658712441389e+05, 3.282644616773623e+05, 3.336297565791147e+05, 3.38681284347135e+05, 3.4343725e+05, 3.479150597213503e+05, 3.521309010177422e+05, 3.561000986168839e+05, 3.598370335411865e+05, 3.6335534375e+05, 3.666678729444488e+05, 3.697867773791028e+05, 3.727235973512874e+05, 3.754886266952436e+05, 3.780919375e+05, 3.805429672290988e+05, 3.828506533860168e+05, 3.85023346414309e+05, 3.870689075040729e+05, 3.8899471875e+05, 3.908079051247715e+05, 3.925150603812684e+05, 3.941224394709131e+05, 3.956357509278391e+05, 3.97060625e+05, 3.984022804174283e+05, 3.996654359322101e+05, 4.008547202083168e+05, 4.019744347208977e+05, 4.0302865625e+05, 4.040212211838006e+05, 4.04955686340282e+05, 4.058354698288537e+05, 4.066638104436868e+05, 4.0744371875e+05, 4.081780018008138e+05, 4.088693741418842e+05, 4.09520344548312e+05, 4.101332484785269e+05, 4.107103125e+05, 4.11253626332332e+05, 4.117651556152049e+05, 4.122467818404886e+05, 4.127002176242927e+05, 4.13127125e+05, 4.135290322358382e+05, 4.139074682803112e+05, 4.142637504369059e+05, 4.145992179171435e+05, 4.149150625e+05, 4.152124382295441e+05, 4.154924371525018e+05, 4.157560628232587e+05, 4.160042806340994e+05, 4.1623796875e+05, 4.164580005396084e+05, 4.16665155992903e+05, 4.168601564892154e+05, 4.170437807942336e+05, 4.172166875e+05, 4.17379437899342e+05, 4.175326873098595e+05, 4.176770001768287e+05, 4.178128434126455e+05, 4.1794075e+05, 4.180611877956323e+05, 4.181745936091331e+05, 4.182813438809327e+05, 4.183818435008583e+05, 4.1847646875e+05, 4.185655627185454e+05, 4.186494373958896e+05, 4.187283750967591e+05, 4.188026873156272e+05, 4.1887265625e+05]
+heaFloRat.Q_flow=[4.2e+05, 4.2e+05]
diff --git a/IBPSA/Resources/Scripts/Dymola/Fluid/Sensors/Examples/HeatMeter.mos b/IBPSA/Resources/Scripts/Dymola/Fluid/Sensors/Examples/HeatMeter.mos
new file mode 100644
index 0000000000..7216079cc4
--- /dev/null
+++ b/IBPSA/Resources/Scripts/Dymola/Fluid/Sensors/Examples/HeatMeter.mos
@@ -0,0 +1,2 @@
+simulateModel("IBPSA.Fluid.Sensors.Examples.HeatMeter", stopTime=60, tolerance=1e-06, resultFile="HeatMeter");
+createPlot(id=1, position={551, 169, 592, 371}, y={"senHeaFlo.Q_flow", "heaFloRat.Q_flow"}, range={0.0, 60.0, -50000.0, 450000.0}, grid=true, colors={{28,108,200}, {238,46,47}}, timeUnit="s", displayUnits={"W", "W"});
diff --git a/IBPSA/package.mo b/IBPSA/package.mo
index 667e7113b5..e776a051d5 100644
--- a/IBPSA/package.mo
+++ b/IBPSA/package.mo
@@ -280,6 +280,9 @@ David Blum, Lawrence Berkeley National Laboratory, Berkeley, CA, USA
Massimo Cimmino, Polytechnique Montreal, Canada
+Jan Gall, Robert Bosch GmbH, Germany
+
+
Jianjun Hu, Lawrence Berkeley National Laboratory, Berkeley, CA, USA