within AixLib.Fluid.Storage;
model StorageDetailed
  "Buffer Storage Model with support for heating rod and two heating coils"

  extends AixLib.Fluid.Interfaces.LumpedVolumeDeclarations(
    final T_start=sum(TStart)/n);

  replaceable package MediumHC1 =
      Modelica.Media.Interfaces.PartialMedium "Medium model for HC1"

  replaceable package MediumHC2 =
      Modelica.Media.Interfaces.PartialMedium "Medium model for HC2"
  parameter Modelica.Units.SI.MassFlowRate m1_flow_nominal(min=0)
    "Nominal mass flow rate of fluid 1 ports"
  parameter Modelica.Units.SI.MassFlowRate m2_flow_nominal(min=0)
    "Nominal mass flow rate of fluid 2 ports"

  parameter Modelica.Units.SI.MassFlowRate mHC1_flow_nominal(min=0) if useHeatingCoil1
    "Nominal mass flow rate of fluid 1 ports"
  parameter Modelica.Units.SI.MassFlowRate mHC2_flow_nominal(min=0) if useHeatingCoil2
    "Nominal mass flow rate of fluid 1 ports"

  parameter Boolean useHeatingCoil1=true "Use Heating Coil1?"
  parameter Boolean useHeatingCoil2=true "Use Heating Coil2?"
  parameter Boolean useHeatingRod=true "Use Heating Rod?"

  parameter Integer n(min=3)=5 "Number of Layers";
  parameter Modelica.Units.SI.Temperature TStart[n]=fill(298.15,n)
    "Start Temperature of fluid in each layer. e.g. for a 3 layer model: {20, 20, 20}"

  replaceable parameter AixLib.DataBase.Storage.StorageDetailedBaseDataDefinition data
    constrainedby AixLib.DataBase.Storage.StorageDetailedBaseDataDefinition
    "Data record for Storage"

////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////CONVECTION/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConIn=100 "Model assumptions heat transfer coefficient water <-> wall";
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConOut=10 "Model assumptions heat transfer coefficient insulation <-> air";
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConHC1=100 "Model assumptions Coefficient of Heat Transfer HC1 <-> Heating Water"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConHC2=100 " Model assumptions Coefficient of Heat Transfer HC2 <-> Heating Water"
  parameter Boolean upToDownHC1 = true
    "Heating Coil 1 orientation from up to down?"
  parameter Boolean upToDownHC2 = true
    "Heating Coil 2 orientation from up to down?"

  parameter Modelica.Units.SI.Temperature TStartWall=293.15
    "Starting Temperature of wall in K"
  parameter Modelica.Units.SI.Temperature TStartIns=293.15
    "Starting Temperature of insulation in K"

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////Advanced parameters/////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
  parameter Integer nLowerPortSupply=integer(max(AixLib.Utilities.Math.Functions.round(data.hLowerPortSupply/(data.hTank/n) + 0.5,0),1)) "Layer number lower end of supply is connected to"
  parameter Integer nUpperPortSupply=integer(min(AixLib.Utilities.Math.Functions.round(data.hUpperPortSupply/(data.hTank/n) + 0.5,0),n)) "Layer number upper end of supply is connected to"
  parameter Integer nLowerPortDemand=integer(max(AixLib.Utilities.Math.Functions.round(data.hLowerPortDemand/(data.hTank/n) + 0.5,0),1)) "Layer number lower end of demand is connected to"
  parameter Integer nUpperPortDemand=integer(min(AixLib.Utilities.Math.Functions.round(data.hUpperPortDemand/(data.hTank/n) + 0.5,0),n)) "Layer number upper end of demand is connected to"
  parameter Integer nTS1=integer(AixLib.Utilities.Math.Functions.round(data.hTS1/(data.hTank/n) + 0.5,0)) "Layer number lower temperature sensor is connected to"
  parameter Integer nTS2=integer(AixLib.Utilities.Math.Functions.round(data.hTS2/(data.hTank/n) + 0.5,0)) "Layer number upper temperature sensor is connected to"

  parameter Integer nHC1Up=integer(ceil(data.hHC1Up/(data.hTank/n))) "Layer number upper end of heating coil 1 is connected to"
  parameter Integer nHC1Low=integer(floor(data.hHC1Low/(data.hTank/n))+1) "Layer number lower end of heating coil 1 is connected to"
  parameter Integer nHC2Up=integer(ceil(data.hHC2Up/(data.hTank/n))) "Layer number upper end of heating coil 2 is connected to"
  parameter Integer nHC2Low=integer(floor(data.hHC2Low/(data.hTank/n))+1) "Layer number lower end of heating coil 2 is connected to"
  parameter Integer nHR=integer(AixLib.Utilities.Math.Functions.round(data.hHR/(data.hTank/n) + 0.5,0)) "Layer number heating rod is connected to"

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////final parameters////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
 final parameter Integer disHC1 = nHC1Up-nHC1Low+1;
 final parameter Integer disHC2 = nHC2Up-nHC2Low+1;

 final parameter Boolean inpLowLayDemand=(nLowerPortDemand == 1); //if there is an input at the lowest layer
 final parameter Boolean inpHigLayDemand=(nUpperPortDemand == n);

 final parameter Boolean inpLowLaySupply=(nLowerPortSupply == 1); //if there is an input at the lowest layer
 final parameter Boolean inpHigLaySupply=(nUpperPortSupply == n);

 final parameter Integer[n] inpActLay = cat(1,{if (nLowerPortDemand==k or nLowerPortSupply==k) then (if (nUpperPortDemand==k or nUpperPortSupply==k) then 2 else 1) else (if (nUpperPortDemand==k or nUpperPortSupply==k) then 1 else 0) for k in 1:n});
 final parameter Integer[n] portsLayer=cat(1,{if inpActLay[1]==1 then 3 else 1},{inpActLay[k]*2+2 for k in 2:n-1},{if inpActLay[n]==1 then 3 else 1});
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////components/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatportOutside "Outer heat port"
  Modelica.Blocks.Interfaces.RealOutput TTop(
      final quantity="ThermodynamicTemperature",
      final unit = "K",
      min=0,
      displayUnit = "degC")
    "Temperature at the top"
  Modelica.Blocks.Interfaces.RealOutput TBottom(
      final quantity="ThermodynamicTemperature",
      final unit = "K",
      min=0,
      displayUnit = "degC")
    "Temperature at the Bottom"
  Modelica.Fluid.Interfaces.FluidPort_a fluidportTop1(redeclare final package
      Medium =  Medium)
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_a fluidportBottom2(redeclare final
      package Medium =
               Medium)
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b fluidportBottom1(redeclare final
      package Medium =
                 Medium)
    "Fluid connector b (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b fluidportTop2(redeclare final package
      Medium =
        Medium)
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

  HeatTransfer heatTransfer(final n=n,
      redeclare final package Medium = Medium,
      final data=data)
      "Heat transfer model"

  AixLib.Fluid.MixingVolumes.MixingVolume layer[n](
    each final energyDynamics=energyDynamics,
    each final massDynamics=massDynamics,
    each final p_start=p_start,
    each final X_start=X_start,
    each final C_start=C_start,
    each final C_nominal=C_nominal,
    each final mSenFac=mSenFac,
    each final allowFlowReversal=allowFlowReversal_layers,
    each final m_flow_small=m_flow_small,
    final V=fill(data.hTank/n*Modelica.Constants.pi/4*data.dTank^2,n),
    final nPorts = portsLayer,
    final T_start=TStart,
    redeclare each final package Medium = Medium,
    each final m_flow_nominal=m1_flow_nominal + m2_flow_nominal)
    "Layer volumes"
    replaceable model HeatTransfer =
      AixLib.Fluid.Storage.BaseClasses.HeatTransferOnlyConduction
    constrainedby AixLib.Fluid.Storage.BaseClasses.PartialHeatTransferLayers
    "Heat Transfer Model between fluid layers"

///////////////////////////////////////////////////////////////////////////////////////
/////HEATING COILS AND RODS/////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////
/////HEATING COIL 1 ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

  Modelica.Fluid.Interfaces.FluidPort_a portHC1In(redeclare final package
      Medium =
        MediumHC1) if useHeatingCoil1
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b portHC1Out(redeclare final package
      Medium =
        MediumHC1) if useHeatingCoil1
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

//////////////////////////////////////////////////////////////////////////////////////////
/////HEATING COIL 2 ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

  Modelica.Fluid.Interfaces.FluidPort_a portHC2In(redeclare final package
      Medium =
        MediumHC2) if useHeatingCoil2
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b portHC2Out(redeclare final package
      Medium =
        MediumHC2) if useHeatingCoil2
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

//////////////////////////////////////////////////////////////////////////////////////////
/////HEATING ROD ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatingRod
                                       if useHeatingRod

//////////////////////////////////////////////////////////////////////////////////////////
/////COVER / INSULATION  ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

  AixLib.Fluid.Storage.BaseClasses.StorageCover topCover(
    final energyDynamics=energyDynamics,
    final lambdaWall=data.lambdaWall,
    final lambdaIns=data.lambdaIns,
    final hConIn=hConIn,
    final hConOut=hConOut,
    final TStartWall=TStartWall,
    final TStartIns=TStartIns,
    final rhoIns=data.rhoIns,
    final cIns=data.cIns,
    final rhoWall=data.rhoWall,
    final cWall=data.cWall,
    final D1=data.dTank,
    final sWall=data.sWall,
    final sIns=data.sIns)
  AixLib.Fluid.Storage.BaseClasses.StorageMantle storageMantle[n](
    each final energyDynamics=energyDynamics,
    each final lambdaWall=data.lambdaWall,
    each final lambdaIns=data.lambdaIns,
    each final TStartWall=TStartWall,
    each final TStartIns=TStartIns,
    each final rhoIns=data.rhoIns,
    each final cIns=data.cIns,
    each final rhoWall=data.rhoWall,
    each final cWall=data.cWall,
    each final height=data.hTank/n,
    each final D1=data.dTank,
    each final sWall=data.sWall,
    each final sIns=data.sIns,
    each final hConIn=hConIn,
    each final hConOut=hConOut)
  AixLib.Fluid.Storage.BaseClasses.StorageCover bottomCover(
    final energyDynamics=energyDynamics,
    final lambdaWall=data.lambdaWall,
    final lambdaIns=data.lambdaIns,
    final hConIn=hConIn,
    final hConOut=hConOut,
    final TStartWall=TStartWall,
    final TStartIns=TStartIns,
    final rhoIns=data.rhoIns,
    final cIns=data.cIns,
    final rhoWall=data.rhoWall,
    final cWall=data.cWall,
    final D1=data.dTank,
    final sWall=data.sWall,
    final sIns=data.sIns)

  AixLib.Fluid.Storage.BaseClasses.HeatingCoil heatingCoil1(
    m_flow_small=m_flow_small_HC1,
    disHC=disHC1,
    hConHC=hConHC1,
    redeclare package Medium = MediumHC1,
    lengthHC=data.lengthHC1,
    pipeHC=data.pipeHC1,
    allowFlowReversal=allowFlowReversal_HC1,
    final m_flow_nominal=mHC1_flow_nominal,
    TStart=sum(TStart)/n) if useHeatingCoil1
  AixLib.Fluid.Storage.BaseClasses.HeatingCoil heatingCoil2(
    m_flow_small=m_flow_small_HC2,
    disHC=disHC2,
    lengthHC=data.lengthHC2,
    hConHC=hConHC2,
    pipeHC=data.pipeHC2,
    redeclare package Medium = MediumHC2,
    allowFlowReversal=allowFlowReversal_HC2,
    final m_flow_nominal=mHC2_flow_nominal,
    TStart=sum(TStart)/n) if useHeatingCoil2

  parameter Modelica.Units.SI.MassFlowRate m_flow_small_HC1=1E-4*abs(mHC1_flow_nominal) if useHeatingCoil1
    "Small mass flow rate for regularization of zero flow"
  parameter Modelica.Units.SI.MassFlowRate m_flow_small_HC2=1E-4*abs(mHC2_flow_nominal) if useHeatingCoil2
    "Small mass flow rate for regularization of zero flow"
  parameter Modelica.Units.SI.MassFlowRate m_flow_small=1E-4*abs(m1_flow_nominal + m2_flow_nominal)
    "Small mass flow rate for regularization of zero flow"
  parameter Boolean allowFlowReversal_layers=true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal. Used only if model has two ports."
  parameter Boolean allowFlowReversal_HC1=true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"
  parameter Boolean allowFlowReversal_HC2=true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"
initial equation
   assert(data.hHC1Up<=data.hTank and data.hHC1Up>=0.0 and
     data.hHC1Low<=data.hTank and data.hHC1Low>=0.0,
     "Storage coil 1 inlet and outlet must be within tank's height.",
     level = AssertionLevel.error);
   assert(data.hHC1Up>data.hHC1Low,
     "Storage coil 1 upper port must be higher than lower port.",
     level = AssertionLevel.error);

   assert(data.hHC2Up<=data.hTank and data.hHC2Up>=0.0 and
     data.hHC2Low<=data.hTank and data.hHC2Low>=0.0,
     "Storage coil 2 inlet and outlet must be within tank's height.",
     level = AssertionLevel.error);
   assert(data.hHC2Up>data.hHC2Low,
     "Storage coil 2 upper port must be higher than lower port.",
     level = AssertionLevel.error);

equation
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////connection of Heating Coils//////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
 if useHeatingCoil1 then

 for i in nHC1Low:nHC1Up loop

   if upToDownHC1 == true then
     connect(heatingCoil1.Therm1[nHC1Up+1-i], layer[i].heatPort);
   else
     connect(heatingCoil1.Therm1[i-nHC1Low+1], layer[i].heatPort);
 end if;

end for;

connect(portHC1In, heatingCoil1.port_a)
  connect(portHC1Out, heatingCoil1.port_b)

end if;

 if useHeatingCoil2 then

 for i in nHC2Low:nHC2Up loop

   if upToDownHC2 == true then
     connect(heatingCoil2.Therm1[nHC2Up+1-i], layer[i].heatPort);
   else
     connect(heatingCoil2.Therm1[i-nHC2Low+1], layer[i].heatPort);
   end if;
   end for;

  connect(portHC2In, heatingCoil2.port_a)
  connect(portHC2Out, heatingCoil2.port_b)

 end if;

////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////connection of Heating Rod//////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////

if useHeatingRod then

connect(heatingRod, layer[nHR].heatPort);
end if;

////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////  connection of Temperature Sensor///////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
  if nTS1 <= 1 then
    TBottom = layer[1].heatPort.T;
  elseif nTS1 >= n then
    TBottom = layer[n].heatPort.T;
  else
    TBottom = layer[nTS1].heatPort.T;
  end if;
  if nTS2 <= 1 then
    TTop = layer[1].heatPort.T;
  elseif nTS2 >= n then
    TTop = layer[n].heatPort.T;
  else
    TTop = layer[nTS2].heatPort.T;
  end if;
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////connection of volumes and walls//////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
   for i in 1:n loop
     connect(layer[i].heatPort, storageMantle[i].heatportInner);
     connect(storageMantle[i].heatportOuter, heatportOutside);
   end for;
    connect(layer[1].heatPort, bottomCover.heatportInner);
    connect(layer[n].heatPort,topCover.heatportInner);

    connect(bottomCover.heatportOuter, heatportOutside);
    connect(topCover.heatportOuter, heatportOutside);

////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////connections of inner layers//////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
for i in 2:(n-1) loop
    if nLowerPortDemand == i then
       connect(layer[i].ports[4], fluidportBottom2);
       if nUpperPortDemand == i then
         connect(layer[i].ports[6],fluidportTop2);
       end if;
    elseif nUpperPortDemand ==i then
         connect(layer[i].ports[4],fluidportTop2);
    end if;

   connect(layer[i].ports[2],layer[i+1].ports[1]);
  end for;

    for i in 2:(n-1) loop
    if nLowerPortSupply == i then
       connect(layer[i].ports[3], fluidportBottom1);
       if nUpperPortSupply == i then
         connect(layer[i].ports[5],fluidportTop1);
       end if;
    elseif nUpperPortSupply ==i then
         connect(layer[i].ports[3],fluidportTop1);
    end if;

  end for;
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////connections of upper and lower layer/////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
  if nLowerPortDemand == 1 then
    connect(layer[1].ports[3],fluidportBottom2);
  end if;
  connect(layer[1].ports[1],layer[2].ports[1]);

  if nUpperPortDemand == n then
    connect(layer[n].ports[3],fluidportTop2);
  end if;

  if nLowerPortSupply == 1 then
    connect(layer[1].ports[2],fluidportBottom1);
  end if;
  connect(layer[1].ports[1],layer[2].ports[1]);

  if nUpperPortSupply == n then
    connect(layer[n].ports[2],fluidportTop1);
  end if;
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////connections of heat transfer model///////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
  connect(heatTransfer.therm, layer.heatPort);
end StorageDetailed;


within AixLib.Fluid.Storage;
model StorageSimple
  "Simplified multi-layer storage model with volumes as heat exchangers without pressure losses"

  replaceable package Medium =
     Modelica.Media.Water.ConstantPropertyLiquidWater
     constrainedby Modelica.Media.Interfaces.PartialMedium;

  parameter Integer n(min = 3) "number of layers";
  parameter Modelica.Units.SI.Length d "storage diameter";
  parameter Modelica.Units.SI.Length h "storage height";
  parameter Modelica.Units.SI.ThermalConductivity lambda_ins
    "thermal conductivity of insulation"
  parameter Modelica.Units.SI.Length s_ins "thickness of insulation"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConIn "Iinternal heat transfer coefficient"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConOut "External heat transfer coefficient"
  parameter Modelica.Units.SI.Volume V_HE "heat exchanger volume"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer k_HE
    "heat exchanger heat transfer coefficient"
  parameter Modelica.Units.SI.Area A_HE "heat exchanger area"
  parameter Modelica.Units.SI.RelativePressureCoefficient beta = 350e-6
  parameter Real kappa = 0.4

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal_layer
    "Nominal mass flow rate in layers";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal_HE
    "Nominal mass flow rate of heat exchanger layers";

  //Optional temperature outputs
  parameter Boolean use_TOut = true "Use temperature real outputs";

  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state in layers and layers_HE";

  //Initialization parameters
  parameter Modelica.Units.SI.Temperature T_start[n]
    "Start value of temperature of each layer, e.g. for 3 layers: {20, 20, 20}"
  parameter Modelica.Media.Interfaces.Types.AbsolutePressure p_start=Medium.p_default
    "Start value of pressure"

  //Mass flow rates to regulate zero flow
  parameter Modelica.Units.SI.MassFlowRate m_flow_small_layer=1E-4*abs(m_flow_nominal_layer)
    "Small mass flow rate for regularization of zero flow"
   parameter Modelica.Units.SI.MassFlowRate m_flow_small_layer_HE=1E-4*abs(m_flow_nominal_HE)
    "Small mass flow rate for regularization of zero flow"

  Modelica.Fluid.Interfaces.FluidPort_a
                    port_a_consumer(redeclare final package Medium = Medium)
  Modelica.Fluid.Interfaces.FluidPort_b
                    port_b_consumer(redeclare final package Medium = Medium)
  Fluid.MixingVolumes.MixingVolume
                     layer[n](
    each final energyDynamics=energyDynamics,
    each final p_start=p_start,
    T_start=T_start,
    each final m_flow_small=m_flow_small_layer,
    each final V = V / n,
    redeclare final package Medium = Medium,
    each final nPorts=2,
    each final m_flow_nominal=m_flow_nominal_layer)
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort
    "connect to ambient temperature around the storage"
  Modelica.Fluid.Interfaces.FluidPort_b
    port_b_heatGenerator(redeclare final package Medium = Medium)
  Modelica.Fluid.Interfaces.FluidPort_a
    port_a_heatGenerator(redeclare final package Medium = Medium)
  Fluid.MixingVolumes.MixingVolume
    layer_HE[n](
    each final energyDynamics=energyDynamics,
    each final p_start=p_start,
    T_start=T_start,
    each final m_flow_small=m_flow_small_layer_HE,
    each final V = V_HE / n,
    redeclare final package Medium = Medium,
    each final nPorts=2,
    each final m_flow_nominal=m_flow_nominal_HE)

  Modelica.Thermal.HeatTransfer.Components.ThermalConductor heatTransfer_HE[n](each final G = k_HE * A_HE / n)
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor heatTransfer[n](final G = cat(1, {G_top_bottom}, array(G_middle for k in 2:n - 1), {G_top_bottom}))
  BaseClasses.Bouyancy bouyancy[n - 1](
    each final rho=Medium.density(Medium.setState_phX(
        port_a_consumer.p,
        inStream(port_a_consumer.h_outflow),
        inStream(port_a_consumer.Xi_outflow))),
    each final lambda=Medium.thermalConductivity(Medium.setState_phX(
        port_a_consumer.p,
        inStream(port_a_consumer.h_outflow),
        inStream(port_a_consumer.Xi_outflow))),
    each final g=Modelica.Constants.g_n,
    each final cp=Medium.specificHeatCapacityCp(Medium.setState_phX(
        port_a_consumer.p,
        inStream(port_a_consumer.h_outflow),
        inStream(port_a_consumer.Xi_outflow))),
    each final A=A,
    each final beta=beta,
    each final dx=dx,
    each final kappa=kappa)

  Modelica.Blocks.Interfaces.RealOutput TLayer[n](
    each final quantity="ThermodynamicTemperature",
    each final unit="K",
    each min=0,
    each displayUnit="degC") if use_TOut "Temperature in the top layer"
  Modelica.Blocks.Interfaces.RealOutput TLayer_HE[n](
    each final quantity="ThermodynamicTemperature",
    each final unit="K",
    each min=0,
    each displayUnit="degC") if use_TOut "Temperature in the top layer"
protected
  parameter Modelica.Units.SI.Volume V = A * h;
  parameter Modelica.Units.SI.Area A = Modelica.Constants.pi * d ^ 2 / 4;
  parameter Modelica.Units.SI.Length dx = V / A / n;
  parameter Modelica.Units.SI.ThermalConductance G_middle=
  2*Modelica.Constants.pi*h/n/(1/(hConIn*d/2) + 1/lambda_ins*
  log((d/2 + s_ins)/(d/2)) + 1/(hConOut*(d/2 + s_ins)));
  parameter Modelica.Units.SI.ThermalConductance G_top_bottom=
   G_middle + lambda_ins / s_ins * A;

equation
  //Connect layers to the upper and lower ports
  connect(port_a_consumer, layer[1].ports[1])
  connect(layer[n].ports[2], port_b_consumer)

  //Connect layers
  for k in 1:n - 1 loop
    connect(layer[k].ports[2], layer[k + 1].ports[1]);
  end for;
  //Connect Heat Transfer to the Outside
  for k in 1:n loop
    connect(heatTransfer[k].port_a, layer[k].heatPort);
    connect(heatTransfer[k].port_b, heatPort);
  end for;
  //Connect layers of Heat Exchanger
  connect(port_a_heatGenerator, layer_HE[n].ports[2])
  connect(port_b_heatGenerator, layer_HE[1].ports[1])
  for k in 1:n - 1 loop
    connect(layer_HE[k].ports[2], layer_HE[k + 1].ports[1]);
  end for;
  //Connect heat exchanger to storage layers
  for k in 1:n loop
    connect(heatTransfer_HE[k].port_a, layer[k].heatPort);
    connect(heatTransfer_HE[k].port_b, layer_HE[k].heatPort);
  end for;
  //Connect bouyancy element
  for k in 1:n - 1 loop
    connect(bouyancy[k].port_a, layer[k + 1].heatPort);
    connect(bouyancy[k].port_b, layer[k].heatPort);
  end for;
  connect(heatPort, heatPort)

///////////////////////////////////////////////////////////////////////////////
///////////////////  connection of Temperature Sensor//////////////////////////
///////////////////////////////////////////////////////////////////////////////

  if use_TOut then
    for k in 1:n loop

      TLayer[k] = layer[k].heatPort.T;

      TLayer_HE[k] = layer_HE[k].heatPort.T;

    end for;
  end if;
end StorageSimple;


within AixLib.Fluid.Storage;
model Stratified "Model of a stratified tank for thermal energy storage"
  extends AixLib.Fluid.Storage.BaseClasses.PartialStratified(vol(each nPorts=3));

  Modelica.Fluid.Interfaces.FluidPorts_a fluPorVol[nSeg](
    redeclare each final package Medium = Medium)
    "Fluid port that connects to the control volumes of the tank"
equation
  connect(port_a, vol[1].ports[1])
  connect(vol[nSeg].ports[2], port_b)
  for i in 1:(nSeg-1) loop
    connect(vol[i].ports[2], vol[i + 1].ports[1])
  end for;
  for i in 1:nSeg loop
    connect(fluPorVol[i], vol[i].ports[3])
  end for;
end Stratified;


within AixLib.Fluid.Storage;
model StratifiedEnhanced "Stratified tank model with enhanced discretization"
  extends BaseClasses.PartialStratified(
    nSeg=4,
    vol(each prescribedHeatFlowRate=true,
        each nPorts=3));

protected
  AixLib.Fluid.Sensors.EnthalpyFlowRate H_a_flow(
    redeclare package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final tau=0,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_small=m_flow_small) "Enthalpy flow rate at port a"
  AixLib.Fluid.Sensors.EnthalpyFlowRate[nSeg - 1] H_vol_flow(
    redeclare package Medium = Medium,
    each final m_flow_nominal=m_flow_nominal,
    each final tau=0,
    each final allowFlowReversal=allowFlowReversal,
    each final m_flow_small=m_flow_small)
    "Enthalpy flow rate between the volumes"
  AixLib.Fluid.Sensors.EnthalpyFlowRate H_b_flow(
    redeclare package Medium = Medium,
    final m_flow_nominal=m_flow_nominal,
    final tau=0,
    final allowFlowReversal=allowFlowReversal,
    final m_flow_small=m_flow_small) "Enthalpy flow rate at port b"

  BaseClasses.ThirdOrderStratifier str(
    redeclare package Medium = Medium,
    nSeg=nSeg,
    m_flow_small=m_flow_small) "Model to reduce numerical dissipation"

  Modelica.Blocks.Sources.RealExpression mTan_flow(y=port_a.m_flow)
    "Mass flow rate at port a"
equation
  connect(H_a_flow.port_b, vol[1].ports[1])
  connect(vol[nSeg].ports[2], H_b_flow.port_a)
  connect(H_b_flow.port_b, port_b)
  for i in 1:(nSeg-1) loop
    connect(vol[i].ports[2], H_vol_flow[i].port_a)
    connect(H_vol_flow[i].port_b, vol[i + 1].ports[1])
  end for;
  connect(port_a, H_a_flow.port_a)

  connect(vol[1:nSeg].ports[3], str.fluidPort[2:nSeg+1])
  connect(H_a_flow.H_flow, str.H_flow[1])
  connect(H_vol_flow[1:nSeg-1].H_flow, str.H_flow[2:nSeg])
  connect(H_b_flow.H_flow, str.H_flow[nSeg + 1])
  connect(str.heatPort, vol.heatPort)
  connect(port_a, str.fluidPort[1])
  connect(port_b, str.fluidPort[nSeg + 2])
  connect(mTan_flow.y, str.m_flow)
end StratifiedEnhanced;


within AixLib.Fluid.Storage;
model StratifiedEnhancedInternalHex
  "A model of a water storage tank with a secondary loop and intenral heat exchanger"
  extends StratifiedEnhanced;

  replaceable package MediumHex =
      Modelica.Media.Interfaces.PartialMedium "Medium in the heat exchanger"

  parameter Modelica.Units.SI.Height hHex_a
    "Height of portHex_a of the heat exchanger, measured from tank bottom"

  parameter Modelica.Units.SI.Height hHex_b
    "Height of portHex_b of the heat exchanger, measured from tank bottom"

  parameter Integer hexSegMult(min=1) = 2
    "Number of heat exchanger segments in each tank segment"

  parameter Modelica.Units.SI.Diameter dExtHex=0.025
    "Exterior diameter of the heat exchanger pipe"

  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal
    "Heat transfer at nominal conditions"
  parameter Modelica.Units.SI.Temperature TTan_nominal
    "Temperature of fluid inside the tank at nominal heat transfer conditions"
  parameter Modelica.Units.SI.Temperature THex_nominal
    "Temperature of fluid inside the heat exchanger at nominal heat transfer conditions"
  parameter Real r_nominal(min=0, max=1)=0.5
    "Ratio between coil inside and outside convective heat transfer at nominal heat transfer conditions"

  parameter Modelica.Units.SI.MassFlowRate mHex_flow_nominal
    "Nominal mass flow rate through the heat exchanger"

  parameter Modelica.Units.SI.PressureDifference dpHex_nominal(displayUnit="Pa")=
       2500 "Pressure drop across the heat exchanger at nominal conditions"

  parameter Boolean computeFlowResistance=true
    "=true, compute flow resistance. Set to false to assume no friction"

  parameter Boolean from_dp=false
    "= true, use m_flow = f(dp) else dp = f(m_flow)"

  parameter Boolean linearizeFlowResistance=false
    "= true, use linear relation between m_flow and dp for any flow rate"

  parameter Real deltaM=0.1
    "Fraction of nominal flow rate where flow transitions to laminar"

  parameter Modelica.Fluid.Types.Dynamics energyDynamicsHex=
    Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Formulation of energy balance for heat exchanger internal fluid mass"

  parameter Modelica.Fluid.Types.Dynamics energyDynamicsHexSolid=energyDynamicsHex
    "Formulation of energy balance for heat exchanger solid mass"

  parameter Modelica.Units.SI.Length lHex=rTan*abs(segHex_a - segHex_b)*
      Modelica.Constants.pi "Approximate length of the heat exchanger"

  parameter Modelica.Units.SI.Area ACroHex=(dExtHex^2 - (0.8*dExtHex)^2)*
      Modelica.Constants.pi/4 "Cross sectional area of the heat exchanger"

  parameter Modelica.Units.SI.SpecificHeatCapacity cHex=490
    "Specific heat capacity of the heat exchanger material"

  parameter Modelica.Units.SI.Density dHex=8000
    "Density of the heat exchanger material"

  parameter Modelica.Units.SI.HeatCapacity CHex=ACroHex*lHex*dHex*cHex
    "Capacitance of the heat exchanger without the fluid"
  parameter Boolean allowFlowReversalHex = true
    "= true to allow flow reversal in heat exchanger, false restricts to design direction (portHex_a -> portHex_b)"
  Modelica.Fluid.Interfaces.FluidPort_a portHex_a(
    redeclare final package Medium =MediumHex,
     m_flow(min=if allowFlowReversalHex then -Modelica.Constants.inf else 0))
    "Heat exchanger inlet"
  Modelica.Fluid.Interfaces.FluidPort_b portHex_b(
     redeclare final package Medium = MediumHex,
     m_flow(max=if allowFlowReversalHex then Modelica.Constants.inf else 0))
    "Heat exchanger outlet"

  BaseClasses.IndirectTankHeatExchanger indTanHex(
    redeclare final package MediumTan = Medium,
    redeclare final package MediumHex = MediumHex,
    final nSeg=nSegHex,
    final CHex=CHex,
    final volHexFlu=volHexFlu,
    final Q_flow_nominal=Q_flow_nominal,
    final TTan_nominal=TTan_nominal,
    final THex_nominal=THex_nominal,
    final r_nominal=r_nominal,
    final dExtHex=dExtHex,
    final dp_nominal=dpHex_nominal,
    final m_flow_nominal=mHex_flow_nominal,
    final energyDynamics=energyDynamicsHex,
    final energyDynamicsSolid=energyDynamicsHexSolid,
    final computeFlowResistance=computeFlowResistance,
    from_dp=from_dp,
    final linearizeFlowResistance=linearizeFlowResistance,
    final deltaM=deltaM,
    final allowFlowReversal=allowFlowReversalHex,
    final m_flow_small=1e-4*abs(mHex_flow_nominal))
    "Heat exchanger inside the tank"

  Modelica.Units.SI.HeatFlowRate QHex_flow=-sum(indTanHex.port.Q_flow)
    "Heat transferred from the heat exchanger to the tank";
protected
  final parameter Integer segHex_a = nSeg-integer(hHex_a/segHeight)
    "Tank segment in which port a1 of the heat exchanger is located in"

  final parameter Integer segHex_b = nSeg-integer(hHex_b/segHeight)
    "Tank segment in which port b1 of the heat exchanger is located in"

  final parameter Modelica.Units.SI.Height segHeight=hTan/nSeg
    "Height of each tank segment (relative to bottom of same segment)";

  final parameter Modelica.Units.SI.Length dHHex=abs(hHex_a - hHex_b)
    "Vertical distance between the heat exchanger inlet and outlet";

  final parameter Modelica.Units.SI.Volume volHexFlu=Modelica.Constants.pi*(0.8
      *dExtHex)^2/4*lHex "Volume of the heat exchanger";

  final parameter Integer nSegHexTan=
    if segHex_a > segHex_b then segHex_a-segHex_b + 1 else segHex_b-segHex_a + 1
    "Number of tank segments the heat exchanger resides in";

  final parameter Integer nSegHex = nSegHexTan*hexSegMult
    "Number of heat exchanger segments";

initial equation
  assert(hHex_a >= 0 and hHex_a <= hTan,
    "The parameter hHex_a is outside its valid range.");

  assert(hHex_b >= 0 and hHex_b <= hTan,
    "The parameter hHex_b is outside its valid range.");

  assert(dHHex > 0,
    "The parameters hHex_a and hHex_b must not be equal.");
equation
  for j in 0:nSegHexTan-1 loop
    for i in 1:hexSegMult loop
      connect(indTanHex.port[j*hexSegMult+i], heaPorVol[segHex_a + (if hHex_a > hHex_b then j else -j)])
    end for;
  end for;
  connect(portHex_a, indTanHex.port_a)
  connect(indTanHex.port_b, portHex_b)
end StratifiedEnhancedInternalHex;


within AixLib.Fluid.Storage;
model TwoPhaseSeparator
  "Model of a fully-mixed two-phase tank that works as ideally phase seperator"

  // Definition of medium
  //
  replaceable package Medium =
   Modelica.Media.Water.WaterIF97_ph
      constrainedby Modelica.Media.Interfaces.PartialTwoPhaseMedium
      "Medium in the component"

  // Parameters describing tank's geometry
  //
  parameter Modelica.Units.SI.Volume VTanInn=2e-3
    "Inner total volume of the tank"
  parameter Modelica.Units.SI.Area ATanInn=VTanInn/hTanInn
    "Inner cross-sectional area of the tank"
  parameter Modelica.Units.SI.Length hTanInn=0.5 "Inner height of the tank"
  parameter Modelica.Units.SI.Diameter dTanInn=sqrt(4*ATanInn/Modelica.Constants.pi)
    "Inner diameter of the tank"

  // Parameters describing tank's heat losses if computed
  //
  parameter Boolean useHeatLoss = false
    "= true, if heat losses are computed"

  parameter Modelica.Units.SI.Length sIns=0.1 "Thickness of insulation"

  parameter Modelica.Units.SI.ThermalConductivity lamIns=0.04
    "Thermal conductivity of the insulation"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer alpInn=100
    "Inner mean heat transfer coefficient"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer alpOut=10
    "Outer mean heat transfer coefficient"

  // Definition of parameters describing assumptions
  //
  parameter Boolean allowFlowReversal = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"

  // Definition of parameters describing initialisation and nominal conditions
  //
  parameter Modelica.Units.SI.PressureDifference dp_start(displayUnit="Pa") = 0
    "Guess value of dp = port_a.p - port_b.p"
  parameter Medium.MassFlowRate m_flow_start_a = 0.1
    "Guess value of port_a.m_flow"
  parameter Medium.MassFlowRate m_flow_start_b = 0.1
    "Guess value of port_b.m_flow"

  parameter Boolean steSta = false
    "= true, if tank is initialised steady state"
  parameter Modelica.Units.SI.Volume VLiq0=0.2*VTanInn
    "Volume of the liquid phase at initialisation"
  parameter Modelica.Units.SI.AbsolutePressure pTan0=10e5
    "Mean pressure of the medium in the tank at initialisation"
  parameter Modelica.Units.SI.SpecificEnthalpy hTan0=300e3
    "Mean specific enthalpy of the medium in the tank at initialisation"

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=0.1
    "Nominal mass flow rate"
  parameter Medium.MassFlowRate m_flow_small = 1e-6*m_flow_nominal
    "Small mass flow rate for regularization of zero flow"

  // Definitions of parameters describing diagnostics
  //
  parameter Boolean show_T = false
    "= true, if temperatures at port_a and port_b are computed"
  parameter Boolean show_V_flow = false
    "= true, if volume flow rate at port_a and port_b are computed"
  parameter Boolean show_tankProperties = true
    "= true, if tank properties are included as summary record"
  parameter Boolean show_tankPropertiesDetailed = false
    "= true, if more detailed tank properties are included as summary record"
  parameter Boolean show_heatLosses = false
    "= true, if heat losses are included as summary record"

  // Definition of variables
  //
  Medium.ThermodynamicState staTan
    "Thermodynamic state of the medium in the tank";
  Medium.SaturationProperties satTan
    "Saturation properties of the medium in the tank";

  record TankProperties
    "Record that contains properties of the tank"
    Modelica.Units.SI.AbsolutePressure pTan
      "Mean pressure of the medium in the tank";
    Modelica.Units.SI.Temperature TTan
      "Mean temperature of the medium in the tank";
    Modelica.Units.SI.Density dTan "Mean density of the medium in the tank";
    Modelica.Units.SI.SpecificEnthalpy hTan
      "Mean specific enthalpy of the medium in the tank";

    Modelica.Units.SI.Density dLiq "Density of the liquid phase";
    Modelica.Units.SI.Density dVap "Density of the vapour phase";
    Modelica.Units.SI.SpecificEnthalpy hLiq
      "Specific enthalpy of the liquid phase";
    Modelica.Units.SI.SpecificEnthalpy hVap
      "Specific enthalpy of the vapour phase";

    Modelica.Units.SI.SpecificEnthalpy hInn "Specific enthalpy at tank's Innet";
    Modelica.Units.SI.SpecificEnthalpy hOut
      "Specific enthalpy at tank's outlet";

    Modelica.Units.SI.Volume VLiq "Volume of the liquid phase";
    Modelica.Units.SI.Volume VVap "Volume of the vapour phase";
    Modelica.Units.SI.Mass mTan "Mass of the medium in the tank";
    Modelica.Units.SI.Mass mLiq "Mass of the liquid phase";
    Modelica.Units.SI.Mass mVap "Mass of the vapour phase";

    Real levTan(unit="1")
      "Relative level of the tank";
    Real quaTan(unit="1")
      "Quality of the tank's medium";
  end TankProperties;

  record TankPropertiesDetailed
    "Record that contains detailed properties of the tank"
    Modelica.Units.SI.Mass mXi[Medium.nXi]
      "Masses of independent components in the fluid";
    Modelica.Units.SI.Mass mC[Medium.nC]
      "Masses of trace substances in the fluid";

    Modelica.Units.SI.MassFlowRate dMTan "Change in tank's mass wrt. time";
    Modelica.Units.SI.Power dUTan "Change in tank's internal energy wrt. time";
    Medium.DerDensityByEnthalpy ddhp
    "Density derivative w.r.t. specific enthalpy";
    Medium.DerDensityByPressure ddph
    "Density derivative w.r.t. pressure";

    Real quaInn(unit="1")
      "Quality of the tank's medium at inlet";
    Real quaOut(unit="1")
      "Quality of the tank's medium at outlet";
  end TankPropertiesDetailed;

  record HeatLosses
    "Record that contains properties of calculated heat losses"
    Modelica.Units.SI.ThermalConductance G
      "Thermal conductance of the tank's sheat";
    Modelica.Units.SI.ThermalConductance GShe
      "Thermal conductance of the tank's sheat";
    Modelica.Units.SI.ThermalConductance GTopBot
      "Thermal conductance of the tank's top and bottom";
    Modelica.Units.SI.TemperatureDifference dTHeaLos
      "Temperature difference between tank and ambient";
    Modelica.Units.SI.Power Q_flow_loss "Heat losses from tank to ambient";
  end HeatLosses;

  // Definition of records that summarise variables computed in the model
  //
  TankProperties tankProperties(
    pTan=pTan,
    TTan=TTan,
    dTan=dTan,
    hTan=hTan,
    dLiq=dLiq,
    dVap=dVap,
    hLiq=hLiq,
    hVap=hVap,
    hInn=hInn,
    hOut=hOut,
    VLiq=VLiq,
    VVap=VVap,
    mTan=mTan,
    mLiq=mLiq,
    mVap=mVap,
    levTan=levTan,
    quaTan=quaTan) if show_tankProperties
    "Record that summarises basic tank properties";
  TankPropertiesDetailed tankPropertiesDetailed(
      mXi=mXi,
      mC=mC,
      dMTan=dMTan,
      dUTan=dUTan,
      ddhp=ddhp,
      ddph=ddph,
      quaInn=quaInn,
      quaOut=quaOut) if show_tankPropertiesDetailed
    "Record that summarises detailed tank properties";
  HeatLosses heatLosses(
    G=G,
    GShe=GShe,
    GTopBot=GTopBot,
    dTHeaLos=TTan-heatPort.T,
    Q_flow_loss=heaFloSen.Q_flow) if (show_heatLosses and useHeatLoss)
    "Record that contains properties of calculated heat losses";

  // Definition of connectors and models
  //
  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare final package Medium = Medium,
     m_flow(min=if allowFlowReversal then -Modelica.Constants.inf else 0),
     h_outflow(start = Medium.h_default))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare final package Medium = Medium,
    m_flow(max=if allowFlowReversal then +Modelica.Constants.inf else 0),
     h_outflow(start = Medium.h_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatPort if useHeatLoss
    "Heat port for heat losses if losses are computed"

  // Calculating diagnostic values
  //
  Modelica.Units.SI.VolumeFlowRate port_a_V_flow=port_a.m_flow/
      Modelica.Fluid.Utilities.regStep(
      port_a.m_flow,
      Medium.density(Medium.setState_phX(
        p=port_a.p,
        h=inStream(port_a.h_outflow),
        X=inStream(port_a.Xi_outflow))),
      Medium.density(Medium.setState_phX(
        p=port_b.p,
        h=inStream(port_b.h_outflow),
        X=inStream(port_b.Xi_outflow))),
      m_flow_small) if show_V_flow
    "Volume flow rate at port_a (positive when flow from port_a to port_b)";
  Modelica.Units.SI.VolumeFlowRate port_b_V_flow=port_b.m_flow/
      Modelica.Fluid.Utilities.regStep(
      port_b.m_flow,
      Medium.density(Medium.setState_phX(
        p=port_b.p,
        h=inStream(port_b.h_outflow),
        X=inStream(port_b.Xi_outflow))),
      Medium.density(Medium.setState_phX(
        p=port_a.p,
        h=inStream(port_a.h_outflow),
        X=inStream(port_a.Xi_outflow))),
      m_flow_small) if show_V_flow
    "Volume flow rate at port_b (positive when flow from port_a to port_b)";

  Medium.Temperature port_a_T=
      Modelica.Fluid.Utilities.regStep(port_a.m_flow,
                  Medium.temperature(
                    Medium.setState_phX(
                      p = port_a.p,
                      h = inStream(port_a.h_outflow),
                      X = inStream(port_a.Xi_outflow))),
                  Medium.temperature(
                    Medium.setState_phX(
                      p = port_a.p,
                      h = port_a.h_outflow,
                      X = port_a.Xi_outflow)),
                  m_flow_small) if show_T
    "Temperature close to port_a, if show_T = true";
  Medium.Temperature port_b_T=
      Modelica.Fluid.Utilities.regStep(port_b.m_flow,
                  Medium.temperature(
                    Medium.setState_phX(
                      p = port_b.p,
                      h = inStream(port_b.h_outflow),
                      X = inStream(port_b.Xi_outflow))),
                  Medium.temperature(
                    Medium.setState_phX(
                      p = port_b.p,
                      h = port_b.h_outflow,
                      X = port_b.Xi_outflow)),
                  m_flow_small) if show_T
    "Temperature close to port_b, if show_T = true";

protected
  parameter Modelica.Units.SI.ThermalConductance G=GShe + 2*GTopBot
    "Thermal conductance of the tank's sheat"
  parameter Modelica.Units.SI.ThermalConductance GShe=2*Modelica.Constants.pi*
      hTanInn/(1/(alpInn*(dTanInn/2)) + 1/lamIns*log(1 + sIns/(dTanInn/2)) + 1/
      (alpOut*(dTanInn/2 + sIns))) "Thermal conductance of the tank's sheat"
  parameter Modelica.Units.SI.ThermalConductance GTopBot=ATanInn/(1/alpInn +
      sIns/lamIns + 1/alpOut)
    "Thermal conductance of the tank's top and bottom"

  Modelica.Units.SI.AbsolutePressure pTan(start=pTan0)
    "Mean pressure of the medium in the tank";
  Modelica.Units.SI.Temperature TTan
    "Mean temperature of the medium in the tank";
  Modelica.Units.SI.Density dTan "Mean density of the medium in the tank";
  Modelica.Units.SI.SpecificEnthalpy hTan(start=hTan0)
    "Mean specific enthalpy of the medium in the tank";

  Modelica.Units.SI.Density dLiq "Density of the liquid phase";
  Modelica.Units.SI.Density dVap "Density of the vapour phase";
  Modelica.Units.SI.SpecificEnthalpy hLiq
    "Specific enthalpy of the liquid phase";
  Modelica.Units.SI.SpecificEnthalpy hVap
    "Specific enthalpy of the vapour phase";

  Modelica.Units.SI.SpecificEnthalpy hInn "Specific enthalpy at tank's Innet";
  Modelica.Units.SI.SpecificEnthalpy hOut "Specific enthalpy at tank's outlet";

  Modelica.Units.SI.Volume VLiq(start=VLiq0) "Volume of the liquid phase";
  Modelica.Units.SI.Volume VVap "Volume of the vapour phase";
  Modelica.Units.SI.Mass mTan "Mass of the medium in the tank";
  Modelica.Units.SI.Mass mLiq "Mass of the liquid phase";
  Modelica.Units.SI.Mass mVap "Mass of the vapour phase";
  Modelica.Units.SI.Mass mXi[Medium.nXi]
    "Masses of independent components in the fluid";
  Modelica.Units.SI.Mass mC[Medium.nC]
    "Masses of trace substances in the fluid";

  Modelica.Units.SI.MassFlowRate dMTan "Change in tank's mass wrt. time";
  Modelica.Units.SI.Power dUTan "Change in tank's internal energy wrt. time";
  Medium.DerDensityByEnthalpy ddhp
  "Density derivative w.r.t. specific enthalpy";
  Medium.DerDensityByPressure ddph
  "Density derivative w.r.t. pressure";

  Real levTan(unit="1")
    "Relative level of the tank";
  Real quaTan(unit="1")
    "Quality of the tank's medium";
  Real quaInn(unit="1")
    "Quality of the tank's medium at inlet";
  Real quaOut(unit="1")
    "Quality of the tank's medium at outlet";

  Real eps = Modelica.Constants.eps
    "Bigest number such that 1.0 + eps = 1.0";
  Real pTriCri
    "Trigger to check if tank's medium exceeds critical pressure";

  Modelica.Thermal.HeatTransfer.Components.ThermalConductor
    heaTran(G=G) if useHeatLoss
    "Model to calculate heat losses"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFloSen
    "Sensor measuring heat losses in order to use measured value for calculations"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature preTem
    "Convert input temperature to heat port temperature"
  Modelica.Blocks.Sources.RealExpression porTem(y=TTan)
    "Source block to set temperature to calculate heat losses"

initial equation
  if steSta then
   der(hTan) = 0;
  else
    hTan = hTan0;
  end if;

equation
  // Introduction of assertion to check model's boundaries
  //
  assert(VLiq/VTanInn <= 1, "Caution: Relative tank level is greater than
    maximum tank capacity! Increase the tank size!",
    level = AssertionLevel.warning)
    "Checking if the tank is undersized";

  // Calculation of thermodynamic states
  //
  satTan = Medium.setSat_p(pTan)
    "Saturation properties based on pressure of the tank's medium";
  staTan = Medium.setState_ph(pTan,hTan)
    "Mean thermodynamic state of the tank's medium";

  dTan = Medium.density(staTan) "Mean density of the tank's medium";
  TTan = Medium.temperature(staTan) "Mean temperature of the tank's medium";

  dLiq = Medium.bubbleDensity(satTan) "Liquid density";
  dVap = Medium.dewDensity(satTan) "Vapour density";
  hLiq = Medium.bubbleEnthalpy(satTan) "Liquid enthalpy";
  hVap = Medium.dewEnthalpy(satTan) "Vapour enthalpy";

  ddhp = Medium.density_derh_p(staTan) "Density derivative wrt. hTan";
  ddph = Medium.density_derp_h(staTan) "Density derivative wrt. pTan";

  pTriCri = pTan/Medium.fluidConstants[1].criticalPressure
    "Trigger to check if tank's medium exceeds ciritical pressure (>=1)";
  quaTan = noEvent(if (pTriCri<1 and hTan<hLiq) then 0
    else if (pTriCri<1 and hTan>hLiq and hTan<hVap) then
    (hTan - hLiq)/max(hVap - hLiq, 1e-6) else 1.0)
    "Mean vapour quality of the tank's medium";
  quaInn = noEvent(if (pTriCri<1 and hInn<hLiq) then 0
    else if (pTriCri<1 and hInn>hLiq and hInn<hVap) then
    (hInn - hLiq)/max(hVap - hLiq, 1e-6) else 1.0)
    "Mean vapour quality of the tank's medium at inlet conditions";
  quaOut = noEvent(if (pTriCri<1 and hOut<hLiq) then 0
    else if (pTriCri<1 and hOut>hLiq and hOut<hVap) then
    (hOut - hLiq)/max(hVap - hLiq, 1e-6) else 1.0)
    "Mean vapour quality of the tank's medium at outlet conditions";

  // Calculation of basic properties assuming thermodynamic equilibrium
  //
  VTanInn = VLiq + VVap "Geometric condition";

  mTan = dTan*VTanInn "Basic relationship";
  mTan = mLiq + mVap  "Converstion of mass";
  VLiq = smooth(1, noEvent(if (quaTan > 0.0 and quaTan < 1.0) then
    mTan*(1-quaTan)/dLiq else if (quaTan >= 1.0) then 0 else VTanInn))
    "Volume of liquid phase depending on tank's actual quality";
  mVap = smooth(1, noEvent(if (quaTan > 0.0 and quaTan < 1.0) then
    mTan*quaTan else if (quaTan >= 1.0) then mTan else 0.0))
    "Mass of vapour phase depending on tank's actual quality";
  /*The calculation procedure of the phases' masses is conducted as presented
    above to ensure two things. Firstly, to ensure that the phases' masses cannot
    be negative and, secondly, to ensure that the phases' masses are resticrted
    by the maximum tank capacity.
  */

  levTan = min(VLiq/VTanInn,1) "Relative level of the tank";

  // Calculation of boundaries and add equations for flow reversal
  //
  pTan = port_a.p "Assuming thermodynamic equilibrium";
  0 = port_a.p - port_b.p "Assuming thermodynamic equilibrium";

  hInn = actualStream(port_a.h_outflow);
  port_a.h_outflow = smooth(1,noEvent(if (levTan >= 1-eps) or (levTan <= eps)
    or (pTriCri>=1) then hTan else hVap))
    "Check relative tank level and set specific enthalpy at tank's inlet";
  /*If flow direction identical to design direction, the inlet specific
    enthalpy is identical to the specific enthalpy at the inlet port. Otherwise,
    vapour state or mean enthalpy is forced.
  */
  hOut = actualStream(port_b.h_outflow);
  port_b.h_outflow = smooth(1,noEvent(if (levTan >= 1-eps) or (levTan <= eps)
    or (pTriCri>=1) then hTan else hLiq))
    "Check relative tank level and set specific enthalpy at tank's outlet";
  /*If flow direction identical to design direction, the outlet specific
    enthalpy is identical to the liquid state or mean specific enthalpy.
    Otherwise, the specific enthalpy is identical to the specific enthalpy at
    tank's outlet.
  */

  // Calculation of conversation equations
  //
  dMTan = port_a.m_flow + port_b.m_flow
    "Mass balance: Ports' side";
  dMTan = VTanInn*(ddph*der(pTan) + ddhp*der(hTan))
    "Mass balance: Tank's side";

  dUTan = port_a.m_flow*actualStream(port_a.h_outflow) + port_b.m_flow*
    actualStream(port_b.h_outflow) - heaFloSen.Q_flow
    "Energy balance: Port's side";
  dUTan =VTanInn*(hTan*(ddph*der(pTan) + ddhp*der(hTan)) + dTan*der(hTan) -
    der(pTan))
    "Energy balance: Tank's side";

  // Calculation of conservation equations of transported substances
  //
  der(mXi) = port_a.m_flow*actualStream(port_a.Xi_outflow) +
    port_b.m_flow*actualStream(port_b.Xi_outflow);
  der(mC) = port_a.m_flow*actualStream(port_a.C_outflow) +
    port_b.m_flow*actualStream(port_b.C_outflow);

  // Connections describing heat losses if computed
  //
  connect(porTem.y, preTem.T)
  connect(preTem.port, heaFloSen.port_a)
  connect(heaFloSen.port_b, heaTran.port_a)
  connect(heaTran.port_b, heatPort)
end TwoPhaseSeparator;


User's Guide

within AixLib.Fluid.Storage;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;
end UsersGuide;


<span style=\.

within AixLib.Fluid.Storage.BaseClasses;
model Bouyancy
  parameter Modelica.Units.SI.Area A=1;
  parameter Modelica.Units.SI.RelativePressureCoefficient beta=350e-6;
  parameter Modelica.Units.SI.Length dx=0.2;
  parameter Real kappa = 0.4;
  Modelica.Units.SI.TemperatureDifference dT;
  Modelica.Units.SI.ThermalConductivity lambda_eff;
  parameter Modelica.Units.SI.Acceleration g=Modelica.Constants.g_n;
  Modelica.Units.SI.SpecificHeatCapacity cp=4180;
  Modelica.Units.SI.ThermalConductivity lambda=0.598;
  Modelica.Units.SI.Density rho=1000;
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_a
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_b port_b
equation
  dT = port_a.T - port_b.T;
  if dT > 0 then
    lambda_eff = lambda;
  else
    lambda_eff = lambda + 2 / 3 * rho * cp * kappa * dx ^ 2 * sqrt(abs(-g * beta * dT / dx));
  end if;
  port_a.Q_flow = lambda_eff * A / dx * dT;
  port_a.Q_flow + port_b.Q_flow = 0;
end Bouyancy;


within AixLib.Fluid.Storage.BaseClasses;
model Buoyancy
  "Model to add buoyancy if there is a temperature inversion in the tank"
  extends Modelica.Blocks.Icons.Block;

  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium model"
  parameter Modelica.Units.SI.Volume V "Volume";
  parameter Integer nSeg(min=2) = 2 "Number of volume segments";
  parameter Modelica.Units.SI.Time tau(min=0) "Time constant for mixing";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a[nSeg] heatPort
    "Heat input into the volumes"

  Modelica.Units.SI.HeatFlowRate[nSeg - 1] Q_flow
    "Heat flow rate from segment i+1 to i";
protected
   parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX(T=Medium.T_default,
         p=Medium.p_default, X=Medium.X_default[1:Medium.nXi])
    "Medium state at default properties";
  parameter Modelica.Units.SI.Density rho_default=Medium.density(sta_default)
    "Density, used to compute fluid mass";
  parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.specificHeatCapacityCp(sta_default) "Specific heat capacity";
   parameter Real k(unit="W/K") = V*rho_default*cp_default/tau/nSeg
    "Proportionality constant, since we use dT instead of dH";
  Modelica.Units.SI.TemperatureDifference dT[nSeg - 1]
    "Temperature difference between adjoining volumes";
equation
  for i in 1:nSeg-1 loop
    dT[i] = heatPort[i+1].T-heatPort[i].T;
    Q_flow[i] = k*noEvent(smooth(1, if dT[i]>0 then dT[i]^2 else 0));
  end for;

  heatPort[1].Q_flow = -Q_flow[1];
  for i in 2:nSeg-1 loop
       heatPort[i].Q_flow = -Q_flow[i]+Q_flow[i-1];
  end for;
  heatPort[nSeg].Q_flow = Q_flow[nSeg-1];
since the equation returns the same value to the left and right of
dT[i]>0.

September 16, 2011 by Michael Wetter:

Changed the implementation from Q_flow[i] = k*max(heatPort[i+1].T-heatPort[i].T, 0); to
Q_flow[i] = k*smooth(1, if dT[i]>0 then dT[i]^2 else 0);.
The previous implementation was not differentiable. In modeling a solar system, this
change reduced the computing time by a factor of 20 during the time when the pumps
were almost switched off and colder temperature was fed from the collector to the tank.

October 28, 2008 by Michael Wetter:

First implementation.

"),
Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,100}}),
        graphics={
        Rectangle(
          extent={{-44,68},{36,28}},
          lineColor={0,0,255},
          pattern=LinePattern.None,
          fillColor={0,0,127},
          fillPattern=FillPattern.Solid),
        Rectangle(
          extent={{-42,-26},{38,-66}},
          lineColor={255,0,0},
          fillColor={255,0,0},
          fillPattern=FillPattern.Solid),
        Rectangle(
          extent={{26,10},{32,-22}},
          lineColor={0,0,255},
          pattern=LinePattern.None,
          fillColor={255,0,0},
          fillPattern=FillPattern.Solid),
        Polygon(
          points={{28,22},{22,10},{36,10},{36,10},{28,22}},
          lineColor={0,0,255},
          pattern=LinePattern.None,
          fillColor={255,0,0},
          fillPattern=FillPattern.Solid),
        Rectangle(
          extent={{-32,22},{-26,-10}},
          lineColor={0,0,255},
          pattern=LinePattern.None,
          fillColor={0,0,127},
          fillPattern=FillPattern.Solid),
        Polygon(
          points={{-28,-18},{-36,-6},{-22,-6},{-22,-6},{-28,-18}},
          lineColor={0,0,255},
          pattern=LinePattern.None,
          fillColor={0,0,127},
          fillPattern=FillPattern.Solid)}),
   __Dymola_LockedEditing="Model from IBPSA");
end Buoyancy;


Heating coil for heat storage model

within AixLib.Fluid.Storage.BaseClasses;
model HeatingCoil "Heating coil for heat storage model"
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface;

 parameter Integer disHC = 2 "Number of elements for heating coil discretization";

  parameter Modelica.Units.SI.Length lengthHC=3 "Length of Pipe for HC";

  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConHC=20
    "Model assumptions heat transfer coefficient HC <-> Heating Water";

  parameter Modelica.Units.SI.Temperature TStart=298.15
    "Start Temperature of fluid"

 parameter AixLib.DataBase.Pipes.PipeBaseDataDefinition pipeHC=
      AixLib.DataBase.Pipes.Copper.Copper_28x1() "Type of Pipe for HC";

  AixLib.Utilities.HeatTransfer.HeatConv convHC1Outside[disHC](each final hCon=hConHC, each final A=pipeHC.d_o*Modelica.Constants.pi*lengthHC/disHC)
                "Outer heat convection"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a Therm1[disHC]
    "Vectorized heat port"

  FixedResistances.PlugFlowPipe pipe[disHC](
    redeclare each final package Medium = Medium,
    each final allowFlowReversal=allowFlowReversal,
    each final dh=pipeHC.d_i,
    each final v_nominal=4*m_flow_nominal/den_default/pipeHC.d_i/pipeHC.d_i/Modelica.Constants.pi,
    each final length=lengthHC/disHC,
    each final m_flow_nominal=m_flow_nominal,
    each final m_flow_small=m_flow_small,
    each final dIns=0.5*(pipeHC.d_o - pipeHC.d_i),
    each final kIns=pipeHC.lambda,
    each final cPip=pipeHC.c,
    each final rhoPip=pipeHC.d,
    each final thickness=0.5*(pipeHC.d_o - pipeHC.d_i),
    each final T_start_in=TStart,
    each final T_start_out=TStart)

protected
  parameter Medium.ThermodynamicState sta_default=
     Medium.setState_pTX(T=Medium.T_default, p=Medium.p_default, X=Medium.X_default);
  parameter Modelica.Units.SI.Density den_default=Medium.density(sta_default)
    "Density of Medium in default state";
  parameter Modelica.Units.SI.SpecificHeatCapacity cp_default=
      Medium.heatCapacity_cp(sta_default)
    "Specific heat capacity of Medium in default state";

equation

  connect(convHC1Outside.port_a, Therm1)

  connect(convHC1Outside.port_b, pipe.heatPort)
  connect(port_a, pipe[1].port_a)
  for i in 1:disHC-1 loop
    connect(pipe[i].port_b, pipe[i + 1].port_a)
  end for;
  connect(pipe[disHC].port_b, port_b)
end HeatingCoil;


Heat transfer with buoyancy as in Buildings library.

within AixLib.Fluid.Storage.BaseClasses;
model HeatTransferBuoyancyWetter "Heat transfer with buoyancy as in Buildings library"
  extends AixLib.Fluid.Storage.BaseClasses.PartialHeatTransferLayers;

  parameter Modelica.Units.SI.Time tau(min=0) = 100 "Time constant for mixing";
  Modelica.Units.SI.HeatFlowRate[n - 1] qFlow
    "Heat flow rate from segment i+1 to i";

protected
  parameter Modelica.Units.SI.Density rho0=1000
    "Density, used to compute fluid mass";
  parameter Modelica.Units.SI.SpecificHeatCapacity cp0=4180
    "Specific heat capacity";
  Modelica.Units.SI.TemperatureDifference dT[n - 1]
    "Temperature difference between adjoining volumes";
   parameter Real k(unit="W/K2") = data.hTank*Modelica.Constants.pi/4*data.dTank^2*rho0*cp0/tau/n
    "Proportionality constant, since we use dT instead of dH";
equation

  for i in 1:n-1 loop
    dT[i] = therm[i].T-therm[i+1].T;
    qFlow[i] = k*noEvent(smooth(1, if dT[i]>0 then dT[i]^2 else 0));
  end for;

  therm[1].Q_flow = qFlow[1];
  for i in 2:n-1 loop
       therm[i].Q_flow = qFlow[i]-qFlow[i-1];
  end for;
  therm[n].Q_flow = -qFlow[n-1];
end HeatTransferBuoyancyWetter;


Heat transfer with buoyancy and effective conductivity.

within AixLib.Fluid.Storage.BaseClasses;
model HeatTransferLambdaEff "Heat transfer with buoyancy and effective conductivity"
  extends AixLib.Fluid.Storage.BaseClasses.PartialHeatTransferLayers;

  Modelica.Units.SI.HeatFlowRate[n - 1] qFlow
    "Heat flow rate from segment i+1 to i";

protected
  parameter Real kappa=0.41 "Karman constant";
  parameter Modelica.Units.SI.Length height=data.hTank/n
    "Height of fluid layers";
  parameter Real beta=350e-6 "Thermal expansion coefficient in 1/K";
  parameter Modelica.Units.SI.Area A=Modelica.Constants.pi/4*data.dTank^2
    "Area of heat transfer between layers";
  parameter Modelica.Units.SI.Density rho=1000
    "Density, used to compute fluid mass";
  parameter Modelica.Units.SI.SpecificHeatCapacity c_p=4180
    "Specific heat capacity";
                              //
  Modelica.Units.SI.TemperatureDifference dT[n - 1]
    "Temperature difference between adjoining volumes";
  Modelica.Units.SI.ThermalConductance[n - 1] k
    "Effective heat transfer coefficient";
  Modelica.Units.SI.ThermalConductivity[n - 1] lambda
    "Effective heat conductivity";
  parameter Modelica.Units.SI.ThermalConductivity lambdaWater=0.64
    "Thermal conductivity of water";
equation

  for i in 1:n-1 loop
    dT[i] = therm[i].T-therm[i+1].T;
    lambda[i]^2=noEvent(max((9.81*beta*dT[i]/height)*(2/3*rho*c_p*kappa*height^2)^2,0));
    k[i]=(noEvent(if dT[i]>0 then lambda[i] else 0)+lambdaWater)*A/height;
    qFlow[i] = k[i]*dT[i];
  end for;

// Positive heat flows here mean negative heat flows for the fluid layers
  therm[1].Q_flow = qFlow[1];
  for i in 2:n-1 loop
       therm[i].Q_flow = -qFlow[i-1]+qFlow[i];
  end for;
  therm[n].Q_flow = -qFlow[n-1];
end HeatTransferLambdaEff;


Heat transfer with buoyancy and smoothing.

within AixLib.Fluid.Storage.BaseClasses;
model HeatTransferLambdaEffSmooth "Heat transfer with buoyancy and smoothing"
  extends AixLib.Fluid.Storage.BaseClasses.PartialHeatTransferLayers;

  Modelica.Units.SI.HeatFlowRate[n - 1] qFlow
    "Heat flow rate from segment i+1 to i";

protected
  parameter Real kappa=0.41 "Karman constant";
  parameter Modelica.Units.SI.Length height=data.hTank/n
    "Height of fluid layers";
  parameter Real beta=350e-6 "Thermal expansion coefficient in 1/K";
  parameter Modelica.Units.SI.Area A=Modelica.Constants.pi/4*data.dTank^2
    "Area of heat transfer between layers";
  parameter Modelica.Units.SI.Density rho=1000
    "Density, used to compute fluid mass";
  parameter Modelica.Units.SI.SpecificHeatCapacity c_p=4180
    "Specific heat capacity";
                              //
  Modelica.Units.SI.TemperatureDifference dT[n - 1]
    "Temperature difference between adjoining volumes";
  Modelica.Units.SI.ThermalConductance[n - 1] k
    "Effective heat transfer coefficient";
  Modelica.Units.SI.ThermalConductivity[n - 1] lambda
    "Effective heat conductivity";
  parameter Modelica.Units.SI.ThermalConductivity lambdaWater=0.64
    "Thermal conductivity of water";
equation

  for i in 1:n-1 loop
    dT[i] = therm[i].T-therm[i+1].T;
    lambda[i]^2=noEvent(max((9.81*beta*dT[i]/height)*(2/3*rho*c_p*kappa*height^2)^2,0));
    k[i]=(noEvent(smooth(1,if dT[i]>0 then lambda[i] else 0))+lambdaWater)*A/height;
    qFlow[i] = k[i]*dT[i];
  end for;

// Positive heat flows here mean negative heat flows for the fluid layers
  therm[1].Q_flow = qFlow[1];
  for i in 2:n-1 loop
       therm[i].Q_flow = -qFlow[i-1]+qFlow[i];
  end for;
  therm[n].Q_flow = -qFlow[n-1];
end HeatTransferLambdaEffSmooth;


Heat transfer with effective conductivity.

within AixLib.Fluid.Storage.BaseClasses;
model HeatTransferLambdaSimple "Heat transfer with effective conductivity"
  extends AixLib.Fluid.Storage.BaseClasses.PartialHeatTransferLayers;

  Modelica.Units.SI.HeatFlowRate[n - 1] qFlow
    "Heat flow rate from segment i+1 to i";

  parameter Modelica.Units.SI.ThermalConductivity const_lambda_eff=100
    "Effective thermal conductivity";

  Modelica.Blocks.Logical.TriggeredTrapezoid triggeredTrapezoid[n - 1](
    each u(start=false),
    rising=100,
    falling=10,
    amplitude=const_lambda_eff,
    offset=lambdaWater)
    "Triggered trapezoid for conductivity between layers"

protected
  parameter Modelica.Units.SI.Length height=data.hTank/n
    "Height of fluid layers";
  parameter Modelica.Units.SI.Area A=Modelica.Constants.pi/4*data.dTank^2
    "Area of heat transfer between layers";
  Modelica.Units.SI.TemperatureDifference dT[n - 1]
    "Temperature difference between adjoining volumes";
  Modelica.Units.SI.ThermalConductance k[n - 1]
    "Effective heat transfer coefficient";
  parameter Modelica.Units.SI.ThermalConductivity lambdaWater=0.64
    "Thermal conductivity of water";

equation

  for i in 1:n-1 loop
    dT[i] = therm[i].T-therm[i+1].T;
    triggeredTrapezoid[i].u=dT[i]>0;
    k[i]=triggeredTrapezoid[i].y*A/height;
    qFlow[i] = k[i]*dT[i];
  end for;

  // Positive heat flows here mean negative heat flows for the fluid layers
  therm[1].Q_flow = qFlow[1];
  for i in 2:n-1 loop
       therm[i].Q_flow = -qFlow[i-1]+qFlow[i];
  end for;
  therm[n].Q_flow = -qFlow[n-1];
end HeatTransferLambdaSimple;


Heat transfer for conduction only.

within AixLib.Fluid.Storage.BaseClasses;
model HeatTransferOnlyConduction "Heat transfer for conduction only"
  extends AixLib.Fluid.Storage.BaseClasses.PartialHeatTransferLayers;
  Modelica.Units.SI.HeatFlowRate[n - 1] qFlow
    "Heat flow rate from segment i+1 to i";
protected
  parameter Modelica.Units.SI.Length height=data.hTank/n
    "Height of fluid layers";
  parameter Modelica.Units.SI.Area A=Modelica.Constants.pi/4*data.dTank^2
    "Area of heat transfer between layers";
  Modelica.Units.SI.TemperatureDifference dT[n - 1]
    "Temperature difference between adjoining volumes";
  parameter Modelica.Units.SI.ThermalConductivity lambdaWater=0.64
    "Thermal conductivity of water";
equation

  for i in 1:n-1 loop
    dT[i] = therm[i].T-therm[i+1].T;
    qFlow[i] = lambdaWater*A/height*dT[i];
  end for;

  // Positive heat flows here mean negative heat flows for the fluid layers
  therm[1].Q_flow = qFlow[1];
  for i in 2:n-1 loop
       therm[i].Q_flow = -qFlow[i-1]+qFlow[i];
  end for;
  therm[n].Q_flow = -qFlow[n-1];
end HeatTransferOnlyConduction;


within AixLib.Fluid.Storage.BaseClasses;
model IndirectTankHeatExchanger
  "Heat exchanger typically submerged in a fluid with a second fluid circulating through it"

  replaceable package MediumHex = Modelica.Media.Interfaces.PartialMedium
    "Heat transfer fluid flowing through the heat exchanger";
  replaceable package MediumTan = Modelica.Media.Interfaces.PartialMedium
    "Heat transfer fluid inside the tank";

  extends AixLib.Fluid.Interfaces.TwoPortFlowResistanceParameters;
  extends AixLib.Fluid.Interfaces.LumpedVolumeDeclarations(
    final massDynamics=energyDynamics,
    redeclare final package Medium = MediumHex);
  extends AixLib.Fluid.Interfaces.PartialTwoPortInterface(
    redeclare final package Medium = MediumHex,
    final show_T=false);

  constant Boolean homotopyInitialization = true "= true, use homotopy method"

  parameter Integer nSeg(min=2) "Number of segments in the heat exchanger";
  parameter Modelica.Units.SI.HeatCapacity CHex
    "Capacitance of the heat exchanger";
  parameter Modelica.Units.SI.Volume volHexFlu
    "Volume of heat transfer fluid in the heat exchanger";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal
    "Heat transfer at nominal conditions"

  final parameter Modelica.Units.SI.ThermalConductance UA_nominal=abs(
      Q_flow_nominal/(THex_nominal - TTan_nominal))
    "Nominal UA value for the heat exchanger";
  parameter Modelica.Units.SI.Temperature TTan_nominal
    "Temperature of fluid inside the tank at UA_nominal"
  parameter Modelica.Units.SI.Temperature THex_nominal
    "Temperature of fluid inside the heat exchanger at UA_nominal"
  parameter Real r_nominal(min=0, max=1)=0.5
    "Ratio between coil inside and outside convective heat transfer"

  parameter Modelica.Units.SI.Diameter dExtHex
    "Exterior diameter of the heat exchanger pipe";

  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Formulation of energy balance for heat exchanger internal fluid mass"
  parameter Modelica.Fluid.Types.Dynamics energyDynamicsSolid=energyDynamics
    "Formulation of energy balance for heat exchanger solid mass"

  parameter Boolean hA_flowDependent = true
    "Set to false to make the convective heat coefficient calculation of the fluid inside the coil independent of mass flow rate"
  parameter Boolean hA_temperatureDependent = true
    "Set to false to make the convective heat coefficient calculation of the fluid inside the coil independent of temperature"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port[nSeg]
    "Heat port connected to water inside the tank"

  AixLib.Fluid.FixedResistances.PressureDrop res(
    redeclare final package Medium = MediumHex,
    final dp_nominal=dp_nominal,
    final m_flow_nominal=m_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    final homotopyInitialization=homotopyInitialization,
    final show_T=show_T,
    final from_dp=from_dp,
    final linearized=linearizeFlowResistance)
    "Calculates the flow resistance and pressure drop through the heat exchanger"

  AixLib.Fluid.MixingVolumes.MixingVolume vol[nSeg](
    redeclare each package Medium = MediumHex,
    each nPorts=2,
    each m_flow_nominal=m_flow_nominal,
    each V=volHexFlu/nSeg,
    each energyDynamics=energyDynamics,
    each massDynamics=energyDynamics,
    each p_start=p_start,
    each T_start=T_start,
    each X_start=X_start,
    each C_start=C_start,
    each C_nominal=C_nominal,
    each final prescribedHeatFlowRate=false,
    each final allowFlowReversal=allowFlowReversal) "Heat exchanger fluid"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor cap[nSeg](
     each C=CHex/nSeg,
     each T(start=T_start,
            fixed=(energyDynamicsSolid == Modelica.Fluid.Types.Dynamics.FixedInitial)),
     each der_T(
            fixed=(energyDynamicsSolid == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)))
          if not energyDynamicsSolid == Modelica.Fluid.Types.Dynamics.SteadyState
    "Thermal mass of the heat exchanger"
protected
  AixLib.Fluid.Sensors.MassFlowRate senMasFlo(
    redeclare package Medium = MediumHex,
    allowFlowReversal=allowFlowReversal)
    "Mass flow rate of the heat transfer fluid"
  Modelica.Thermal.HeatTransfer.Components.Convection htfToHex[nSeg]
    "Convection coefficient between the heat transfer fluid and heat exchanger"
  Modelica.Thermal.HeatTransfer.Components.Convection HexToTan[nSeg]
    "Convection coefficient between the heat exchanger and the surrounding medium"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSenHex[nSeg]
    "Temperature of the heat transfer fluid"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSenWat[nSeg]
    "Temperature sensor of the fluid surrounding the heat exchanger"
  Modelica.Blocks.Routing.Replicator rep(nout=nSeg)
    "Replicates senMasFlo signal from 1 seg to nSeg"
  AixLib.Fluid.HeatExchangers.BaseClasses.HACoilInside hAPipIns[nSeg](
    each m_flow_nominal=m_flow_nominal,
    each hA_nominal=UA_nominal/nSeg*(r_nominal + 1)/r_nominal,
    each T_nominal=THex_nominal,
    each final flowDependent=hA_flowDependent,
    each final temperatureDependent=hA_temperatureDependent)
    "Computation of convection coefficients inside the coil"
  AixLib.Fluid.HeatExchangers.BaseClasses.HANaturalCylinder hANatCyl[nSeg](
    redeclare each final package Medium = Medium,
    each final ChaLen=dExtHex,
    each final hA_nominal=UA_nominal/nSeg*(1 + r_nominal),
    each final TFlu_nominal=TTan_nominal,
    each final TSur_nominal=TTan_nominal - (r_nominal/(1 + r_nominal))*(
        TTan_nominal - THex_nominal))
    "Calculates an hA value for each side of the heat exchanger"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSenSur[nSeg]
    "Temperature at the external surface of the heat exchanger"

initial equation
  assert(homotopyInitialization, "In " + getInstanceName() +
    ": The constant homotopyInitialization has been modified from its default value. This constant will be removed in future releases.",
    level = AssertionLevel.warning);

equation
  for i in 1:(nSeg - 1) loop
    connect(vol[i].ports[2], vol[i + 1].ports[1]);
  end for;

  connect(rep.u,senMasFlo. m_flow)
  connect(port,HexToTan. fluid)
  connect(vol[1].ports[1],senMasFlo.port_b)
  connect(cap.port,HexToTan.solid)
  connect(vol.heatPort,htfToHex. fluid)
  connect(htfToHex.solid,HexToTan. solid)
  connect(temSenHex.T, hAPipIns.T)
  connect(hAPipIns.hA, htfToHex.Gc)
  connect(HexToTan.solid,temSenSur. port)
  connect(temSenWat.port, port)
  connect(temSenSur.T, hANatCyl.TSur)
  connect(hANatCyl.TFlu, temSenWat.T)
  connect(port_a, senMasFlo.port_a)
  connect(vol[nSeg].ports[2], res.port_a)
  connect(res.port_b, port_b)
  connect(temSenHex.port, vol.heatPort)
  connect(rep.y, hAPipIns.m_flow)
  connect(hANatCyl.hA,HexToTan. Gc)
end IndirectTankHeatExchanger;


within AixLib.Fluid.Storage;
package BaseClasses "Package with base classes for AixLib.Fluid.Storage"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


within AixLib.Fluid.Storage.BaseClasses;
partial model PartialHeatTransferLayers "Partial for storage heat transfer models"

  parameter Integer n(min=2)=3 "Number of layers";
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a[n] therm
  replaceable package Medium =
      Modelica.Media.Interfaces.PartialMedium "Medium model"

  replaceable parameter
    AixLib.DataBase.Storage.StorageDetailedBaseDataDefinition data=
      AixLib.DataBase.Storage.Generic_New_2000l()
      "Storage data record"

end PartialHeatTransferLayers;


within AixLib.Fluid.Storage.BaseClasses;
model PartialStratified
  "Partial model of a stratified tank for thermal energy storage"
  extends AixLib.Fluid.Storage.BaseClasses.PartialTwoPortInterface;

  import Modelica.Fluid.Types;
  import Modelica.Fluid.Types.Dynamics;

  parameter Modelica.Units.SI.Volume VTan "Tank volume";
  parameter Modelica.Units.SI.Length hTan "Height of tank (without insulation)";
  parameter Modelica.Units.SI.Length dIns "Thickness of insulation";
  parameter Modelica.Units.SI.ThermalConductivity kIns=0.04
    "Specific heat conductivity of insulation";
  parameter Integer nSeg(min=2) = 2 "Number of volume segments";

  ////////////////////////////////////////////////////////////////////
  // Assumptions
  parameter Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial
    "Formulation of energy balance"

  // Initialization
  parameter Medium.AbsolutePressure p_start = Medium.p_default
    "Start value of pressure"
  parameter Medium.Temperature T_start=Medium.T_default
    "Start value of temperature"
  parameter Modelica.Units.SI.Temperature TFlu_start[nSeg]=T_start*ones(nSeg)
    "Initial temperature of the tank segments, with TFlu_start[1] being the top segment"
  parameter Medium.MassFraction X_start[Medium.nX] = Medium.X_default
    "Start value of mass fractions m_i/m"
  parameter Medium.ExtraProperty C_start[Medium.nC](
       quantity=Medium.extraPropertiesNames)=fill(0, Medium.nC)
    "Start value of trace substances"

  // Dynamics
  parameter Modelica.Units.SI.Time tau=1 "Time constant for mixing";

  ////////////////////////////////////////////////////////////////////
  // Connectors

  Modelica.Blocks.Interfaces.RealOutput Ql_flow
    "Heat loss of tank (positive if heat flows from tank to ambient)"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a[nSeg] heaPorVol
    "Heat port that connects to the control volumes of the tank"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorSid
    "Heat port tank side (outside insulation)"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorTop
    "Heat port tank top (outside insulation)"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heaPorBot
    "Heat port tank bottom (outside insulation). Leave unconnected for adiabatic condition"

  // Models
  AixLib.Fluid.MixingVolumes.MixingVolume[nSeg] vol(
    redeclare each package Medium = Medium,
    each energyDynamics=energyDynamics,
    each massDynamics=energyDynamics,
    each p_start=p_start,
    T_start=TFlu_start,
    each X_start=X_start,
    each C_start=C_start,
    each V=VTan/nSeg,
    each m_flow_nominal=m_flow_nominal,
    each final mSenFac=1,
    each final m_flow_small=m_flow_small,
    each final allowFlowReversal=allowFlowReversal) "Tank segment"

protected
  parameter Medium.ThermodynamicState sta_default = Medium.setState_pTX(
    T=Medium.T_default,
    p=Medium.p_default,
    X=Medium.X_default[1:Medium.nXi]) "Medium state at default properties";
  parameter Modelica.Units.SI.Length hSeg=hTan/nSeg "Height of a tank segment";
  parameter Modelica.Units.SI.Area ATan=VTan/hTan
    "Tank cross-sectional area (without insulation)";
  parameter Modelica.Units.SI.Length rTan=sqrt(ATan/Modelica.Constants.pi)
    "Tank diameter (without insulation)";
  parameter Modelica.Units.SI.ThermalConductance conFluSeg=ATan*
      Medium.thermalConductivity(sta_default)/hSeg
    "Thermal conductance between fluid volumes";
  parameter Modelica.Units.SI.ThermalConductance conTopSeg=ATan*kIns/dIns
    "Thermal conductance from center of top (or bottom) volume through tank insulation at top (or bottom)";

  BaseClasses.Buoyancy buo(
    redeclare final package Medium = Medium,
    final V=VTan,
    final nSeg=nSeg,
    final tau=tau) "Model to prevent unstable tank stratification"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor[nSeg - 1] conFlu(
    each G=conFluSeg) "Thermal conductance in fluid between the segments"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor[nSeg] conWal(
     each G=2*Modelica.Constants.pi*kIns*hSeg/Modelica.Math.log((rTan+dIns)/rTan))
    "Thermal conductance through tank wall"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor conTop(
     G=conTopSeg) "Thermal conductance through tank top"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor conBot(
     G=conTopSeg) "Thermal conductance through tank bottom"

  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFloTop
    "Heat flow at top of tank (outside insulation)"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFloBot
    "Heat flow at bottom of tank (outside insulation)"
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFloSid[nSeg]
    "Heat flow at wall of tank (outside insulation)"

  Modelica.Blocks.Routing.Multiplex3 mul(
    n1=1,
    n2=nSeg,
    n3=1) "Multiplex to collect heat flow rates"
  Modelica.Blocks.Math.Sum sum1(nin=nSeg + 2)

  Modelica.Thermal.HeatTransfer.Components.ThermalCollector theCol(m=nSeg)
    "Connector to assign multiple heat ports to one heat port"
equation
  connect(buo.heatPort, vol.heatPort)
  for i in 1:nSeg-1 loop
  // heat conduction between fluid nodes
     connect(vol[i].heatPort, conFlu[i].port_a)
    connect(vol[i+1].heatPort, conFlu[i].port_b)
  end for;
  connect(vol[1].heatPort, conTop.port_a)
  connect(vol.heatPort, conWal.port_a)
  connect(conBot.port_a, vol[nSeg].heatPort)
  connect(vol.heatPort, heaPorVol)
  connect(conWal.port_b, heaFloSid.port_a)

  connect(conTop.port_b, heaFloTop.port_a)
  connect(conBot.port_b, heaFloBot.port_a)
  connect(heaFloTop.port_b, heaPorTop)
  connect(heaFloBot.port_b, heaPorBot)
  connect(heaFloTop.Q_flow, mul.u1[1])
  connect(heaFloSid.Q_flow, mul.u2)
  connect(heaFloBot.Q_flow, mul.u3[1])
  connect(mul.y, sum1.u)
  connect(sum1.y, Ql_flow)
  connect(heaFloSid.port_b, theCol.port_a)
  connect(theCol.port_b, heaPorSid)
end PartialStratified;


within AixLib.Fluid.Storage.BaseClasses;
partial model PartialTwoPortInterface
  "Partial model transporting fluid between two ports without storing mass or energy"

  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium in the component"

  parameter Boolean allowFlowReversal = true
    "= false to simplify equations, assuming, but not enforcing, no flow reversal"

  Modelica.Fluid.Interfaces.FluidPort_a port_a(
    redeclare final package Medium = Medium,
    m_flow(min=if allowFlowReversal then -Modelica.Constants.inf else 0),
    p(start=Medium.p_default),
    h_outflow(start = Medium.h_default, nominal = Medium.h_default))
    "Fluid connector a (positive design flow direction is from port_a to port_b)"
  Modelica.Fluid.Interfaces.FluidPort_b port_b(
    redeclare final package Medium = Medium,
    m_flow(max=if allowFlowReversal then +Modelica.Constants.inf else 0),
    p(start=Medium.p_default),
    h_outflow(start = Medium.h_default, nominal = Medium.h_default))
    "Fluid connector b (positive design flow direction is from port_a to port_b)"

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate m_flow_small(min=0) = 1E-4*abs(
    m_flow_nominal) "Small mass flow rate for regularization of zero flow"
  // Diagnostics
   parameter Boolean show_T = false
    "= true, if actual temperature at port is computed"

  Modelica.Units.SI.MassFlowRate m_flow(start=_m_flow_start) = port_a.m_flow
    "Mass flow rate from port_a to port_b (m_flow > 0 is design flow direction)";

  Modelica.Units.SI.PressureDifference dp(
    start=_dp_start,
    displayUnit="Pa") = port_a.p - port_b.p
    "Pressure difference between port_a and port_b";

  Medium.ThermodynamicState sta_a=
    if allowFlowReversal then
      Medium.setState_phX(port_a.p,
                          noEvent(actualStream(port_a.h_outflow)),
                          noEvent(actualStream(port_a.Xi_outflow)))
    else
      Medium.setState_phX(port_a.p,
                          noEvent(inStream(port_a.h_outflow)),
                          noEvent(inStream(port_a.Xi_outflow)))
      if show_T "Medium properties in port_a";

  Medium.ThermodynamicState sta_b=
    if allowFlowReversal then
      Medium.setState_phX(port_b.p,
                          noEvent(actualStream(port_b.h_outflow)),
                          noEvent(actualStream(port_b.Xi_outflow)))
    else
      Medium.setState_phX(port_b.p,
                          noEvent(port_b.h_outflow),
                          noEvent(port_b.Xi_outflow))
       if show_T "Medium properties in port_b";

protected
  final parameter Modelica.Units.SI.MassFlowRate _m_flow_start=0
    "Start value for m_flow, used to avoid a warning if not set in m_flow, and to avoid m_flow.start in parameter window";
  final parameter Modelica.Units.SI.PressureDifference _dp_start(displayUnit=
        "Pa") = 0
    "Start value for dp, used to avoid a warning if not set in dp, and to avoid dp.start in parameter window";
end PartialTwoPortInterface;


Sandwich wall construction for heat storage cover.

within AixLib.Fluid.Storage.BaseClasses;
model StorageCover "Sandwich wall construction for heat storage cover"

  // Assumptions
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"

  parameter Modelica.Units.SI.Diameter D1=1 "Inner tank diameter"
  parameter Modelica.Units.SI.Thickness sWall=0.1 "Thickness of wall"
  parameter Modelica.Units.SI.Thickness sIns=0.1 "Thickness of insulation"

  parameter Modelica.Units.SI.Area AWall=D1^2/4*Modelica.Constants.pi "Area"

  parameter Modelica.Units.SI.ThermalConductivity lambdaWall=50
    "Thermal Conductivity of wall"
  parameter Modelica.Units.SI.ThermalConductivity lambdaIns=0.045
    "Thermal Conductivity of insulation"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConIn=2
    "Convective heat transfer coefficient water <-> wall"
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConOut=2
    "Convective heat transfer coefficient insulation <-> air"
  parameter Modelica.Units.SI.Temperature TStartWall=293.15
    "Starting Temperature of wall in K"
  parameter Modelica.Units.SI.Temperature TStartIns=293.15
    "Starting Temperature of insulation in K"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatportInner
    "Inner heat port"
  parameter Modelica.Units.SI.Density rhoIns=1600 "Density of insulation"
  parameter Modelica.Units.SI.SpecificHeatCapacity cIns=1000
    "Specific heat capacity of insulation"
  parameter Modelica.Units.SI.Density rhoWall=1600 "Density of Insulation"
  parameter Modelica.Units.SI.SpecificHeatCapacity cWall=1000
    "Specific heat capacity of wall"

  AixLib.Utilities.HeatTransfer.HeatConv convInside(final hCon=hConIn, final A=AWall)
                                                                          "Inside heat convection"

  Modelica.Thermal.HeatTransfer.Components.ThermalConductor condWall1(final G=(AWall)*(lambdaWall)/(sWall/2))
        "Heat conduction through first wall layer"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor loadWall(
    final C=cWall*rhoWall*AWall*sWall,
    final T(
      stateSelect=StateSelect.always,
      fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial),
      start=TStartWall),
    final der_T(
      fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial),
      start=0)) if not (energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState)
        "Heat capacity of wall"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor condWall2(final G=(AWall)*(lambdaWall)/(sWall/2))
        "Heat conduction through second wall layer"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor condIns1(G=(AWall)*(lambdaIns)/(sIns/2))
        "Heat conduction through first insulation layer"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor loadIns(
    C=cIns*rhoIns*AWall*sIns,
    final T(
      stateSelect=StateSelect.always,
      fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial),
      start=TStartIns),
    final der_T(
      fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial),
      start=0)) if not (energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyState)
        "Heat capacity of insulation"
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor condIns2(G=(AWall)*(lambdaIns)/(sIns/2))
        "Heat conduction through second insulation layer"

  AixLib.Utilities.HeatTransfer.HeatConv convOutside(final hCon=hConOut, A=AWall)
                                                                            "Outside heat convection"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatportOuter
    "Outer heat port"
equation
  connect(convOutside.port_a, heatportOuter)
  connect(condWall1.port_b,condWall2.port_a)
  connect(condWall2.port_b,condIns1.port_a)
  connect(condIns1.port_b,condIns2.port_a)
  connect(condIns2.port_b,convOutside.port_b)
  connect(loadWall.port, condWall1.port_b)
  connect(loadIns.port, condIns1.port_b)
  connect(convInside.port_a, heatportInner)
  connect(convInside.port_b,condWall1.port_a)
end StorageCover;


Type of energy balance: dynamic (3 initialization options) or steady state.

within AixLib.Fluid.Storage.BaseClasses;
model StorageMantle

  // Assumptions
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"

  parameter Modelica.Units.SI.Length height=0.15 "Height of layer"
  parameter Modelica.Units.SI.Diameter D1=1 "Inner tank diameter"
  parameter Modelica.Units.SI.Thickness sWall=0.1 "Thickness of wall"
  parameter Modelica.Units.SI.Thickness sIns=0.1 "Thickness of insulation"

  final parameter Modelica.Units.SI.Area AInside=D1*Modelica.Constants.pi*
      height "Inner area";
  final parameter Modelica.Units.SI.Area AOutside=(D1 + 2*(sWall + sIns))*
      Modelica.Constants.pi*height "Outer area";

  parameter Modelica.Units.SI.ThermalConductivity lambdaWall=50
    "Thermal Conductivity of wall";
  parameter Modelica.Units.SI.ThermalConductivity lambdaIns=0.045
    "Thermal Conductivity of insulation";
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConIn=2
    "Heat transfer coefficient water <-> wall";
  parameter Modelica.Units.SI.CoefficientOfHeatTransfer hConOut=2
    "Heat transfer coefficient insulation <-> air";
  parameter Modelica.Units.SI.Temperature TStartWall=293.15
    "Starting Temperature of wall in K";
  parameter Modelica.Units.SI.Temperature TStartIns=293.15
    "Starting Temperature of insulation in K";
  parameter Modelica.Units.SI.Density rhoIns=1600 "Density of insulation";
  parameter Modelica.Units.SI.SpecificHeatCapacity cIns=1000
    "Specific heat capacity of insulation";
  parameter Modelica.Units.SI.Density rhoWall=1600 "Density of Insulation";
  parameter Modelica.Units.SI.SpecificHeatCapacity cWall=1000
    "Specific heat capacity of wall";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatportOuter
    "Outer heat port"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a heatportInner
    "Inner heat port"

  AixLib.Utilities.HeatTransfer.CylindricHeatTransfer Insulation(
    final energyDynamics=energyDynamics,
    rho=rhoIns,
    c=cIns,
    lambda=lambdaIns,
    T0=TStartIns,
    length=height,
    d_out=D1 + 2*sWall + 2*sIns,
    d_in=D1 + 2*sWall)
    "Heat transfer through insulation"

  AixLib.Utilities.HeatTransfer.CylindricHeatTransfer Wall(
    final energyDynamics=energyDynamics,
    rho=rhoWall,
    c=cWall,
    lambda=lambdaWall,
    T0=TStartWall,
    length=height,
    d_out=D1 + 2*sWall,
    d_in=D1)
    "Heat transfer through wall"

  AixLib.Utilities.HeatTransfer.HeatConv convInside(hCon=hConIn, A=AInside) "Inner heat convection"
  AixLib.Utilities.HeatTransfer.HeatConv convOutside(A=AOutside, hCon=hConOut) "Outer heat convection"
equation
  connect(convInside.port_a, heatportInner)
  connect(convOutside.port_a, heatportOuter)
  connect(convInside.port_b,Wall.port_a)
  connect(Wall.port_b,Insulation.port_a)
  connect(Insulation.port_b,convOutside.port_b)
end StorageMantle;


within AixLib.Fluid.Storage.BaseClasses;
model ThirdOrderStratifier
  "Model to reduce the numerical dissipation in a tank"
  extends Modelica.Blocks.Icons.Block;

  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
    "Medium model"

  parameter Modelica.Units.SI.MassFlowRate m_flow_small(min=0)
    "Small mass flow rate for regularization of zero flow";
  parameter Integer nSeg(min=4) "Number of volume segments";

  parameter Real alpha(
    min=0,
    max=1) = 0.5 "Under-relaxation coefficient (1: QUICK; 0: 1st order upwind)";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a[nSeg] heatPort
    "Heat input into the volumes"

  Modelica.Blocks.Interfaces.RealInput m_flow
    "Mass flow rate from port a to port b"

  Modelica.Blocks.Interfaces.RealInput[nSeg + 1] H_flow
    "Enthalpy flow between the volumes"

  Modelica.Fluid.Interfaces.FluidPort_a[nSeg + 2] fluidPort(redeclare each
      package Medium = Medium)
    "Fluid port, needed to get pressure, temperature and species concentration"

protected
  Modelica.Units.SI.SpecificEnthalpy[nSeg + 1] hOut
    "Extended vector with new outlet enthalpies to reduce numerical dissipation (at the boundary between two volumes)";
  Modelica.Units.SI.SpecificEnthalpy[nSeg + 2] h
    "Extended vector with port enthalpies, needed to simplify loop";
  Modelica.Units.SI.HeatFlowRate Q_flow[nSeg]
    "Heat exchange computed using upwind third order discretization scheme";
  //    Modelica.Units.SI.HeatFlowRate Q_flow_upWind
  //     "Heat exchange computed using upwind third order discretization scheme"; //Used to test the energy conservation
  Real sig
    "Sign used to implement the third order upwind scheme without triggering a state event";
  Real comSig
    "Sign used to implement the third order upwind scheme without triggering a state event";

equation
  assert(nSeg >= 4,
  "Number of segments of the enhanced stratified tank should be no less than 4 (nSeg>=4).");

  // assign zero flow conditions at port
  fluidPort[:].m_flow = zeros(nSeg + 2);
  fluidPort[:].h_outflow = zeros(nSeg + 2);
  fluidPort[:].Xi_outflow = zeros(nSeg + 2, Medium.nXi);
  fluidPort[:].C_outflow = zeros(nSeg + 2, Medium.nC);

  // assign extended enthalpy vectors
  for i in 1:nSeg + 2 loop
    h[i] = inStream(fluidPort[i].h_outflow);
  end for;

  // Value that transitions between 0 and 1 as the flow reverses.
  sig = Modelica.Fluid.Utilities.regStep(
    m_flow,
    1,
    0,
    m_flow_small);
             // at surface between port_a and vol1

  comSig = 1 - sig;

  // at surface between port_a and vol1
  hOut[1] = sig*h[1] + comSig*h[2];
  // at surface between vol[nSeg] and port_b
  hOut[nSeg + 1] = sig*h[nSeg + 1] + comSig*h[nSeg + 2];

  // Pros: These two equations can further reduce the temperature overshoot by using the upwind
  // Cons: The minimum of nSeg hase to be 4 instead of 2.
  hOut[2] = sig*h[2] + comSig*h[3];
  // at surface between vol1 and vol2
  hOut[nSeg] = sig*h[nSeg] + comSig*h[nSeg + 1];
  // at surface between vol[nSeg-1] and vol[nSeg]

  for i in 3:nSeg - 1 loop
    // at surface between vol[i-1] and vol[i]
    // QUICK method
    hOut[i] = 0.5*(h[i] + h[i + 1]) - comSig*0.125*(h[i + 2] + h[i] - 2*h[i + 1])
       - sig*0.125*(h[i - 1] + h[i + 1] - 2*h[i]);
    //     hOut[i] = 0.5*(h[i]+h[i+1]); // Central difference method
  end for;

  for i in 1:nSeg loop
    // difference between QUICK and UPWIND; index of H_flow is same as hOut
    Q_flow[i] = m_flow*(hOut[i + 1] - hOut[i]) - (H_flow[i + 1] - H_flow[i]);
  end for;

  //   Q_flow_upWind = sum(Q_flow[i] for i in 1:nSeg); //Used to test the energy conservation

  for i in 1:nSeg loop
    // Add the difference back to the volume as heat flow. An under-relaxation is needed to reduce
    // oscillations caused by high order method
    heatPort[i].Q_flow = Q_flow[i]*alpha;
  end for;
end ThirdOrderStratifier;


within AixLib.Fluid.Storage.BaseClasses.Examples;
model IndirectTankHeatExchanger
  "Example showing the use of IndirectTankHeatExchanger"
  extends Modelica.Icons.Example;

  package Medium = AixLib.Media.Water "Buildings library model for water";

  AixLib.Fluid.Storage.BaseClasses.IndirectTankHeatExchanger indTanHex(
    nSeg=3,
    CHex=50,
    Q_flow_nominal=3000,
    m_flow_nominal=3000/20/4200,
    volHexFlu=0.0004,
    dExtHex=0.01905,
    redeclare package MediumTan = Medium,
    redeclare package MediumHex = Medium,
    dp_nominal=10000,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TTan_nominal=293.15,
    THex_nominal=323.15) "Heat exchanger"

  AixLib.Fluid.Sources.Boundary_pT bou1(
    nPorts=1,
    redeclare package Medium = Medium)
  AixLib.Fluid.Sources.MassFlowSource_T bou(
    m_flow=0.1,
    nPorts=1,
    redeclare package Medium = Medium,
    T=323.15)

  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature watTem[3](each T=293.15)
equation
  connect(bou1.ports[1], indTanHex.port_a)
  connect(bou.ports[1], indTanHex.port_b)

  connect(watTem.port, indTanHex.port)
end IndirectTankHeatExchanger;


Examples for BaseClasses models

within AixLib.Fluid.Storage.BaseClasses;
package Examples "Examples for BaseClasses models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;


Test model for expansion vessel

within AixLib.Fluid.Storage.Examples;
model ExpansionVessel "Test model for expansion vessel"
  extends Modelica.Icons.Example;

// package Medium = Modelica.Media.Water.WaterIF97OnePhase_ph "Medium model";
 package Medium = AixLib.Media.Water "Medium model";

  AixLib.Fluid.Storage.ExpansionVessel expVes(
    redeclare package Medium = Medium, V_start=1) "Expansion vessel"
  AixLib.Fluid.MixingVolumes.MixingVolume vol(
    redeclare package Medium = Medium,
    V=1,
    nPorts=1,
    m_flow_nominal=0.001,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
    "Volume of water"
  Modelica.Blocks.Sources.Pulse pulse(
    amplitude=20,
    period=3600,
    offset=293.15)
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature preTem
  Modelica.Thermal.HeatTransfer.Components.ThermalConductor theCon(G=10000)
equation
  connect(pulse.y, preTem.T)
  connect(preTem.port, theCon.port_a)
  connect(theCon.port_b, vol.heatPort)
  connect(vol.ports[1], expVes.port_a)
end ExpansionVessel;


within AixLib.Fluid.Storage;
package Examples "Collection of models that illustrate model use and test models"
  extends Modelica.Icons.ExamplesPackage;
end Examples;