within Buildings.Fluid.Geothermal.Borefields.Examples;
model Borefields
  "Example model with several borefield configurations operating simultaneously"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Water;

  parameter Modelica.Units.SI.Time tLoaAgg=300
    "Time resolution of load aggregation";

  parameter Modelica.Units.SI.Temperature TGro=283.15 "Ground temperature";
  Buildings.Fluid.Geothermal.Borefields.TwoUTubes borFie2UTubPar(
    redeclare package Medium = Medium,
    borFieDat=borFie2UTubParDat,
    tLoaAgg=tLoaAgg,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TExt0_start=TGro)
    "Borefield with a 2-U-tube connected in parallel borehole configuration"
  Buildings.Fluid.Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFie2UTubParDat.conDat.mBorFie_flow_nominal,
    T=303.15) "Source"
  Buildings.Fluid.Sensors.TemperatureTwoPort T2UTubParIn(
    redeclare package Medium = Medium,
    m_flow_nominal=borFie2UTubParDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Inlet temperature of the borefield with 2-UTube in serie configuration"
  Buildings.Fluid.Sources.Boundary_pT sin1(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"
  Buildings.Fluid.Sensors.TemperatureTwoPort T2UTubParOut(
    redeclare package Medium = Medium,
    m_flow_nominal=borFie2UTubParDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Outlet temperature of the borefield with 2-UTube in parallel configuration"
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Example borFieUTubDat(
    filDat=Buildings.Fluid.Geothermal.Borefields.Data.Filling.Bentonite(
    steadyState=true),
    conDat=Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Example(
    borCon=Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.SingleUTube))

  Buildings.Fluid.Geothermal.Borefields.TwoUTubes borFie2UTubSer(
    redeclare package Medium = Medium,
    borFieDat=borFie2UTubSerDat,
    tLoaAgg=tLoaAgg,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TExt0_start=TGro)
    "Borefield with a 2-U-tube connected in serie borehole configuration"
  Buildings.Fluid.Sources.MassFlowSource_T sou2(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFie2UTubSerDat.conDat.mBorFie_flow_nominal,
    T=303.15) "Source"
  Buildings.Fluid.Sensors.TemperatureTwoPort T2UTubSerIn(
    redeclare package Medium = Medium,
    m_flow_nominal=borFie2UTubSerDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Inlet temperature of the borefield with 2-UTube in serie configuration"
  Buildings.Fluid.Sources.Boundary_pT sin2(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"
  Buildings.Fluid.Sensors.TemperatureTwoPort T2UTubSerOut(
    redeclare package Medium = Medium,
    m_flow_nominal=borFie2UTubSerDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Outlet temperature of the borefield with 2-UTube in serie configuration"
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Example borFie2UTubParDat(
    filDat=Buildings.Fluid.Geothermal.Borefields.Data.Filling.Bentonite(
    steadyState=true),
    conDat=Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Example(
    borCon=Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel))
    "Data from the borefield with 2-UTube in parallel borehole configuration"
  Buildings.Fluid.Geothermal.Borefields.OneUTube borFieUTub(
    redeclare package Medium = Medium,
    borFieDat=borFieUTubDat,
    tLoaAgg=tLoaAgg,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TExt0_start=TGro)
    "Borefield with a U-tube borehole configuration"
  Buildings.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFieUTubDat.conDat.mBorFie_flow_nominal,
    T=303.15) "Source"
  Buildings.Fluid.Sensors.TemperatureTwoPort TUTubIn(
    redeclare package Medium = Medium,
    m_flow_nominal=borFieUTubDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Inlet temperature of the borefield with UTube configuration"
  Buildings.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"
  Buildings.Fluid.Sensors.TemperatureTwoPort TUTubOut(
    redeclare package Medium = Medium,
    m_flow_nominal=borFieUTubDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Inlet temperature of the borefield with UTube configuration"
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Example borFie2UTubSerDat(
      filDat=Buildings.Fluid.Geothermal.Borefields.Data.Filling.Bentonite(
        steadyState=true),
    conDat=Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Example(
    borCon=Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeSeries))
    "Data from the borefield with 2-UTube in serie borehole configuration"

equation
  connect(sou1.ports[1], T2UTubParIn.port_a)
  connect(T2UTubParIn.port_b, borFie2UTubPar.port_a)
  connect(T2UTubParOut.port_a, borFie2UTubPar.port_b)
  connect(T2UTubParOut.port_b, sin1.ports[1])
  connect(sou2.ports[1], T2UTubSerIn.port_a)
  connect(T2UTubSerIn.port_b, borFie2UTubSer.port_a)
  connect(T2UTubSerOut.port_a, borFie2UTubSer.port_b)
  connect(T2UTubSerOut.port_b,sin2. ports[1])
  connect(sou.ports[1], TUTubIn.port_a)
  connect(TUTubIn.port_b, borFieUTub.port_a)
  connect(borFieUTub.port_b, TUTubOut.port_a)
  connect(TUTubOut.port_b, sin.ports[1])
end Borefields;


within Buildings.Fluid.Geothermal.Borefields;
package Examples "Example models for Buildings.Fluid.Geothermal.Borefields"
extends Modelica.Icons.ExamplesPackage;
end Examples;


within Buildings.Fluid.Geothermal.Borefields.Examples;
model RectangularBorefield "Example model of a rectangular borefield"
  extends Modelica.Icons.Example;

  package Medium = Buildings.Media.Water "Medium model";

  parameter Modelica.Units.SI.Time tLoaAgg=300
    "Time resolution of load aggregation";

  parameter Modelica.Units.SI.Temperature TGro=283.15 "Ground temperature";
  parameter Modelica.Units.SI.Velocity v_nominal=1 "Nominal velocity";
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=nBorHol*v_nominal*
      rTub^2*3.14*1000 "Nominal mass flow rate";
  parameter Modelica.Units.SI.Pressure dpBorFie_nominal=(hBor + (xBorFie +
      yBorFie)/2)*2 "Pressure losses for the entire borefield";
  parameter Modelica.Units.SI.Pressure dpHex_nominal=10000
    "Pressure drop heat exchanger";
  parameter Modelica.Units.SI.Pressure dp_nominal=dpBorFie_nominal +
      dpHex_nominal "Total pressure drop";

  parameter Modelica.Units.SI.Height hBor=100 "Total height of the borehole";
  parameter Modelica.Units.SI.Radius rTub=0.02 "Outer radius of the tubes";
  parameter Modelica.Units.SI.Length xBorFie=10 "Borefield length";
  parameter Modelica.Units.SI.Length yBorFie=30 "Borefield width";
  parameter Modelica.Units.SI.Length dBorHol=5 "Distance between two boreholes";

  final parameter Integer nXBorHol = integer((xBorFie+dBorHol)/dBorHol) "Number of boreholes in x-direction";
  final parameter Integer nYBorHol = integer((yBorFie+dBorHol)/dBorHol) "Number of boreholes in y-direction";
  final parameter Integer nBorHol = nXBorHol*nYBorHol "Number of boreholes";

  final parameter Buildings.Fluid.Geothermal.Borefields.Data.Filling.Bentonite filDat
  final parameter Buildings.Fluid.Geothermal.Borefields.Data.Soil.SandStone soiDat
    "Soil data"
  final parameter Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Template conDat(
    final borCon=Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.SingleUTube,
    final use_Rb=false,
    final mBor_flow_nominal=m_flow_nominal/(nXBorHol*nYBorHol),
    final mBorFie_flow_nominal=m_flow_nominal,
    final hBor=hBor,
    final dBor=1,
    final rBor=0.2,
    final rTub=rTub,
    final kTub=0.5,
    final eTub=0.002,
    final xC=0.05,
    final dp_nominal=dpBorFie_nominal,
    final cooBor = {{dBorHol*mod((i-1),nXBorHol), dBorHol*floor((i-1)/nXBorHol)} for i in 1:nBorHol})
    "Borefield configuration"

  final parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Template
    borFieDat(
      final filDat=filDat,
      final soiDat=soiDat,
      final conDat=conDat) "Borefield parameters"

  Buildings.Fluid.Geothermal.Borefields.OneUTube borFie(
    redeclare package Medium = Medium,
    borFieDat=borFieDat,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TExt0_start=280.65,
    allowFlowReversal=false)
      "Geothermal borefield"

  Buildings.Fluid.Sources.Boundary_pT bou(
    redeclare package Medium = Medium, nPorts=1)
    "Pressure boundary condition"

  Buildings.Fluid.Movers.FlowControlled_m_flow pum(
    redeclare package Medium = Medium,
    addPowerToMedium=false,
    nominalValuesDefineDefaultPressureCurve=true,
    use_riseTime=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    allowFlowReversal=false)
  Buildings.Fluid.HeatExchangers.Heater_T hea(
    redeclare package Medium = Medium,
    show_T=true,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    m_flow(start=borFieDat.conDat.mBorFie_flow_nominal),
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial,
    dp_nominal=dpHex_nominal,
    allowFlowReversal=false)  "Heater"
  Modelica.Blocks.Sources.Constant TSou(k=293.15)
    "Temperature of water that goes into the borefield"
equation

  connect(hea.port_b, pum.port_a)
  connect(TSou.y, hea.TSet)
  connect(pum.port_b, borFie.port_a)
  connect(borFie.port_b, hea.port_a)
  connect(pum.port_a, bou.ports[1])
end RectangularBorefield;


within Buildings.Fluid.Geothermal.Borefields.Validation;
model ConstantHeatInjection_100Boreholes
  "Long-term temperature response of a borefield of 100 boreholes"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;

  parameter Modelica.Units.SI.Temperature T_start=273.15
    "Initial temperature of the soil";

  Buildings.Fluid.Geothermal.Borefields.OneUTube borHol(
    redeclare package Medium = Medium, borFieDat=
    borFieDat,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TExt0_start=T_start,
    dT_dz=0,
    tLoaAgg=3600000)
    "Borehole"
  Buildings.Fluid.Movers.FlowControlled_m_flow pum(
    redeclare package Medium = Medium,
    T_start=T_start,
    addPowerToMedium=false,
    use_riseTime=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    nominalValuesDefineDefaultPressureCurve=true,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    dp_nominal=60E3) "Circulation pump"
  Buildings.Fluid.Sensors.TemperatureTwoPort TBorFieIn(
    redeclare package Medium = Medium,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Inlet temperature of the borefield"
  Buildings.Fluid.Sensors.TemperatureTwoPort TBorFieOut(
    redeclare package Medium = Medium,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Outlet temperature of the borefield"
  parameter Buildings.Fluid.Geothermal.Borefields.Validation.BaseClasses.ConstantHeatInjection_100Boreholes_Borefield borFieDat
    "Borefield data"
  Buildings.Fluid.Sources.Boundary_ph sin(
    redeclare package Medium = Medium,
    nPorts=1) "Sink"
  Buildings.Fluid.HeatExchangers.HeaterCooler_u hea(
    redeclare package Medium = Medium,
    dp_nominal=10000,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    m_flow(start=borFieDat.conDat.mBorFie_flow_nominal),
    p_start=100000,
    Q_flow_nominal=2*Modelica.Constants.pi*borFieDat.soiDat.kSoi*borFieDat.conDat.hBor
        *borFieDat.conDat.nBor)
      "Heater"
  Modelica.Blocks.Sources.Constant heaRat(k=1)
    "Constant heat injection rate into the borefield"
equation
  connect(TBorFieIn.port_b, borHol.port_a)
  connect(borHol.port_b, TBorFieOut.port_a)
  connect(pum.port_b, TBorFieIn.port_a)
  connect(sin.ports[1], TBorFieOut.port_b)
  connect(hea.port_b, pum.port_a)
  connect(hea.port_a, TBorFieOut.port_b)
  connect(hea.u, heaRat.y)
end ConstantHeatInjection_100Boreholes;


within Buildings.Fluid.Geothermal.Borefields;
package Validation "Validation models for Buildings.Fluid.Geothermal.Borefields"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


within Buildings.Fluid.Geothermal.Borefields.Validation;
model Sandbox "Validation of BorefieldOneUTube based on the experiment of Beier et al. (2011)"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;

  parameter Modelica.Units.SI.Temperature T_start=273.15 + 22.09
    "Initial temperature of the sandbox";

  // mSenFac is set to its numerical value because it is a constant in the borehole model.
  // mSenFac = 1 +
  //  (1.8e6*Modelica.Constants.pi*(borFieDat.conDat.rTub^2
  //        -(borFieDat.conDat.rTub-borFieDat.conDat.eTub)^2)
  //        +2.4e6*2*Modelica.Constants.pi*borFieDat.conDat.rBor*0.002/2)
  //        /(4.2e6*Modelica.Constants.pi*(borFieDat.conDat.rTub-borFieDat.conDat.eTub)^2)

  constant Real mSenFac = 1.59186
  "Scaling factor for the borehole capacitances, modified to account for the thermal mass of the pipes and the borehole casing";

  Buildings.Fluid.Geothermal.Borefields.OneUTube borHol(
    redeclare package Medium = Medium, borFieDat=
    borFieDat,
    tLoaAgg=60,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    mSenFac=mSenFac,
    TExt0_start=T_start,
    dT_dz=0) "Borehole"
  Buildings.Fluid.Movers.FlowControlled_m_flow pum(
    redeclare package Medium = Medium,
    T_start=T_start,
    addPowerToMedium=false,
    use_riseTime=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    nominalValuesDefineDefaultPressureCurve=true,
    inputType=Buildings.Fluid.Types.InputType.Constant,
    dp_nominal=60E3) "Circulation pump"
  Buildings.Fluid.Sensors.TemperatureTwoPort TBorFieIn(
    redeclare package Medium = Medium,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Inlet temperature of the borefield"
  Buildings.Fluid.Sensors.TemperatureTwoPort TBorFieOut(
    redeclare package Medium = Medium,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    tau=0)
    "Outlet temperature of the borefield"
  parameter Buildings.Fluid.Geothermal.Borefields.Validation.BaseClasses.SandBox_Borefield borFieDat "Borefield data"
  Buildings.Fluid.Sources.Boundary_ph sin(
    redeclare package Medium = Medium,
    nPorts=1) "Sink"
  Modelica.Blocks.Sources.CombiTimeTable sandBoxMea(
    tableOnFile=true,
    tableName="data",
    offset={0,0,0},
    columns={2,3,4},
    fileName=Modelica.Utilities.Files.loadResource(
      "modelica://Buildings/Resources/Data/Fluid/Geothermal/Borefields/HeatTransfer/Validation/Beier_Smith_Spitler_2011_SandBox.txt"))
  Buildings.Fluid.HeatExchangers.HeaterCooler_u hea(
    redeclare package Medium = Medium,
    dp_nominal=10000,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    T_start=T_start,
    Q_flow_nominal=1056,
    m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal,
    m_flow(start=borFieDat.conDat.mBorFie_flow_nominal),
    p_start=100000) "Heater"
equation
  connect(TBorFieIn.port_b, borHol.port_a)
  connect(borHol.port_b, TBorFieOut.port_a)
  connect(pum.port_b, TBorFieIn.port_a)
  connect(sin.ports[1], TBorFieOut.port_b)
  connect(hea.port_b, pum.port_a)
  connect(hea.port_a, TBorFieOut.port_b)
  connect(sandBoxMea.y[3], hea.u)
end Sandbox;


within Buildings.Fluid.Geothermal.Borefields.Validation;
package BaseClasses "Package with base classes for Buildings.Fluid.Geothermal.Borefields.Validation"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


Package with borehole heat exchangers

within Buildings.Fluid.Geothermal;
package Boreholes "Package with borehole heat exchangers"
  extends Modelica.Icons.VariantsPackage;
end Boreholes;


Single U-tube borehole heat exchanger

within Buildings.Fluid.Geothermal.Boreholes;
model UTube "Single U-tube borehole heat exchanger"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    show_T=true);
  extends Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters(final
      computeFlowResistance=false, final linearizeFlowResistance=false);
  extends Buildings.Fluid.Interfaces.LumpedVolumeDeclarations(
    final massDynamics=energyDynamics);

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

  replaceable parameter Buildings.HeatTransfer.Data.Soil.Generic matSoi
    "Thermal properties of soil"
  replaceable parameter Buildings.HeatTransfer.Data.BoreholeFillings.Generic matFil
    "Thermal properties of the filling material"

  parameter Modelica.Units.SI.Radius rTub=0.02 "Radius of the tubes"
  parameter Modelica.Units.SI.ThermalConductivity kTub=0.5
    "Thermal conductivity of the tube"
  parameter Modelica.Units.SI.Length eTub=0.002 "Thickness of a tube"

  parameter Modelica.Units.SI.Height hBor "Total height of the borehole"
  parameter Integer nVer=10
    "Number of segments used for discretization in the vertical direction"
  parameter Modelica.Units.SI.Radius rBor=0.1 "Radius of the borehole";

  parameter Modelica.Units.SI.Radius rExt=3
    "Radius of the soil used for the external boundary condition"
  parameter Integer nHor(min=1) = 10
    "Number of state variables in each horizontal layer of the soil"

  parameter Modelica.Units.SI.Temperature TExt0_start=283.15
    "Initial far field temperature"
  parameter Modelica.Units.SI.Temperature TExt_start[nVer]={if z[i] >= z0 then
      TExt0_start + (z[i] - z0)*dT_dz else TExt0_start for i in 1:nVer}
    "Temperature of the undisturbed ground"

  parameter Modelica.Units.SI.Temperature TFil0_start=TExt0_start
    "Initial temperature of the filling material for h = 0...z0"
  parameter Modelica.Units.SI.Temperature TFil_start[nVer]=TExt_start
    "Temperature of the undisturbed ground"

  parameter Modelica.Units.SI.Height z0=10
    "Depth below which the temperature gradient starts"
  parameter Real dT_dz(unit="K/m") = 0.01
    "Vertical temperature gradient of the undisturbed soil for h below z0"

  parameter Modelica.Units.SI.Time samplePeriod
    "Sample period for the external boundary condition"
  parameter Modelica.Units.SI.Length xC=0.05
    "Shank spacing, defined as the distance between the center of a pipe and the center of the borehole"
  parameter Real B0=17.44 "Shape coefficient for grout resistance"
  parameter Real B1=-0.605 "Shape coefficient for grout resistance"

  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.BoreholeSegment borHol[nVer](
    redeclare each final package Medium = Medium,
    each final matSoi=matSoi,
    each final matFil=matFil,
    each final hSeg=hBor/nVer,
    each final samplePeriod=samplePeriod,
    each final rTub=rTub,
    each final rBor=rBor,
    each final rExt=rExt,
    each final xC=xC,
    each final eTub=eTub,
    each final kTub=kTub,
    each final nSta=nHor,
    each final m_flow_nominal=m_flow_nominal,
    each final m_flow_small=m_flow_small,
    final dp_nominal={if i == 1 then dp_nominal else 0 for i in 1:nVer},
    TExt_start=TExt_start,
    TFil_start=TExt_start,
    each final homotopyInitialization=homotopyInitialization,
    each final show_T=show_T,
    each final computeFlowResistance=computeFlowResistance,
    each final from_dp=from_dp,
    each final linearizeFlowResistance=linearizeFlowResistance,
    each final deltaM=deltaM,
    each final energyDynamics=energyDynamics,
    each final massDynamics=massDynamics,
    each final 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 allowFlowReversal=allowFlowReversal) "Discretized borehole segments"

  Modelica.Units.SI.Temperature Tdown[nVer] "Medium temperature in pipe 1";
  Modelica.Units.SI.Temperature Tup[nVer] "Medium temperature in pipe 2";
protected
  parameter Modelica.Units.SI.Height z[nVer]={hBor/nVer*(i - 0.5) for i in 1:
      nVer} "Distance from the surface to the considered segment";

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
  Tdown[:] = borHol[:].pipFil.vol1.heatPort.T;
  Tup[:] = borHol[:].pipFil.vol2.heatPort.T;
  connect(port_a, borHol[1].port_a1)
  connect(port_b, borHol[1].port_b2)
  connect(borHol[nVer].port_b1, borHol[nVer].port_a2)
  for i in 1:nVer - 1 loop
    connect(borHol[i].port_b1, borHol[i + 1].port_a1)
    connect(borHol[i].port_a2, borHol[i + 1].port_b2)
  end for;
end UTube;


Vertical segment of a borehole

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses;
model BoreholeSegment "Vertical segment of a borehole"
  extends Buildings.Fluid.Interfaces.PartialFourPortInterface(
     redeclare final package Medium1 = Medium,
     redeclare final package Medium2 = Medium,
     final m1_flow_nominal = m_flow_nominal,
     final m2_flow_nominal = m_flow_nominal,
     final m1_flow_small =   m_flow_small,
     final m2_flow_small =   m_flow_small,
     final allowFlowReversal1=allowFlowReversal,
     final allowFlowReversal2=allowFlowReversal);
  extends Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters;
  extends Buildings.Fluid.Interfaces.LumpedVolumeDeclarations(T_start=TFil_start);
  replaceable package Medium = Modelica.Media.Interfaces.PartialMedium
    "Medium in the component"

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

  replaceable parameter Buildings.HeatTransfer.Data.Soil.Generic matSoi
    "Thermal properties of soil"
  replaceable parameter Buildings.HeatTransfer.Data.BoreholeFillings.Generic matFil
    "Thermal properties of the filling material"

  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"

  parameter Modelica.Units.SI.Radius rTub=0.02 "Radius of the tubes"
  parameter Modelica.Units.SI.ThermalConductivity kTub=0.5
    "Thermal conductivity of the tubes"
  parameter Modelica.Units.SI.Length eTub=0.002 "Thickness of the tubes"
  parameter Modelica.Units.SI.Temperature TFil_start=283.15
    "Initial temperature of the filling material"
  parameter Modelica.Units.SI.Radius rExt=3
    "Radius of the soil used for the external boundary condition"
  parameter Modelica.Units.SI.Temperature TExt_start=283.15
    "Initial far field temperature"
  parameter Integer nSta(min=1) = 10 "Number of state variables in the soil"
  parameter Modelica.Units.SI.Time samplePeriod=604800
    "Sample period for the external boundary condition"
  parameter Modelica.Units.SI.Radius rBor=0.1 "Radius of the borehole";
  parameter Modelica.Units.SI.Height hSeg "Height of the element";
  parameter Modelica.Units.SI.Length xC=0.05
    "Shank spacing, defined as the distance between the center of a pipe and the center of the borehole";

 parameter Boolean allowFlowReversal = true
    "= true to allow flow reversal, false restricts to design direction (port_a -> port_b)"

 Buildings.Fluid.Geothermal.Boreholes.BaseClasses.HexInternalElement pipFil(
    redeclare final package Medium = Medium,
    final matFil=matFil,
    final matSoi=matSoi,
    final hSeg=hSeg,
    final rTub=rTub,
    final eTub=eTub,
    final kTub=kTub,
    final kSoi=matSoi.k,
    final xC=xC,
    final rBor=rBor,
    final TFil_start=TFil_start,
    final m1_flow_nominal=m_flow_nominal,
    final m2_flow_nominal=m_flow_nominal,
    final dp1_nominal=dp_nominal,
    final dp2_nominal=0,
    final from_dp1=from_dp,
    final from_dp2=from_dp,
    final linearizeFlowResistance1=linearizeFlowResistance,
    final linearizeFlowResistance2=linearizeFlowResistance,
    final deltaM1=deltaM,
    final deltaM2=deltaM,
    final m1_flow_small=m_flow_small,
    final m2_flow_small=m_flow_small,
    final allowFlowReversal1=allowFlowReversal,
    final allowFlowReversal2=allowFlowReversal,
    final homotopyInitialization=homotopyInitialization,
    final energyDynamics=energyDynamics,
    final p1_start=p_start,
    T1_start=T_start,
    X1_start=X_start,
    C1_start=C_start,
    C1_nominal=C_nominal,
    final p2_start=p_start,
    T2_start=T_start,
    X2_start=X_start,
    C2_start=C_start,
    C2_nominal=C_nominal)
    "Internal part of the borehole including the pipes and the filling material"
  Buildings.HeatTransfer.Conduction.SingleLayerCylinder soi(
    final material=matSoi,
    final h=hSeg,
    final nSta=nSta,
    final r_a=rBor,
    final r_b=rExt,
    final steadyStateInitial=false,
    final TInt_start=TFil_start,
    final TExt_start=TExt_start) "Heat conduction in the soil"
  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.SingleUTubeBoundaryCondition
    TBouCon(
    final matSoi=matSoi,
    final rExt=rExt,
    final hSeg=hSeg,
    final TExt_start=TExt_start,
    final samplePeriod=samplePeriod)
    "Thermal boundary condition for the far-field"
protected
  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor heaFlo

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
  connect(pipFil.port_b1, port_b1)
  connect(pipFil.port_a2, port_a2)
  connect(pipFil.port_b2, port_b2)
  connect(pipFil.port, heaFlo.port_a)
  connect(heaFlo.port_b, soi.port_a)
  connect(soi.port_b, TBouCon.port)
  connect(port_a1, pipFil.port_a1)
  connect(heaFlo.Q_flow, TBouCon.Q_flow)
end BoreholeSegment;


Thermal resistance between the fluid and the tube. This model computes the convection resistance in the pipes of a borehole segment
with heigth hSeg.

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses;
function convectionResistance
  "Thermal resistance between the fluid and the tube"

  // Geometry of the borehole
  input Modelica.Units.SI.Height hSeg "Height of the element";
  input Modelica.Units.SI.Radius rTub "Tube radius";

  // Thermal properties
  input Modelica.Units.SI.ThermalConductivity kMed
    "Thermal conductivity of the fluid";
  input Modelica.Units.SI.DynamicViscosity mueMed
    "Dynamic viscosity of the fluid";
  input Modelica.Units.SI.SpecificHeatCapacity cpMed
    "Specific heat capacity of the fluid";
  // Mass flow rates
  input Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";
  input Modelica.Units.SI.MassFlowRate m_flow_nominal "Nominal mass flow rate";

  // Outputs
  output Modelica.Units.SI.ThermalResistance R
    "Thermal resistance between the fluid and the tube";

protected
  Modelica.Units.SI.CoefficientOfHeatTransfer h
    "Convective heat transfer coefficient of the fluid";

  Real k(unit="s/kg")
    "Coefficient used in the computation of the convective heat transfer coefficient";

algorithm
  // ********** Convection resistance **********
  // Dittus-Boelter: h = 0.023*k_f*Re*Pr/(2*rTub)
  // Re = rho*v*DTub / mue_f = m_flow/(pi r^2) * DTub/mue_f = 2*m_flow / ( mue*pi*rTub)
  k := 2/(mueMed*Modelica.Constants.pi*rTub);

  // Convection
  h := 0.023*kMed*(cpMed*mueMed/kMed)^(0.35)/(2*rTub)*
         Buildings.Utilities.Math.Functions.regNonZeroPower(
           x=m_flow*k,
           n=0.8,
           delta=0.01*m_flow_nominal*k);
  R := 1/(2*Modelica.Constants.pi*rTub*hSeg*h);
end convectionResistance;


Internal part of a borehole

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses;
model HexInternalElement "Internal part of a borehole"
  extends Buildings.Fluid.Interfaces.FourPortHeatMassExchanger(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    T1_start=TFil_start,
    T2_start=TFil_start,
    final tau1=Modelica.Constants.pi*rTub^2*hSeg*rho1_nominal/m1_flow_nominal,
    final tau2=Modelica.Constants.pi*rTub^2*hSeg*rho2_nominal/m2_flow_nominal,
    vol1(final energyDynamics=energyDynamics,
         final massDynamics=energyDynamics,
         final prescribedHeatFlowRate=false,
         final V=m2_flow_nominal*tau2/rho2_nominal,
         final m_flow_small=m1_flow_small),
    final vol2(final energyDynamics=energyDynamics,
         final massDynamics=energyDynamics,
         final prescribedHeatFlowRate=false,
         final V=m1_flow_nominal*tau1/rho1_nominal,
         final m_flow_small=m2_flow_small));

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

  replaceable parameter Buildings.HeatTransfer.Data.BoreholeFillings.Generic
    matFil "Thermal properties of the filling material"
  replaceable parameter Buildings.HeatTransfer.Data.Soil.Generic matSoi
    "Thermal properties of soil"

  parameter Modelica.Units.SI.Radius rTub=0.02 "Radius of the tubes"
  parameter Modelica.Units.SI.ThermalConductivity kTub=0.5
    "Thermal conductivity of the tubes"
  parameter Modelica.Units.SI.Length eTub=0.002 "Thickness of the tubes"
  parameter Modelica.Units.SI.ThermalConductivity kSoi
    "Thermal conductivity of the soil used for the calculation of the internal interference resistance";

  parameter Modelica.Units.SI.Temperature TFil_start=283.15
    "Initial temperature of the filling material"

  parameter Modelica.Units.SI.Height hSeg "Height of the element";
  parameter Modelica.Units.SI.Radius rBor "Radius of the borehole";

  parameter Modelica.Units.SI.Length xC=0.05
    "Shank spacing, defined as half the center-to-center distance between the two pipes";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port
    "Heat port that connects to filling material"

  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil1(
    final C=Co_fil/2,
    T(final start=TFil_start,
      fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
    der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)))
    "Heat capacity of the filling material"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil2(
    final C=Co_fil/2,
    T(final start=TFil_start,
      fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
    der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)))
    "Heat capacity of the filling material"

protected
  final parameter Modelica.Units.SI.SpecificHeatCapacity cpFil=matFil.c
    "Specific heat capacity of the filling material";
  final parameter Modelica.Units.SI.ThermalConductivity kFil=matFil.k
    "Thermal conductivity of the filling material";
  final parameter Modelica.Units.SI.Density dFil=matFil.d
    "Density of the filling material";
  parameter Modelica.Units.SI.HeatCapacity Co_fil=dFil*cpFil*hSeg*Modelica.Constants.pi
      *(rBor^2 - 2*(rTub + eTub)^2) "Heat capacity of the filling material";
  parameter Modelica.Units.SI.SpecificHeatCapacity cpMed=
      Medium.specificHeatCapacityCp(Medium.setState_pTX(
      Medium.p_default,
      Medium.T_default,
      Medium.X_default)) "Specific heat capacity of the fluid";
  parameter Modelica.Units.SI.ThermalConductivity kMed=
      Medium.thermalConductivity(Medium.setState_pTX(
      Medium.p_default,
      Medium.T_default,
      Medium.X_default)) "Thermal conductivity of the fluid";
  parameter Modelica.Units.SI.DynamicViscosity mueMed=Medium.dynamicViscosity(
      Medium.setState_pTX(
      Medium.p_default,
      Medium.T_default,
      Medium.X_default)) "Dynamic viscosity of the fluid";
  parameter Modelica.Units.SI.ThermalResistance Rgb_val(fixed=false)
    "Thermal resistance between grout zone and borehole wall";
  parameter Modelica.Units.SI.ThermalResistance Rgg_val(fixed=false)
    "Thermal resistance between the two grout zones";
  parameter Modelica.Units.SI.ThermalResistance RCondGro_val(fixed=false)
    "Thermal resistance of the pipe wall";
  parameter Real x(fixed=false) "Capacity location";

  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv1
    "Pipe convective resistance"
  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv2
    "Pipe convective resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg1(
    final R=RCondGro_val) "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg2(
    final R=RCondGro_val) "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb1(
    final R=Rgb_val) "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb2(
    final R=Rgb_val) "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg(
    final R=Rgg_val) "Grout thermal resistance"
  Modelica.Blocks.Sources.RealExpression RVol1(y=
    convectionResistance(
      hSeg=hSeg,
      rTub=rTub,
      kMed=kMed,
      mueMed=mueMed,
      cpMed=cpMed,
      m_flow=m1_flow,
      m_flow_nominal=m1_flow_nominal))
    "Convective and thermal resistance at fluid 1"
  Modelica.Blocks.Sources.RealExpression RVol2(y=
    convectionResistance(
      hSeg=hSeg,
      rTub=rTub,
      kMed=kMed,
      mueMed=mueMed,
      cpMed=cpMed,
      m_flow=m2_flow,
      m_flow_nominal=m2_flow_nominal))
    "Convective and thermal resistance at fluid 2"
initial equation
  (Rgb_val, Rgg_val, RCondGro_val, x) =
    singleUTubeResistances(
    hSeg=hSeg,
    rBor=rBor,
    rTub=rTub,
    eTub=eTub,
    xC=xC,
    kSoi=matSoi.k,
    kFil=matFil.k,
    kTub=kTub);

equation
  connect(vol1.heatPort, RConv1.fluid)
  connect(RConv1.solid, Rpg1.port_a)
  connect(Rpg1.port_b, capFil1.port)
  connect(capFil1.port, Rgb1.port_a)
  connect(capFil1.port, Rgg.port_a)
  connect(Rgb1.port_b, port)
  connect(RConv2.solid, Rpg2.port_a)
  connect(Rpg2.port_b, capFil2.port)
  connect(RConv2.fluid, vol2.heatPort)
  connect(capFil2.port, Rgb2.port_a)
  connect(Rgg.port_b, capFil2.port)
  connect(Rgb2.port_b, port)
  connect(RVol1.y, RConv1.Rc)
  connect(RVol2.y, RConv2.Rc)
end HexInternalElement;


Base classes for Borehole

within Buildings.Fluid.Geothermal.Boreholes;
package BaseClasses "Base classes for Borehole"
 extends Modelica.Icons.BasesPackage;
end BaseClasses;


Prescribed temperature at the outer boundary of a single U tube borehole. This model computes the temperature boundary condition at the outer boundary of the borehole.
It takes as an input the heat flow rate at the center of the borehole.
This heat flow rate is averaged over the sample period.
At each sampling interval, typically every one week, a new temperature boundary conditions is computed using
the analytical solution to a line source heat transfer problem.

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses;
model SingleUTubeBoundaryCondition
  "Prescribed temperature at the outer boundary of a single U tube borehole"
  replaceable parameter Buildings.HeatTransfer.Data.Soil.Generic matSoi
    "Thermal properties of the soil"
  parameter Modelica.Units.SI.Radius rExt=3
    "Distance from the brine where the calculation is performed";
  parameter Modelica.Units.SI.Height hSeg=10 "Height of the segment";
  parameter Modelica.Units.SI.Temperature TExt_start=283.15
    "Initial external temperature";
  parameter Modelica.Units.SI.Time samplePeriod=604800
    "Period between two samples";
  ExtendableArray table=ExtendableArray()
    "Extentable array, used to store history of rate of heat flows";
  Modelica.Units.SI.HeatFlowRate QAve_flow
    "Average heat flux over a time period";
  Modelica.Blocks.Interfaces.RealInput Q_flow(unit="W")
    "Heat flow rate at the center of the borehole, positive if heat is added to soil"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_b port "Heat port"
protected
  final parameter Modelica.Units.SI.SpecificHeatCapacity c=matSoi.c
    "Specific heat capacity of the soil";
  final parameter Modelica.Units.SI.ThermalConductivity k=matSoi.k
    "Thermal conductivity of the soil";
  final parameter Modelica.Units.SI.Density d=matSoi.d "Density of the soil";
  Modelica.Units.SI.Energy UOld "Internal energy at the previous period";
  Modelica.Units.SI.Energy U
    "Current internal energy, defined as U=0 for t=tStart";
  final parameter Modelica.Units.SI.Time startTime(fixed=false)
    "Start time of the simulation";
  Integer iSam(min=1)
    "Counter for how many time the model was sampled. Defined as iSam=1 when called at t=0";
initial equation
  U         = 0;
  UOld      = 0;
  startTime = time;
  iSam      = 1;
  port.T    = TExt_start;
  QAve_flow = 0;
equation
  der(U) = Q_flow;

  when sample(startTime, samplePeriod) then
    QAve_flow = (U-pre(UOld))/samplePeriod;
    UOld      = U;
    port.T    = TExt_start + Buildings.Fluid.Geothermal.Boreholes.BaseClasses.temperatureDrop(
                                 table=table, iSam=pre(iSam),
                                 Q_flow=QAve_flow, samplePeriod=samplePeriod,
                                 rExt=rExt, hSeg=hSeg,
                                 k=k, d=d, c=c);
    iSam = pre(iSam)+1;
  end when;
end SingleUTubeBoundaryCondition;


Thermal resistances for single U-tube. This model computes the thermal resistances of a
single-U-tube borehole using the method of Bauer et al. (2011).
It also computes the fluid-to-ground thermal resistance Rb
and the grout-to-grout thermal resistance Ra as defined by
Hellstroem (1991) using the multipole method.

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses;
function singleUTubeResistances "Thermal resistances for single U-tube"

  // Geometry of the borehole
  input Modelica.Units.SI.Height hSeg "Height of the element";
  input Modelica.Units.SI.Radius rBor "Radius of the borehole";
  // Geometry of the pipe
  input Modelica.Units.SI.Radius rTub "Radius of the tube";
  input Modelica.Units.SI.Length eTub "Thickness of the tubes";
  input Modelica.Units.SI.Length xC
    "Shank spacing, defined as the distance between the center of a pipe and the center of the borehole";

  // Thermal properties
  input Modelica.Units.SI.ThermalConductivity kSoi
    "Thermal conductivity of the soi";
  input Modelica.Units.SI.ThermalConductivity kFil
    "Thermal conductivity of the grout";
  input Modelica.Units.SI.ThermalConductivity kTub
    "Thermal conductivity of the tube";

  // Outputs
  output Modelica.Units.SI.ThermalResistance Rgb
    "Thermal resistance between the grout zone and the borehole wall";
  output Modelica.Units.SI.ThermalResistance Rgg
    "Thermal resistance between the two grout zones";
  output Modelica.Units.SI.ThermalResistance RCondGro
    "Thermal resistance between the pipe wall ant the capacity in the grout";
  output Real x "Capacity location";

protected
  Boolean test=false "Thermodynamic test for R and x value";

  Modelica.Units.SI.ThermalResistance Rg
    "Thermal resistance between outer borehole wall and one tube";
  Modelica.Units.SI.ThermalResistance Rar
    "Thermal resistance between the two pipe outer walls";
  Modelica.Units.SI.ThermalResistance RCondPipe
    "Thermal resistance of the pipe wall";

  Real Rb
    "Fluid-to-grout resistance, as defined by Hellstroem. Resistance from the fluid in the pipe to the borehole wall";
  Real Ra
    "Grout-to-grout resistance (2D) as defined by Hellstroem. Interaction between the different grout part";

  // Help variables
  Real sigma "Help variable as defined by Hellstroem";
  Real beta "Help variable as defined by Hellstroem";
  Real R_1delta_LS
    "One leg of the triangle resistance network, corresponding to the line source solution";
  Real R_1delta_MP
    "One leg of the triangle resistance network, corresponding to the multipole solution";
  Real Ra_LS
    "Grout-to-grout resistance calculated with the line-source approximation";

  Integer i=1 "Loop counter";

algorithm
  // ********** Rb and Ra from multipole **********
  // Help variables
  RCondPipe :=Modelica.Math.log((rTub + eTub)/rTub)/(2*Modelica.Constants.pi*hSeg*kTub);
  beta :=2*Modelica.Constants.pi*kFil*RCondPipe;
  sigma :=(kFil - kSoi)/(kFil + kSoi);
  R_1delta_LS :=1/(2*Modelica.Constants.pi*kFil)*(log(rBor/(rTub + eTub)) + log(rBor/(2*xC)) +
    sigma*log(rBor^4/(rBor^4 - xC^4)));
  R_1delta_MP :=R_1delta_LS - 1/(2*Modelica.Constants.pi*kFil)*((rTub + eTub)^2/
    (4*xC^2)*(1 - sigma*4*xC^4/(rBor^4 - xC^4))^2)/((1 + beta)/(1 - beta) + (
    rTub + eTub)^2/(4*xC^2)*(1 + sigma*16*xC^4*rBor^4/(rBor^4 - xC^4)^2));
  Ra_LS      :=1/(Modelica.Constants.pi*kFil)*(log(2*xC/rTub) + sigma*log((
    rBor^2 + xC^2)/(rBor^2 - xC^2)));

  //Rb and Ra
  Rb :=R_1delta_MP/2;
  Ra :=Ra_LS - 1/(Modelica.Constants.pi*kFil)*(rTub^2/(4*xC^2)*(1 + sigma*
    4*rBor^4*xC^2/(rBor^4 - xC^4))/((1 + beta)/(1 - beta) - rTub^2/(4*xC^2) +
    sigma*2*rTub^2*rBor^2*(rBor^4 + xC^4)/(rBor^4 - xC^4)^2));

  //Conversion of Rb (resp. Ra) to Rg (resp. Rar) of Bauer:
  Rg  :=2*Rb/hSeg;
  Rar :=Ra/hSeg;

/* **************** Simplification of Bauer for single U-tube ************************
  //Thermal resistance between: Outer wall and one tube
     Rg := Modelica.Math.acosh((rBor^2 + (rTub + eTub)^2 - xC^2)/(2*rBor*(rTub +
       eTub)))/(2*Modelica.Constants.pi*hSeg*kFil)*(1.601 - 0.888*xC/rBor);

  //Thermal resistance between: The two pipe outer walls
  Rar := Modelica.Math.acosh((2*xC^2 - (rTub + eTub)^2)/(rTub + eTub)^2)/(2*
       Modelica.Constants.pi*hSeg*kFil);
*************************************************************************************** */

  // ********** Resistances and capacity location according to Bauer **********
  while test == false and i <= 15 loop
    // Capacity location (with correction factor in case that the test is negative)
    x := Modelica.Math.log(sqrt(rBor^2 + 2*(rTub + eTub)^2)/(2*(rTub + eTub)))/
      Modelica.Math.log(rBor/(sqrt(2)*(rTub + eTub)))*((15 - i + 1)/15);

    //Thermal resistance between the grout zone and bore hole wall
    Rgb := (1 - x)*Rg;

    //Thermal resistance between the two grout zones
    Rgg := 2*Rgb*(Rar - 2*x*Rg)/(2*Rgb - Rar + 2*x*Rg);

    // Thermodynamic test to check if negative R values make sense.
    // If not, decrease x-value.
    test := (1/Rgg + 1/2/Rgb > 0);
    i := i + 1;
  end while;
  // Check for errors.
  assert(test,
  "Maximum number of iterations exceeded. Check the borehole geometry.
  The tubes may be too close to the borehole wall.
  Input to the function
  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.singleUTubeResistances
  is
           hSeg = " + String(hSeg) + " m
           rBor = " + String(rBor) + " m
           rTub = " + String(rTub) + " m
           eTub = " + String(eTub) + " m
           xC   = " + String(xC)  + " m
           kSoi = " + String(kSoi) + " W/m/K
           kFil = " + String(kFil) + " W/m/K
           kTub = " + String(kTub) + " W/m/K
  Computed x    = " + String(x) + " K/W
           Rgb  = " + String(Rgb) + " K/W
           Rgg  = " + String(Rgg) + " K/W");

  //Conduction resistance in grout from pipe wall to capacity in grout
  RCondGro := x*Rg + RCondPipe;
end singleUTubeResistances;


Calculate the temperature drop of the soil at the external boundary of the cylinder. This function calculates the temperature drop of the soil at the outer boundary of the cylinder.
The analytical formula of Hart and Couvillion (1986) for constant
heat extraction is adapted to a non-constant heat flux.
To adapt the formula for a variable rate of heat extraction,
different constant heat extraction rates, starting at different time instances,
are super-imposed.
To obtain the temperature drop at the time <i>t=n*Δt</i>, the effects of
constant rate of heat extractions are super-imposed as

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses;
function temperatureDrop
  "Calculate the temperature drop of the soil at the external boundary of the cylinder"
input ExtendableArray table
    "External object that contains the history terms of the heat flux";
input Integer iSam(min=1)
    "Counter for how many time the model was sampled. Defined as iSam=1 when called at t=0";

  input Modelica.Units.SI.HeatFlowRate Q_flow
    "Heat flow rate to be stored in the external object";
  input Modelica.Units.SI.Time samplePeriod "Period between two samples";
  input Modelica.Units.SI.Radius rExt "External radius of the cylinder";
  input Modelica.Units.SI.Height hSeg "Height of the cylinder";
  input Modelica.Units.SI.ThermalConductivity k
    "Thermal conductivity of the soil";
  input Modelica.Units.SI.Density d "Density of the soil";
  input Modelica.Units.SI.SpecificHeatCapacity c
    "Specific heat capacity of the soil";
  output Modelica.Units.SI.TemperatureDifference dT
    "Temperature drop of the soil";
protected
  Modelica.Units.SI.Time minSamplePeriod=rExt^2/(4*(k/c/d)*3.8)
    "Minimal length of the sampling period";
  Modelica.Units.SI.HeatFlowRate QL_flow
    "Intermediate variable for heat flow rate at the lower bound of the time interval";
  Modelica.Units.SI.HeatFlowRate QU_flow
    "Intermediate variable for heat flow rate at the upper bound of the time interval";

algorithm
  assert(rExt*rExt/(4*(k/c/d)*samplePeriod)<=3.8,
  "The samplePeriod has to be bigger than " + String(minSamplePeriod) + " for convergence purpose.
  samplePeriod = "+ String(samplePeriod));
  if iSam == 1 then
    // First call, at t=0
    dT := 0;
    QL_flow := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
                       table=table, iX=iSam, x=Q_flow, iY=iSam);
  else
    dT := 0;
    // The first evaluation is at iSam=2, in which case we have one term of the sum,
    // and t=samplePeriod=(iSam-1)*samplePeriod
   for i in 1:(iSam-1) loop
      QL_flow := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
        table=table, iX=iSam, x=Q_flow, iY=iSam+1-i);
      QU_flow := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
        table=table, iX=iSam, x=Q_flow, iY=iSam-i);
      // The division by hSeg is because QU_flow and QL_flow are in [W], but the equation
      // requires [W/m], i.e., heat flow rate per unit length of the line source.
      dT := dT + 1/(4*Modelica.Constants.pi*k)*
        Buildings.Fluid.Geothermal.Boreholes.BaseClasses.powerSeries(
           u=c*d/(4*k*i*samplePeriod)*rExt^2, N=10)*
        (QL_flow-QU_flow)/hSeg;
 end for;
  end if;
end temperatureDrop;


within Buildings.Fluid.Geothermal.Boreholes.BaseClasses.Examples;
model BoreholeSegment
  "Model that tests a basic segment that is used to build a borehole"
  extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Water "Medium in the pipes";
 parameter Buildings.HeatTransfer.Data.BoreholeFillings.Bentonite bento
    "Borehole filling material";
 Buildings.Fluid.Geothermal.Boreholes.BaseClasses.BoreholeSegment seg(
    redeclare package Medium = Medium,
    matFil=bento,
    m_flow_nominal=0.2,
    dp_nominal=5,
    rTub=0.02,
    eTub=0.002,
    rBor=0.1,
    rExt=3,
    nSta=9,
    samplePeriod=604800,
    kTub=0.5,
    hSeg=10,
    xC=0.05,
    redeclare Buildings.HeatTransfer.Data.Soil.Concrete matSoi,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial,
    TFil_start=283.15,
    TExt_start=283.15) "Borehole segment"
Fluid.Sources.Boundary_pT sou_1(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    p=101340,
    T=303.15) "Flow source"
 Buildings.Fluid.Sources.Boundary_pT sin_2(
redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Flow sink"
equation
  connect(sou_1.ports[1], seg.port_a1)
  connect(seg.port_b1, seg.port_a2)
  connect(seg.port_b2, sin_2.ports[1])
end BoreholeSegment;


within Buildings.Fluid.Geothermal.Boreholes.BaseClasses.Examples;
model ConvectionResistance
  "Model that tests a basic segment that is used to build a borehole"
  extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Water "Medium in the pipes";

  parameter Modelica.Units.SI.SpecificHeatCapacity cpMed=
      Medium.specificHeatCapacityCp(Medium.setState_pTX(
      Medium.p_default,
      Medium.T_default,
      Medium.X_default)) "Specific heat capacity of the fluid";
  parameter Modelica.Units.SI.ThermalConductivity kMed=
      Medium.thermalConductivity(Medium.setState_pTX(
      Medium.p_default,
      Medium.T_default,
      Medium.X_default)) "Thermal conductivity of the fluid";
  parameter Modelica.Units.SI.DynamicViscosity mueMed=Medium.dynamicViscosity(
      Medium.setState_pTX(
      Medium.p_default,
      Medium.T_default,
      Medium.X_default)) "Dynamic viscosity of the fluid";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=3000/10/4200
    "Nominal mass flow rate";
  Modelica.Units.SI.MassFlowRate m_flow "Mass flow rate";
  Modelica.Units.SI.ThermalResistance R
    "Thermal resistance between the fluid and the tube";
protected
 constant Real conv(unit="1/s")=1 "Conversion factor";

equation
  m_flow =m_flow_nominal*(time - 0.5)*2*conv;
  R = Buildings.Fluid.Geothermal.Boreholes.BaseClasses.convectionResistance(
    hSeg=10,
    rTub=0.02,
    kMed=kMed,
    mueMed=mueMed,
    cpMed=cpMed,
    m_flow=m_flow,
    m_flow_nominal=m_flow_nominal);
end ConvectionResistance;


Test problem for the function that exchanges values. This example tests the function

Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues by
assigning and reading different elements of the array.
The assert statements check whether the returned values is correct.

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses.Examples;
model ExchangeValues "Test problem for the function that exchanges values"
 extends Modelica.Icons.Example;
  parameter Real x = 3;
  Real y;
  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.ExtendableArray table=
    Buildings.Fluid.Geothermal.Boreholes.BaseClasses.ExtendableArray()
    "Extentable array, used to store history of rate of heat flows";

algorithm
  y := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
                       table=table, iX=1, x=x, iY=1);
  assert(abs(y-3) < 1E-10, "Error in implementation of exchangeVaules.");
  y := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
                       table=table, iX=2, x=4*x, iY=1);
  assert(abs(y-3) < 1E-10, "Error in implementation of exchangeVaules.");
  y := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
                       table=table, iX=2, x=4*x, iY=2);
  assert(abs(y-12) < 1E-10, "Error in implementation of exchangeVaules.");
  y := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
                       table=table, iX=200, x=5*x, iY=1);
  assert(abs(y-3) < 1E-10, "Error in implementation of exchangeVaules.");
  y := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
                       table=table, iX=10, x=6*x, iY=200);
  assert(abs(y-15) < 1E-10, "Error in implementation of exchangeVaules.");
  y := Buildings.Fluid.Geothermal.Boreholes.BaseClasses.exchangeValues(
                       table=table, iX=10, x=6*x, iY=1);
  assert(abs(y-3) < 1E-10, "Error in implementation of exchangeVaules.");
end ExchangeValues;


within Buildings.Fluid.Geothermal.Boreholes.BaseClasses.Examples;
model HexInternalElement
  "Model that tests the basic element that is used to built borehole models"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water "Fluid";

  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.HexInternalElement hex(
    redeclare package Medium = Medium,
    m1_flow_nominal=0.3,
    m2_flow_nominal=0.3,
    rTub=0.02,
    kTub=0.5,
    rBor=0.1,
    xC=0.025,
    kSoi=3.1,
    dp1_nominal=5,
    dp2_nominal=5,
    hSeg=20,
    redeclare parameter Buildings.HeatTransfer.Data.BoreholeFillings.Bentonite matFil,
    redeclare parameter Buildings.HeatTransfer.Data.Soil.Sandstone matSoi,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TFil_start=283.15)
  Buildings.Fluid.Sources.Boundary_pT sou_1(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    p=101340,
    T=303.15)
  Buildings.Fluid.Sources.Boundary_pT sin_2(
    redeclare package Medium = Medium,
    nPorts=1,
    use_p_in=false,
    use_T_in=false,
    p=101330,
    T=283.15)
equation
  connect(sou_1.ports[1], hex.port_a1)
  connect(hex.port_b1, hex.port_a2)
  connect(hex.port_b2, sin_2.ports[1])
end HexInternalElement;


Example models to test base classes

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses;
package Examples "Example models to test base classes"
extends Modelica.Icons.ExamplesPackage;
end Examples;


Test model the temperature boundary condition of a single U tube heat exchanger.

within Buildings.Fluid.Geothermal.Boreholes.BaseClasses.Examples;
model SingleUTubeBoundaryCondition
  "Test model the temperature boundary condition of a single U tube heat exchanger"
  extends Modelica.Icons.Example;
  parameter Modelica.Units.SI.HeatFlowRate Q_flow=-50
    "Heat flow rate extracted at center of cylinder";
  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.SingleUTubeBoundaryCondition
      TBouSte(
      final rExt=3,
      final samplePeriod=604800,
      hSeg=1,
      redeclare final Buildings.HeatTransfer.Data.Soil.Sandstone matSoi,
      TExt_start=293.15) "Boundary condition"
 Modelica.Blocks.Sources.Step step(
    height=Q_flow,
    offset=0,
    startTime=0)
  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.SingleUTubeBoundaryCondition
    TBouCon(
    final rExt=3,
    final samplePeriod=604800,
    hSeg=1,
    redeclare final Buildings.HeatTransfer.Data.Soil.Sandstone matSoi,
    TExt_start=293.15) "Boundary condition"
 Modelica.Blocks.Sources.Constant con(k=Q_flow)
  Buildings.Fluid.Geothermal.Boreholes.BaseClasses.SingleUTubeBoundaryCondition
    TBouPul(
    final rExt=3,
    final samplePeriod=604800,
    hSeg=1,
    redeclare final Buildings.HeatTransfer.Data.Soil.Sandstone matSoi,
    TExt_start=293.15) "Boundary condition"
 Modelica.Blocks.Sources.Pulse pulse(
    offset=0,
    startTime=0,
    amplitude=2*Q_flow,
    period=7200)
equation
  connect(con.y, TBouCon.Q_flow)
  connect(step.y, TBouSte.Q_flow)
  connect(pulse.y, TBouPul.Q_flow)
end SingleUTubeBoundaryCondition;


within Buildings.Fluid.Geothermal.Boreholes.BaseClasses.Examples;
model SingleUTubeResistances "Model that tests the resistances in the borehole"
  extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Water "Medium in the pipes";

  replaceable parameter Buildings.HeatTransfer.Data.Soil.Granite matSoi
    "Thermal properties of soil"

  replaceable parameter Buildings.HeatTransfer.Data.BoreholeFillings.Bentonite matFil
    "Thermal properties of the filling material"

  parameter Modelica.Units.SI.Height hSeg=1 "Height of the element";
  parameter Modelica.Units.SI.Radius rBor=0.2 "Radius of the borehole";
  parameter Modelica.Units.SI.Radius rTub=0.02 "Radius of the tubes"
  parameter Modelica.Units.SI.ThermalConductivity kTub=0.5
    "Thermal conductivity of the tube"
  parameter Modelica.Units.SI.Length eTub=0.002 "Thickness of a tube"
  parameter Modelica.Units.SI.Length xC=0.05
    "Shank spacing, defined as the distance between the center of a pipe and the center of the borehole"

  parameter Modelica.Units.SI.ThermalResistance Rgb(fixed=false)
    "Thermal resistance between grout zone and borehole wall";
  parameter Modelica.Units.SI.ThermalResistance Rgg(fixed=false)
    "Thermal resistance between the two grout zones";
  parameter Modelica.Units.SI.ThermalResistance RCondGro(fixed=false)
    "Thermal resistance of the pipe wall";
  parameter Real x(fixed=false) "Capacity location";

initial equation
  (Rgb, Rgg, RCondGro, x) =
    Buildings.Fluid.Geothermal.Boreholes.BaseClasses.singleUTubeResistances(
    hSeg=hSeg,
    rBor=rBor,
    rTub=rTub,
    eTub=eTub,
    xC=xC,
    kSoi=matSoi.k,
    kFil=matFil.k,
    kTub=kTub);
end SingleUTubeResistances;


within Buildings.Fluid.Geothermal.Boreholes;
package Examples "Example files for borehole heat exchanger"
extends Modelica.Icons.ExamplesPackage;
end Examples;


Model that tests the borehole model

within Buildings.Fluid.Geothermal.Boreholes.Examples;
model UTube "Model that tests the borehole model"
  extends Modelica.Icons.Example;
 package Medium = Buildings.Media.Water;
 Buildings.Fluid.Geothermal.Boreholes.UTube borHol(
    redeclare package Medium = Medium,
    hBor=150,
    dp_nominal=10000,
    dT_dz=0.0015,
    samplePeriod=604800,
    m_flow_nominal=0.3,
    redeclare parameter
      Buildings.HeatTransfer.Data.BoreholeFillings.Bentonite matFil,
    redeclare parameter Buildings.HeatTransfer.Data.Soil.Sandstone matSoi,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TExt0_start=283.15,
    TFil0_start=283.15) "Borehole heat exchanger"
  Sources.Boundary_ph sin(
    redeclare package Medium = Medium,
    nPorts=1) "Sink"
  Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_m_flow_in=true,
    T=298.15) "Source"
  Modelica.Blocks.Sources.Pulse pulse(
    amplitude=0.3,
    period=365*86400,
    startTime=365*86400/4)
equation
  connect(sou.ports[1], borHol.port_a)
  connect(borHol.port_b, sin.ports[1])
  connect(pulse.y, sou.m_flow_in)
end UTube;


Borefield model containing single U-tube boreholes. This model simulates a borehole thermal energy storage system with multiple
zones of single U-tube boreholes. Boreholes within the same zone are connected
in parallel. The borefield configuration and thermal parameters are defined in
the borFieDat record.

within Buildings.Fluid.Geothermal.ZonedBorefields;
model OneUTube "Borefield model containing single U-tube boreholes"
  extends Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.PartialStorage(
    redeclare Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.OneUTube borHol[nZon]);
end OneUTube;


within Buildings.Fluid.Geothermal;
package ZonedBorefields "Package with borefield models for thermal energy storage with multiple parallel loops"
  extends Modelica.Icons.VariantsPackage;
end ZonedBorefields;


Borefield model containing double U-tube boreholes. This model simulates a borehole thermal energy storage system with multiple
zones of double U-tube boreholes. Boreholes within the same zone are connected
in parallel. The borefield configuration and thermal parameters are defined in
the borFieDat record.

within Buildings.Fluid.Geothermal.ZonedBorefields;
model TwoUTubes "Borefield model containing double U-tube boreholes"
  extends Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.PartialStorage(
    redeclare Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.TwoUTube borHol[nZon]);
end TwoUTubes;


User's Guide

within Buildings.Fluid.Geothermal.ZonedBorefields;
package UsersGuide "User's Guide"
  extends Modelica.Icons.Information;

connects the outlet of the first zone to the inlet of the second zone.

Model description

The borefield models rely on the following key assumptions:

The thermal properties of the soil (conductivity and diffusivity) are constant,
homogenous and isotropic.

The conductivity, capacitance and density of the grout and pipe material are constant, homogenous and isotropic.

There is no heat extraction or injection prior to the simulation start.

All of the boreholes in the borefield have uniform dimensions (including the pipe dimensions).

Inside the boreholes, the non-advective heat transfer is only in the radial direction.

The borefield models are constructed in two main parts: the borehole(s) and the ground heat transfer.
The former is modeled as a vertical discretization of borehole segments, where a uniform temperature increase or decrease
(due to heat injection or extraction) is superimposed to the far-field ground temperature to obtain the borehole wall
temperature. The thermal effects of the circulating fluid (including the convection resistance),
of the pipes and of the filling material are all taken into consideration, which allows modeling
short-term thermal effects in the borehole. The borehole segments do not take into account axial effects,
thus only radial (horizontal) effects are considered within the borehole(s). The thermal
behavior between the pipes and borehole wall are modeled as a resistance-capacitance network, with
the grout capacitance being split in the number of pipes present in a borehole section.
The capacitance is only present if the steadyState parameter of the borehole filling material
is set to false.
The figure below shows an example for a borehole section within a single U-tube configuration.

The second main part of the borefield models is the ground heat transfer. The heat transfer in the ground
is modeled analytically as a superposition of convolution integrals between the heat flux at each
of the borehole segments and the borefield's thermal response factor.

The model uses a load aggregation technique to reduce the time required to calculate
the borehole wall temperature changes resulting from heat injection or extraction.

The ground heat transfer takes into account both the borehole axial effects and
the borehole radial effects which are a result of its cylindrical geometry.
Further information on the
ground heat transfer model and the thermal temperature response calculations can
be found in

Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.GroundTemperatureResponse.

Calculation times

The calculation times for both the initialization and the time integration depends
on the square of the number of zones and the number of segments.
The number of boreholes should only weakly impact on the initialization time.

"));

end UsersGuide;


within Buildings.Fluid.Geothermal.ZonedBorefields;
package BaseClasses "Base classes used in Buildings.Fluid.Geothermal.ZonedBorefields"
extends Modelica.Icons.BasesPackage;
end BaseClasses;


within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses;
partial model PartialStorage
  "Partial model for borehole thermal energy storage with independent borefield zones"
  extends
    Buildings.Fluid.Geothermal.ZonedBorefields.Interfaces.PartialTwoNPortsInterface(
    final nPorts=nZon,
    final m_flow_nominal=borFieDat.conDat.mZon_flow_nominal);
  extends
    Buildings.Fluid.Geothermal.ZonedBorefields.Interfaces.TwoNPortsFlowResistanceParameters(
    final nPorts=nZon,
    final dp_nominal=borFieDat.conDat.dp_nominal,
    final computeFlowResistance={_dp_nominal > Modelica.Constants.eps for _dp_nominal in borFieDat.conDat.dp_nominal})

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

  // Simulation parameters
  parameter Modelica.Units.SI.Time tLoaAgg = 3600.0
    "Time resolution of load aggregation";
  parameter Integer nCel(min=1) = 5
    "Number of cells per aggregation level";
  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";

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

  // Initialization
  parameter Medium.AbsolutePressure p_start = Medium.p_default
    "Start value of pressure"
  // Temperature gradient in undisturbed soil
  parameter Modelica.Units.SI.Temperature TExt0_start=283.15
    "Initial far field temperature"
  parameter Modelica.Units.SI.Temperature TExt_start[nSeg]=
    {if z[i] >= z0 then TExt0_start + (z[i] - z0)*dT_dz else TExt0_start for i in 1:nSeg}
    "Temperature of the undisturbed ground"
  parameter Modelica.Units.SI.Temperature TGro_start[nSeg]=TExt_start
    "Start value of grout temperature"
  parameter Modelica.Units.SI.Temperature TFlu_start[nSeg]=TGro_start
    "Start value of fluid temperature"
  parameter Modelica.Units.SI.Height z0=10
    "Depth below which the temperature gradient starts"
  parameter Real dT_dz(final unit="K/m", min=0) = 0.01
    "Vertical temperature gradient of the undisturbed soil for h below z0"

  // General parameters of borefield
  parameter Buildings.Fluid.Geothermal.ZonedBorefields.Data.Borefield.Template borFieDat
    "Borefield data record"
  final parameter Integer nZon(min=1) = borFieDat.conDat.nZon
    "Total number of independent bore field zones";
  final parameter Integer[nZon] nBorPerZon(each min=1) = borFieDat.conDat.nBorPerZon
    "Number of boreholes per borefield zone";

  // Models
  replaceable
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialBorehole
    borHol[nZon] constrainedby
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialBorehole(
    redeclare each final package Medium = Medium,
    final borFieDat=zonDat,
    each final nSeg=nSeg,
    final m_flow_nominal=borFieDat.conDat.mZon_flow_nominal,
    final dp_nominal=dp_nominal,
    each final allowFlowReversal=allowFlowReversal,
    final m_flow_small=m_flow_small,
    each final show_T=show_T,
    final computeFlowResistance=computeFlowResistance,
    final from_dp=from_dp,
    final linearizeFlowResistance=linearizeFlowResistance,
    final deltaM=deltaM,
    each final energyDynamics=energyDynamics,
    each final p_start=p_start,
    each final mSenFac=mSenFac,
    each final TFlu_start=TFlu_start,
    each final TGro_start=TGro_start) "Borehole"

  Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.GroundTemperatureResponse groTemRes(
    final tLoaAgg=tLoaAgg,
    final nCel=nCel,
    final nSeg=nSeg,
    final borFieDat=borFieDat) "Ground thermal response"

  Modelica.Blocks.Interfaces.RealOutput TBorAve[nZon](
    each quantity="ThermodynamicTemperature",
    each unit="K",
    each displayUnit="degC",
    each start=TExt0_start)
    "Average borehole wall temperature in the borefield"

  Modelica.Blocks.Interfaces.RealOutput QBorAve[nZon](
    each quantity="HeatFlowRate",
    each unit="W") "Average (per borehole) heat transfer rate in each zone"

protected
  constant Real mSenFac(min=1)=1
    "Factor for scaling the sensible thermal mass of the volume";

  parameter Modelica.Units.SI.Height z[nSeg]={borFieDat.conDat.hBor/nSeg*(i - 0.5) for i in 1:nSeg}
    "Distance from the surface to the considered segment";

  // General parameters of the boreholes. These records are required because
  // the borehole model expects a configuration record from the
  // Buildings.Fluid.Geothermal.Borefields that is different from the record in
  // the Buildings.Fluid.Geothermal.ZonedBorefields package.
  final parameter Borefields.Data.Borefield.Template zonDat[nZon](
    each filDat=borFieDat.filDat,
    each soiDat=borFieDat.soiDat,
    conDat=zonConDat) "Data record for the boreholes"
  final parameter Borefields.Data.Configuration.Template zonConDat[nZon](
    each borCon=borFieDat.conDat.borCon,
    each use_Rb=borFieDat.conDat.use_Rb,
    each Rb=borFieDat.conDat.Rb,
    mBor_flow_nominal=borFieDat.conDat.mBor_flow_nominal,
    mBorFie_flow_nominal=borFieDat.conDat.mZon_flow_nominal,
    dp_nominal=borFieDat.conDat.dp_nominal,
    each hBor=borFieDat.conDat.hBor,
    each rBor=borFieDat.conDat.rBor,
    each dBor=borFieDat.conDat.dBor,
    nBor=borFieDat.conDat.nBorPerZon,
    each cooBor=borFieDat.conDat.cooBor,
    each rTub=borFieDat.conDat.rTub,
    each kTub=borFieDat.conDat.kTub,
    each eTub=borFieDat.conDat.eTub,
    each xC=borFieDat.conDat.xC,
    mBor_flow_small=borFieDat.conDat.mBor_flow_small)
    "Configuration data record for each borehole zone"

  Buildings.Fluid.BaseClasses.MassFlowRateMultiplier masFloDiv[nZon](
    redeclare final package Medium = Medium,
    final k=nBorPerZon) "Division of flow rate"

  Buildings.Fluid.BaseClasses.MassFlowRateMultiplier masFloMul[nZon](
    redeclare final package Medium = Medium,
    final k=nBorPerZon) "Mass flow multiplier"

  Modelica.Blocks.Sources.Constant TSoiUnd[nSeg](
    k = TExt_start,
    y(each unit="K",
      each displayUnit="degC"))
    "Undisturbed soil temperature"

  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor QBorHol[nZon,nSeg]
    "Heat flow rate of all segments of all boreholes"

  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature TemBorWal[nZon,nSeg]
    "Borewall temperature at each segment"

  Modelica.Blocks.Math.Add TSoiDisl[nZon,nSeg](each final k1=1, each final k2=1)
    "Addition of undisturbed soil temperature and change of soil temperature"

  Modelica.Blocks.Routing.Replicator repTSoiUnd[nSeg](each final nout=nZon)
    "Signal replicator for temperature difference of the borehole"
  Buildings.Utilities.Math.Average aveTBor[nZon](each final nin=nSeg)
    "Average temperature of all the borehole segments in each zone"
  Modelica.Blocks.Math.Sum aveQBor[nZon](each nin=nSeg)
    "Average (per borehole) heat transfer rate, equal to the sum of the heat transfer rates att all segments along a borehole"

equation

  connect(borHol.port_wall, QBorHol.port_a)
  connect(TemBorWal.port, QBorHol.port_b)
  connect(QBorHol.Q_flow, groTemRes.QBor_flow)
  connect(TSoiUnd.y, repTSoiUnd.u)
  for i in 1:nZon loop
    for j in 1:nSeg loop
      connect(repTSoiUnd[j].y[i], TSoiDisl[i,j].u2)
    end for;
  end for;
  connect(groTemRes.delTBor, TSoiDisl.u1)
  connect(TSoiDisl.y, TemBorWal.T)
  connect(TSoiDisl.y, aveTBor.u)
  connect(aveTBor.y, TBorAve)
  connect(port_a, masFloDiv.port_b)
  connect(masFloDiv.port_a, borHol.port_a)
  connect(borHol.port_b, masFloMul.port_a)
  connect(masFloMul.port_b, port_b)
  connect(aveQBor.y, QBorAve)
  connect(QBorHol.Q_flow, aveQBor.u)
end PartialStorage;


Model calculating discrete load aggregation. This model calculates the ground temperature response to obtain the temperature
at the wall of each borehole segment in a geothermal system where heat is being
injected into or extracted from the ground.

within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer;
model GroundTemperatureResponse
  "Model calculating discrete load aggregation"
  parameter Modelica.Units.SI.Time tLoaAgg(final min=Modelica.Constants.eps)=
    3600 "Time resolution of load aggregation";
  parameter Integer nCel(min=1)=5 "Number of cells per aggregation level";
  parameter Integer nSeg(min=1)
    "Number of segments per borehole";
  parameter Buildings.Fluid.Geothermal.ZonedBorefields.Data.Borefield.Template borFieDat
    "Record containing all the parameters of the borefield model"

  // Model inputs and outputs
  Modelica.Blocks.Interfaces.RealOutput[nZon, nSeg] delTBor(
    each final unit="K",
    each displayUnit="degC")
    "Temperature difference current borehole wall temperature minus initial borehole wall temperature"
  Modelica.Blocks.Interfaces.RealInput[nZon, nSeg] QBor_flow(each final unit="W")
    "Heat flow from borehole segment (positive if heat from fluid into soil)"

protected
  constant Real lvlBas = 2 "Base for exponential cell growth between levels";
  constant Real ttsMax = exp(5) "Maximum non-dimensional time for g-function calculation";
  // Adjust the timFin parameter for simulation that span more than 50 years
  constant Modelica.Units.SI.Time timFin=50.*8760.*3600.
    "Final time for g-function calculation";
  constant Real relTol = 0.02 "Relative tolerance on distance between boreholes";

  parameter String sha=
    Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.shaKappa(
      nBor=borFieDat.conDat.nBor,
      cooBor=borFieDat.conDat.cooBor,
      hBor=borFieDat.conDat.hBor,
      dBor=borFieDat.conDat.dBor,
      rBor=borFieDat.conDat.rBor,
      aSoi=borFieDat.soiDat.aSoi,
      kSoi=borFieDat.soiDat.kSoi,
      nSeg=nSeg,
      nZon=borFieDat.conDat.nZon,
      iZon=borFieDat.conDat.iZon,
      nBorPerZon=borFieDat.conDat.nBorPerZon,
      nu=nu,
      nTim=i,
      relTol=relTol) "String with encrypted thermal response factor arguments";

  final parameter Integer nSegTot = nZon * nSeg
    "Total number of segments";
  final parameter Integer nZon(min=1) = borFieDat.conDat.nZon
    "Total number of independent borefield zones";
  final parameter Integer i(min=1)=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.countAggregationCells(
      lvlBas=lvlBas,
      nCel=nCel,
      timFin=timFin,
      tLoaAgg=tLoaAgg)
      "Number of aggregation cells";
  final parameter Modelica.Units.SI.Time[i] nu(each fixed=false)
    "Time vector for load aggregation";
  final parameter Modelica.Units.SI.Time t_start(fixed=false)
    "Simulation start time";
  final parameter Real[nSegTot,nSegTot,i] kappa(each fixed=false)
    "Weight factor for each aggregation cell";
  final parameter Real[i] rCel(each fixed=false) "Cell widths";

  discrete Modelica.Units.SI.HeatFlowRate[nSegTot,i] QAgg_flow
    "Vector of aggregated loads";
  discrete Modelica.Units.SI.HeatFlowRate[nSegTot,i] QAggShi_flow
    "Shifted vector of aggregated loads";
  Modelica.Units.SI.HeatFlowRate[nSegTot] QBor_flow_1d
    "Flattened array of heat flows (QBor_flow)";
  Modelica.Units.SI.TemperatureDifference[nSegTot] delTBor_1d
    "Flattened array of temperature differences (delTBor)";
  discrete Integer curCel "Current occupied cell";

  discrete Modelica.Units.SI.TemperatureDifference[nSegTot] delTBor0
    "Previous time step's temperature difference current borehole wall temperature minus initial borehole temperature";
  discrete Real[nSegTot] derDelTBor0(each unit="K/s")
    "Derivative of wall temperature change from previous time steps";
  final parameter Real[nSegTot] dTStepdt(each fixed=false)
    "Time derivative of h_ii/(2*pi*H*Nb*ks) within most recent cell";

  Modelica.Units.SI.Heat[nSegTot,1] U "Accumulated heat flow from all segments";
  discrete Modelica.Units.SI.Heat[nSegTot,1] U_old
    "Accumulated heat flow from all segments at last aggregation step";

initial equation
  QAgg_flow = zeros(nSegTot,i);
  curCel = 1;
  delTBor_1d = zeros(nSegTot);
  QAggShi_flow = QAgg_flow;
  delTBor0 = zeros(nSegTot);
  U = zeros(nSegTot,1);
  U_old = zeros(nSegTot,1);
  derDelTBor0 = zeros(nSegTot);

  (nu,rCel) = Buildings.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.aggregationCellTimes(
    i=i,
    lvlBas=lvlBas,
    nCel=nCel,
    tLoaAgg=tLoaAgg,
    timFin=timFin);

  t_start = time;

  kappa =
    Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.temperatureResponseMatrix(
      nBor=borFieDat.conDat.nBor,
      cooBor=borFieDat.conDat.cooBor,
      hBor=borFieDat.conDat.hBor,
      dBor=borFieDat.conDat.dBor,
      rBor=borFieDat.conDat.rBor,
      aSoi=borFieDat.soiDat.aSoi,
      kSoi=borFieDat.soiDat.kSoi,
      nSeg=nSeg,
      nZon=borFieDat.conDat.nZon,
      iZon=borFieDat.conDat.iZon,
      nBorPerZon=borFieDat.conDat.nBorPerZon,
      nu=nu,
      nTim=i,
      relTol=relTol,
      sha=sha);

  dTStepdt = {kappa[i,i,1]/tLoaAgg for i in 1:nSegTot};

equation
  assert(
    time - t_start < timFin,
    "The simulation is longer than the calculated thermal response factors.
    Adjust the constant `timFin` in
    Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.GroundTemperatureResponse.");
  der(delTBor_1d) = dTStepdt .* QBor_flow_1d + derDelTBor0;
  der(U[:,1]) = QBor_flow_1d;
  for i in 1:nZon loop
    for j in 1:nSeg loop
      delTBor[i,j] = delTBor_1d[(i-1)* nSeg + j];
      QBor_flow[i,j] = QBor_flow_1d[(i-1)* nSeg + j];
    end for;
  end for;

  when sample(t_start, tLoaAgg) then
    // Assign average load since last aggregation step to the first cell of the
    // aggregation vector
    U_old = U;

    // Store (U - pre(U_old))/tLoaAgg in QAgg_flow[1], and pre(QAggShi_flow) in the other elements
    QAgg_flow = cat(2, (U - pre(U_old))/tLoaAgg, pre(QAggShi_flow[:,2:end]));
    // Shift loads in aggregation cells
    (curCel,QAggShi_flow) = Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.shiftAggregationCells(
      i=i,
      nSeg=nSegTot,
      QAgg_flow=QAgg_flow,
      rCel=rCel,
      nu=nu,
      curTim=(time - t_start));

    // Determine the temperature change at the next aggregation step (assuming
    // no loads until then)
    delTBor0 = Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.temporalSuperposition(
      i=i,
      nSeg=nSegTot,
      QAgg_flow=QAggShi_flow,
      kappa=kappa,
      curCel=curCel);

    derDelTBor0 = (delTBor0 - delTBor_1d) / tLoaAgg;
  end when;
end GroundTemperatureResponse;


within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses;
package HeatTransfer "Package with ground heat transfer models"
extends Modelica.Icons.VariantsPackage;
end HeatTransfer;


within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer;
package Validation "Validation models for the geothermal heat transfer models"
extends Modelica.Icons.ExamplesPackage;
end Validation;


Verifies the SHA-1 encryption of a zoned borefield. This example uses a zoned borefield parameterization to test the SHA1-encryption of the
arguments required to determine the thermal response factor.

within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.Validation;
model ShaKappa "Verifies the SHA-1 encryption of a zoned borefield"
  extends Modelica.Icons.Example;

  //Input
  parameter String strIn=
    Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.shaKappa(
      nBor=3,
      cooBor={{0,0}, {0,5}, {5,0}},
      hBor=150,
      dBor=4,
      rBor=0.075,
      aSoi=1e-6,
      kSoi=2,
      nSeg=4,
      nZon=2,
      iZon={1, 2, 1},
      nBorPerZon={2, 1},
      nu={300, 3600, 86400, 604800, 2592000, 31536000, 315360000},
      nTim=7,
      relTol=0.02) "SHA1-encrypted thermal response factor";

  //Expected output (SHA1-encryption of (1,{{0,0}},150,4,0.075,1e-6,12,1,26,50,exp(5)))
  parameter String strEx=
    "1d813242b2177c5e31011dc1656add40a2a9d178"
    "Expected string output";

  //Comparison result
  Boolean cmp "Comparison result";

equation
  cmp = Modelica.Utilities.Strings.isEqual(strIn,strEx,false);
end ShaKappa;


This validation case test the cell shifting procedure. This validation case replicates the load-shifting procedure illustred in the figure below by Cimmino (2014).

within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.Validation;
model ShiftAggregationCells
  "This validation case test the cell shifting procedure"
  extends Modelica.Icons.Example;

  discrete Integer curCel "Current occupied cell";
  discrete Modelica.Units.SI.HeatFlowRate[2,5] QAggShi_flow
    "Shifted vector of aggregated loads";

initial equation
  curCel=3;
  QAggShi_flow={{1,3,2,0,0}, {1,3,2,0,0}};

equation
  when (sample(4, 1)) then
    (curCel,QAggShi_flow) = Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.shiftAggregationCells(
        i=5,
        nSeg=2,
        QAgg_flow=pre(QAggShi_flow),
        rCel={1,1,1,2,2},
        nu={1,2,3,5,7},
        curTim=time);
  end when;
end ShiftAggregationCells;


This validation case tests the calculation of thermal resposne factors. This validation case calculates the thermal response matrix for a field of 3
boreholes divided into 2 zones.

within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.Validation;
model TemperatureResponseMatrix
  "This validation case tests the calculation of thermal resposne factors"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Time kappa[8,8,7]=
      Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.temperatureResponseMatrix(
      nBor=3,
      cooBor={{0,0}, {0,5}, {5,0}},
      hBor=150,
      dBor=4,
      rBor=0.075,
      aSoi=1e-6,
      kSoi=2,
      nSeg=4,
      nZon=2,
      iZon={1, 2, 1},
      nBorPerZon={2, 1},
      nu={300, 3600, 86400, 604800, 2592000, 31536000, 315360000},
      nTim=7,
      sha="TemperatureResponseMatrix_zonedBorefield_validation") "Resulting temperature response matrix";

end TemperatureResponseMatrix;


This validation case applies temporal superposition with truncated vectors. This validation case uses a fictional load profile and weighting factors to ensure
that the temporal superposition is correctly done. The curCel input to
the function call truncates the vectors involved in the scalar product such that
the large load in the QAgg_flow vector does not affect the final result.

within Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.Validation;
model TemporalSuperposition
  "This validation case applies temporal superposition with truncated vectors"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.TemperatureDifference[2] supPos=
      Buildings.Fluid.Geothermal.ZonedBorefields.BaseClasses.HeatTransfer.temporalSuperposition(
      i=5,
      nSeg=2,
      QAgg_flow={ {2,0,3,1e6,1e6}, {0,1,0,1e6,1e6}},
      kappa={ { {0.4,0,0.2,1,10},
                {0.1,0.15,0,0,20}},
              { {0.1,0.15,0,0,20},
                {0.2,1,0.3,0,10}}},
      curCel=3) "Temporal superposition";
  Modelica.Units.SI.TemperatureDifference[2] supPosErr;

equation
  supPosErr = abs({2*0.4+3*0.2+0.15, 2*0.1+1}-supPos);
end TemporalSuperposition;


within Buildings.Fluid.Geothermal.ZonedBorefields;
package Data "Collection of data records for ground heat exchanger models"
extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Collection of borefield data records

within Buildings.Fluid.Geothermal.ZonedBorefields.Data;
package Borefield "Collection of borefield data records"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Borefield;


within Buildings.Fluid.Geothermal.ZonedBorefields.Data;
package Configuration "Collection of records of configuration data of borefields"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Configuration;


Collection of records of filling data

within Buildings.Fluid.Geothermal.ZonedBorefields.Data;
package Filling "Collection of records of filling data"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Filling;


Collection of records of soil data

within Buildings.Fluid.Geothermal.ZonedBorefields.Data;
package Soil "Collection of records of soil data"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Soil;


Example models for zoned borefields

within Buildings.Fluid.Geothermal.ZonedBorefields;
package Examples "Example models for zoned borefields"
extends Modelica.Icons.ExamplesPackage;
end Examples;


Description

within Buildings.Fluid.Geothermal.ZonedBorefields.Examples;
model SeriesConnectedZones "Description"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;

  parameter Modelica.Units.SI.Temperature T_start=283.15
    "Initial temperature of the soil";
  final parameter Integer nZon(min=1) = borFieDat.conDat.nZon
    "Total number of independent bore field zones";

  Buildings.Fluid.Geothermal.ZonedBorefields.OneUTube borHol(
    redeclare package Medium = Medium,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    borFieDat=borFieDat,
    TExt0_start=T_start,
    dT_dz=0)
    "Borehole"
  Movers.Preconfigured.FlowControlled_m_flow pum(
    redeclare package Medium = Medium,
    T_start=T_start,
    allowFlowReversal=true,
    addPowerToMedium=false,
    use_riseTime=false,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal[1],
    dp_nominal=60E3) "Circulation pump"
  Sensors.TemperatureTwoPort TBorFieIn(
    redeclare package Medium = Medium,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal[1],
    tau=0) "Inlet temperature of the borefield"
  Sensors.TemperatureTwoPort TBorFieOut(
    redeclare package Medium = Medium,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal[1],
    tau=0) "Outlet temperature of the borefield"
  parameter Buildings.Fluid.Geothermal.ZonedBorefields.Data.Borefield.Validation
    borFieDat(
      filDat=filDat,
      soiDat=soiDat,
      conDat=conDat) "Borefield data"
  Sources.Boundary_ph sin(redeclare package Medium = Medium, nPorts=1)
    "Sink"
  HeatExchangers.HeaterCooler_u hea(
    redeclare package Medium = Medium,
    dp_nominal=10000,
    show_T=true,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal[1],
    m_flow(start=borFieDat.conDat.mZon_flow_nominal[1]),
    p_start=100000,
    Q_flow_nominal=2*Modelica.Constants.pi*borFieDat.soiDat.kSoi*borFieDat.conDat.hBor
        *borFieDat.conDat.nBor) "Heater"
  parameter ZonedBorefields.Data.Configuration.Validation conDat
    "Borefield configuration data"
  parameter ZonedBorefields.Data.Filling.Bentonite filDat
    "Borehole filling data"
  parameter ZonedBorefields.Data.Soil.SandStone soiDat
    "Soil data"

  Modelica.Blocks.Sources.Cosine heaRat(
    amplitude=0.75,
    f=1/(7*24*3600),
    offset=0.5)
    "Heating rate signal"
  Modelica.Blocks.Logical.GreaterThreshold
                            sig
    "Sign of heating rate signal to determine flow direction"
  Modelica.Blocks.Math.BooleanToReal
                            gai(realTrue=conDat.mZon_flow_nominal[1], realFalse
      =-conDat.mZon_flow_nominal[1])
    "Gain for fluid mass flow rate"
equation
  connect(heaRat.y, hea.u)
  connect(hea.port_b, pum.port_a)
  connect(pum.port_b, TBorFieIn.port_a)
  connect(TBorFieIn.port_b, borHol.port_a[1])
  connect(borHol.port_b[1:end - 1], borHol.port_a[2:end])
  connect(borHol.port_b[end], TBorFieOut.port_a)
  connect(TBorFieOut.port_b, hea.port_a)
  connect(sin.ports[1], TBorFieOut.port_b)
  connect(heaRat.y, sig.u)
  connect(gai.y, pum.m_flow_in)
  connect(sig.y, gai.u)
end SeriesConnectedZones;


within Buildings.Fluid.Geothermal.ZonedBorefields;
package Interfaces "Package with interface for bore field models"
extends Modelica.Icons.InterfacesPackage;
end Interfaces;


Partial component with vectors of ports

within Buildings.Fluid.Geothermal.ZonedBorefields.Interfaces;
partial model PartialTwoNPorts "Partial component with vectors of ports"
  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"

  parameter Integer nPorts(min=1)
    "Number of fluid ports on each side";

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


Partial model with two vectors of ports and declaration of quantities for bore field models. This component defines the interface for models with multiple pairs of
inlet and outlet ports, here implemented as two vectors of ports.
It is similar to

Modelica.Fluid.Interfaces.PartialTwoPortTransport, but it does not
include the species balance

within Buildings.Fluid.Geothermal.ZonedBorefields.Interfaces;
partial model PartialTwoNPortsInterface
  "Partial model with two vectors of ports and declaration of quantities for bore field models"
  extends
    Buildings.Fluid.Geothermal.ZonedBorefields.Interfaces.PartialTwoNPorts;

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal[nPorts]
    "Nominal mass flow rate"
  parameter Modelica.Units.SI.MassFlowRate m_flow_small[nPorts](each 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[nPorts](each 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[nPorts](
    each start=_dp_start,
    each displayUnit="Pa") = port_a[:].p - port_b[:].p
    "Pressure difference between port_a and port_b";

  Medium.ThermodynamicState sta_a[nPorts]=
    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[nPorts]=
    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";

Thus, it can be used as a base class for a heat and mass transfer component

The partial model extends

Buildings.Fluid.Geothermal.ZonedBorefields.Interfaces.PartialTwoNPorts
and adds quantities that are used by many models such as
m_flow_nominal, m_flow and dp.

", revisions="

May 9, 2024, by Michael Wetter:

Corrected handling of array for state calculations.

February, 2024, by Massimo Cimmino:

First implementation.

"));

end PartialTwoNPortsInterface;


within Buildings.Fluid.Geothermal.ZonedBorefields;
package Validation "Validation models for zoned geothermal energy storage"
extends Modelica.Icons.ExamplesPackage;

end Validation;


Description. This validation cases tests the independent operation of borefield zones for the
borefield configured in

Buildings.Fluid.Geothermal.ZonedBorefields.Data.Configuration.Validation.

within Buildings.Fluid.Geothermal.ZonedBorefields.Validation;
model SteadyStateBoreholeDynamics "Description"
  extends Buildings.Fluid.Geothermal.ZonedBorefields.Validation.TransientBoreholeDynamics(
    borHol(
      energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyStateInitial),
      filDat(
        steadyState=true));
end SteadyStateBoreholeDynamics;


Description

within Buildings.Fluid.Geothermal.ZonedBorefields.Validation;
model TransientBoreholeDynamics "Description"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water;

  parameter ZonedBorefields.Data.Configuration.Validation conDat
    "Borefield configuration data"
  parameter ZonedBorefields.Data.Filling.Bentonite filDat
    "Borehole filling data"
  parameter ZonedBorefields.Data.Soil.SandStone soiDat
    "Soil data"

  parameter Modelica.Units.SI.Temperature T_start=273.15
    "Initial temperature of the soil";
  final parameter Integer nZon(min=1) = borFieDat.conDat.nZon
    "Total number of independent bore field zones";

  Buildings.Fluid.Geothermal.ZonedBorefields.OneUTube borHol(
    redeclare package Medium = Medium,
    nSeg=6,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    borFieDat=borFieDat,
    TExt0_start=T_start,
    dT_dz=0)
    "Borehole"
  Movers.Preconfigured.FlowControlled_m_flow pum[nZon](
    redeclare each package Medium = Medium,
    each T_start=T_start,
    each addPowerToMedium=false,
    each use_riseTime=false,
    each energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal,
    each dp_nominal=60E3) "Circulation pump"
  Sensors.TemperatureTwoPort TBorFieIn[nZon](
    redeclare each package Medium = Medium,
    each T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal,
    each tau=0) "Inlet temperature of the borefield"
  Sensors.TemperatureTwoPort TBorFieOut[nZon](
    redeclare each package Medium = Medium,
    each T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal,
    each tau=0) "Outlet temperature of the borefield"
  parameter
    Buildings.Fluid.Geothermal.ZonedBorefields.Data.Borefield.Validation
    borFieDat(
    filDat=filDat,
    soiDat=soiDat,
    conDat=conDat) "Borefield data"
  Sources.Boundary_ph sin[nZon](
    redeclare each package Medium = Medium,
    each nPorts=1) "Sink"
  HeatExchangers.HeaterCooler_u hea[nZon](
    redeclare each package Medium = Medium,
    each dp_nominal=10000,
    each show_T=true,
    each energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    each T_start=T_start,
    m_flow_nominal=borFieDat.conDat.mZon_flow_nominal,
    m_flow(start=borFieDat.conDat.mZon_flow_nominal),
    each p_start=100000,
    Q_flow_nominal=2*Modelica.Constants.pi*borFieDat.soiDat.kSoi*borFieDat.conDat.hBor
        *borFieDat.conDat.nBorPerZon) "Heater"
  Modelica.Blocks.Sources.Pulse heaRat[nZon](
    each amplitude=1,
    each width=50,
    period=3600.*24*60*{1,2},
    startTime=3600.*24*30*{0,1}) "Heating rate into each zone"

  Modelica.Blocks.Sources.CombiTimeTable timTabT(
    tableOnFile=true,
    tableName="tab1",
    columns={2,3},
    smoothness=Modelica.Blocks.Types.Smoothness.LinearSegments,
    fileName=Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/Data/Fluid/Geothermal/ZonedBorefields/Validation/SteadyStateBoreholeDynamics.txt"),
    y(each unit="degC",
      each displayUnit="degC"),
    timeScale=3600)
    "Reference results for the average borehole wall temperature in each zone"

  Modelica.Blocks.Sources.Constant m_flow_nominal[nZon](
    k=borFieDat.conDat.mZon_flow_nominal)
    "Design flow rates"
equation
  connect(TBorFieIn.port_b,borHol. port_a)
  connect(borHol.port_b,TBorFieOut. port_a)
  connect(pum.port_b,TBorFieIn. port_a)
  connect(sin[:].ports[1],TBorFieOut[:]. port_b)
  connect(hea.port_b,pum. port_a)
  connect(hea.port_a,TBorFieOut. port_b)
  connect(heaRat.y, hea.u)
  connect(m_flow_nominal.y, pum.m_flow_in)
end TransientBoreholeDynamics;


Comparative model validation with FEFLOW for a response to a pulse in inlet temperature. This validation cases compares the outlet temperature of a borefield with two
zones against the temperatures that were calculated with the FEFLOW software.
The inlet temperature in both zones is a pulse function.

within Buildings.Fluid.Geothermal.ZonedBorefields.Validation.FEFLOW;
model InletTemperaturePulse
  "Comparative model validation with FEFLOW for a response to a pulse in inlet temperature"
  extends Buildings.Fluid.Geothermal.ZonedBorefields.Validation.FEFLOW.MassFlowRatePulse100(
    redeclare Modelica.Blocks.Sources.Constant m_flow(
      k=borFieDat.conDat.mZon_flow_nominal),
    redeclare Modelica.Blocks.Sources.Pulse TIn(
      each amplitude(
        each final unit="K")=13,
      each period=3600*24*10,
      each offset(
        each final unit="K",
        each displayUnit="degC") =280.15),
      TOut(
        fileName=Modelica.Utilities.Files.loadResource("modelica://Buildings/Resources/Data/Fluid/Geothermal/ZonedBorefields/Validation/FEFLOW/InletTemperaturePulse.txt")));
end InletTemperaturePulse;