within Buildings.Fluid.FMI.Validation;
model HeaterFan
  "Heater and fan in series, model configured to allow flow reversal"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Air "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/1000/
      10 "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 2000 "Pressure";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=1000
    "Heat flow rate at u=1, positive for heating";

  parameter Boolean use_p_in = true
    "= true to use a pressure from connector, false to output Medium.p_default"

  ExportContainers.Examples.FMUs.Fan floMac(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    use_p_in=use_p_in) "Flow machine with pressure raise as an input"

  ExportContainers.Examples.FMUs.HeaterCooler_u hea(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    Q_flow_nominal=Q_flow_nominal,
    use_p_in=use_p_in) "Heater"

  Modelica.Blocks.Sources.Constant dp(k=dp_nominal) "Pressure raise of fan"

  Modelica.Blocks.Sources.Constant uHea(k=0.2) "Control signal for heater"

  Source_T sou(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Source for mass flow rate and pressure"

  Sink_T sin(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Sink for flow rate, and source for backflow properties"

  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal) "Mass flow rate"

  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"

  Modelica.Blocks.Sources.Constant TIn(k=293.15) "Inlet temperature"

  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"

  Modelica.Blocks.Sources.Constant TBac(k=303.15)
    "Temperature of backward flow"

  Modelica.Blocks.Sources.Constant X_w_bac(k=0.015)
    "Moisture mass fraction for back flow"

  Modelica.Blocks.Sources.Constant CBac[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for back flow"

  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
equation
  connect(uHea.y, hea.u)
  connect(dp.y, floMac.dp_in)
  connect(sou.outlet, hea.inlet)
  connect(hea.outlet, floMac.inlet)
  connect(sin.inlet, floMac.outlet)
  connect(sin.T_in, TBac.y)
  connect(CBac.y, sin.C_in)
  connect(m_flow.y, sou.m_flow_in)
  connect(sou.p_in, pIn.y)
  connect(TIn.y, sou.T_in)
  connect(C.y, sou.C_in)
  connect(X_w_in.y, sou.X_w_in)
  connect(sin.X_w_in, X_w_bac.y)
end HeaterFan;


within Buildings.Fluid.FMI.Validation;
model HeaterFanPressureDriven
  "Heater and fan in series, model configured to allow flow reversal and pressure driven flow rate"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Air "Medium model";

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/1000/
      10 "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 2000 "Pressure";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=1000
    "Heat flow rate at u=1, positive for heating";

  constant Boolean use_p_in = true
    "Set to true, as this model computes the mass flow rate based on the pressure drop"

  ExportContainers.Examples.FMUs.Fan floMac(
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    use_p_in=use_p_in) "Flow machine with pressure raise as an input"

  ExportContainers.Examples.FMUs.HeaterCooler_u hea(
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    Q_flow_nominal=Q_flow_nominal,
    use_p_in=use_p_in) "Heater"

  Modelica.Blocks.Sources.Constant dp(k=dp_nominal) "Pressure raise of fan"

  Modelica.Blocks.Sources.Constant uHea(k=0.2) "Control signal for heater"

  Source_T sou(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Source for mass flow rate and pressure"

  Sink_T sin(redeclare package Medium = Medium, use_p_in=use_p_in)
    "Sink for flow rate, and source for backflow properties"

  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal) "Mass flow rate"

  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"

  Modelica.Blocks.Sources.Constant TIn(k=293.15) "Inlet temperature"

  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"

  Modelica.Blocks.Sources.Constant TBac(k=303.15)
    "Temperature of backward flow"

  Modelica.Blocks.Sources.Constant X_w_bac(k=0.015)
    "Moisture mass fraction for back flow"

  Modelica.Blocks.Sources.Constant CBac[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for back flow"

  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
  Modelica.Blocks.Math.InverseBlockConstraints invBloCon
    "Block to set up residual function for nonlinear system of equation for pressure drop and mass flow rate"
equation
  connect(uHea.y, hea.u)
  connect(dp.y, floMac.dp_in)
  connect(sou.outlet, hea.inlet)
  connect(hea.outlet, floMac.inlet)
  connect(sin.inlet, floMac.outlet)
  connect(sin.T_in, TBac.y)
  connect(CBac.y, sin.C_in)
  connect(sou.p_in, pIn.y)
  connect(TIn.y, sou.T_in)
  connect(C.y, sou.C_in)
  connect(pIn.y, invBloCon.u1)
  connect(sin.p, invBloCon.u2)
  connect(invBloCon.y1, sou.m_flow_in)
  connect(X_w_in.y, sou.X_w_in)
  connect(X_w_bac.y, sin.X_w_in)
end HeaterFanPressureDriven;


within Buildings.Fluid.FMI.Validation;
model HeaterFan_noReverseFlow
  "Heater and fan in series, model configured to not allow flow reversal"
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Air "Medium model";

  final parameter Boolean allowFlowReversal = false
    "= true to allow flow reversal, false restricts to design direction (inlet -> outlet)";

  parameter Boolean use_p_in = false
    "= true to use a pressure from connector, false to output Medium.p_default"

  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal=Q_flow_nominal/1000/
      10 "Nominal mass flow rate";
  parameter Modelica.Units.SI.PressureDifference dp_nominal(displayUnit="Pa")
     = 2000 "Pressure";
  parameter Modelica.Units.SI.HeatFlowRate Q_flow_nominal=1000
    "Heat flow rate at u=1, positive for heating";

  ExportContainers.Examples.FMUs.Fan floMac(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    final allowFlowReversal=allowFlowReversal,
    use_p_in=use_p_in) "Flow machine with pressure raise as an input"

  ExportContainers.Examples.FMUs.HeaterCooler_u hea(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal,
    dp_nominal=dp_nominal,
    Q_flow_nominal=Q_flow_nominal,
    final allowFlowReversal=allowFlowReversal,
    use_p_in=use_p_in) "Heater"

  Modelica.Blocks.Sources.Constant dp(k=1000) "Pressure raise of fan"

  Modelica.Blocks.Sources.Constant uHea(k=0.2) "Control signal for heater"

  Source_T sou(
    redeclare package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    use_p_in=use_p_in) "Source for mass flow rate and pressure"

  Modelica.Blocks.Sources.Constant m_flow(k=m_flow_nominal) "Mass flow rate"

  Modelica.Blocks.Sources.Constant pIn(k=100000) "Inlet pressure"

  Modelica.Blocks.Sources.Constant TIn(k=293.15) "Inlet temperature"

  Modelica.Blocks.Sources.Constant X_w_in(k=0.01) "Inlet mass fraction"

  Modelica.Blocks.Sources.Constant C[Medium.nC](each k=0.01)
  if Medium.nC > 0 "Trace substances for forward flow"
equation
  connect(uHea.y, hea.u)
  connect(dp.y, floMac.dp_in)
  connect(sou.outlet, hea.inlet)
  connect(hea.outlet, floMac.inlet)
  connect(m_flow.y, sou.m_flow_in)
  connect(sou.p_in, pIn.y)
  connect(TIn.y, sou.T_in)
  connect(C.y, sou.C_in)
  connect(X_w_in.y, sou.X_w_in)
end HeaterFan_noReverseFlow;


Collection of validation models

within Buildings.Fluid.FMI;
package Validation "Collection of validation models"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


within Buildings.Fluid;
package Geothermal "Package with models for geothermal heat exchange"
  extends Modelica.Icons.VariantsPackage;
end Geothermal;


within Buildings.Fluid.Geothermal.Aquifer;
model MultiWell "Model of a single well for aquifer thermal energy storage"
 replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component"
  parameter Integer nVol(min=1)=10 "Number of control volumes used in discretization"
  parameter Modelica.Units.SI.Height h "Aquifer thickness";
  parameter Modelica.Units.SI.Length d "Distance between wells";
  parameter Modelica.Units.SI.Height length
    "Length of one well, used to compute pressure drop";
  parameter Real nPai=1 "Number of paired wells";
  parameter Modelica.Units.SI.Radius rWB=0.1 "Wellbore radius"
  parameter Modelica.Units.SI.Radius rMax=d/2 "Domain radius"
  parameter Real griFac(min=1) = 1.15 "Grid factor for spacing"
  parameter Modelica.Units.SI.Temperature TCol_start=283.15
     "Initial temperature of cold well"
  parameter Modelica.Units.SI.Temperature THot_start=TCol_start
     "Initial temperature of warm well"
  parameter Modelica.Units.SI.Temperature TGroCol=285.15
     "Undisturbed ground temperature (cold well)"
  parameter Modelica.Units.SI.Temperature TGroHot=TGroCol
     "Undisturbed ground temperature (warm well)"
  parameter Buildings.Fluid.Geothermal.Aquifer.Data.Template aquDat
     "Aquifer thermal properties"
  parameter Modelica.Units.SI.MassFlowRate m_flow_nominal "Nominal mass flow rate"
  parameter Modelica.Units.SI.PressureDifference dpAquifer_nominal(displayUnit= "Pa")=
    m_flow_nominal/nPai*Modelica.Constants.g_n/2/Modelica.Constants.pi/h/aquDat.K*log(d/rWB)
     "Pressure drop at nominal mass flow rate in the aquifer"
  final parameter Modelica.Units.SI.PressureDifference dpWell_nominal(
      displayUnit="Pa") = 2*resWelCol.dp_nominal
    "Pressure drop at nominal mass flow rate in the well"
  parameter Modelica.Units.SI.PressureDifference dpExt_nominal(displayUnit="Pa")
    "Pressure drop at nominal mass flow rate in the above-surface system (used to size the head of the well pump)"

  final parameter Modelica.Units.SI.Radius rVol[nVol](
    each final fixed=false)
    "Radius to the center of the i-th domain";

  Modelica.Blocks.Interfaces.RealInput u(
      final min=-1,
      final max=1,
      final unit="1")
      "Pump control input (-1: extract from hot well, +1: extract from cold well, 0: off)"
  Modelica.Blocks.Interfaces.RealOutput PTot(
    final unit="W")
  "Total power consumed by all circulation pumps in the wells"

  Modelica.Fluid.Interfaces.FluidPort_a port_Col(
    redeclare final package Medium = Medium) "Fluid connector"

  Modelica.Fluid.Interfaces.FluidPort_a port_Hot(
    redeclare final package Medium = Medium) "Fluid connector"

  Modelica.Units.SI.Temperature TAquHot[nVol] = heaCapHot.T
    "Temperatures of the hot aquifer";
  Modelica.Units.SI.Temperature TAquCol[nVol] = heaCapCol.T
    "Temperatures of the cold aquifer";

  Movers.Preconfigured.SpeedControlled_y pumCol(
    redeclare final package Medium = Medium,
    final m_flow_nominal=m_flow_nominal/nPai,
    final dp_nominal=resAqu.dpMea_nominal + 2* resWelCol.dp_nominal + dpExt_nominal)
       "Pump to extract from cold well"
  Movers.Preconfigured.SpeedControlled_y pumHot(
    redeclare final package Medium = Medium,
    final m_flow_nominal=pumCol.m_flow_nominal,
    final dp_nominal=pumCol.dp_nominal) "Pump to extract from hot well"

  FixedResistances.PressureDrop resWelCol(
    redeclare final package Medium = Medium,
    m_flow_nominal=m_flow_nominal/nPai,
    final from_dp=false,
    dp_nominal=
        Modelica.Fluid.Pipes.BaseClasses.WallFriction.Detailed.pressureLoss_m_flow(
        m_flow=m_flow_nominal/nPai,
        rho_a=rhoWat,
        rho_b=rhoWat,
        mu_a=mu,
        mu_b=mu,
        length=length,
        diameter=rWB,
        roughness=2.5e-5)) "Pressure drop in the cold well"

  // The resistances on the hot side are set to be the negative of the cold
  // side, as they have the same geometry and flow rate.
  // This reduces the size of the nonlinear system of equations,
  // and leads to fewer equations in the model.
  Movers.BaseClasses.IdealSource resWelHot(
    redeclare final package Medium = Medium,
    final allowFlowReversal=true,
    final control_m_flow=false,
    final control_dp=true,
    final m_flow_small=1E-4*abs(m_flow_nominal))
    "Pressure drop in the warm well"

  BaseClasses.MassFlowRateMultiplier mulCol(
    redeclare final package Medium = Medium,
    k=nPai)
    "Mass flow multiplier for the cold well"
  BaseClasses.MassFlowRateMultiplier mulHot(
    redeclare final package Medium = Medium,
    k=nPai)
    "Mass flow multiplier for the warm well"

  Modelica.Blocks.Math.Add addPum(
    final k1=1,
    final k2=1)
    "Sum of pump electrical power"

  Sensors.RelativePressure dpWelCol(
    redeclare package Medium = Medium)
    "Pressure drop of cold well"

  Airflow.Multizone.Point_m_flow resAqu(
    redeclare final package Medium = Medium,
    m=1,
    final dpMea_nominal=dpAquifer_nominal,
    final mMea_flow_nominal=m_flow_nominal/nPai)
    "Pressure drop in the cold and warm side of the aquifer"
protected
  parameter Modelica.Units.SI.Radius r[nVol + 1](each fixed=false)
    "Radius to the boundary of the i-th domain";
  parameter Modelica.Units.SI.HeatCapacity C[nVol](each fixed=false)
    "Heat capacity of segment";
  parameter Modelica.Units.SI.Volume VWat[nVol](each fixed=false)
    "Volumes of water";
  parameter Modelica.Units.SI.ThermalResistance R[nVol](each fixed=false)
    "Thermal resistances between nodes";
  parameter Real cAqu(fixed=false)
    "Heat capacity normalized with volume for aquifer";
  parameter Real kVol(fixed=false)
    "Heat conductivity normalized with volume";
  parameter Modelica.Units.SI.DynamicViscosity mu=Medium.dynamicViscosity(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water dynamic viscosity";
  parameter Modelica.Units.SI.SpecificHeatCapacity cpWat=Medium.specificHeatCapacityCp(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water specific heat capacity";
  parameter Modelica.Units.SI.Density rhoWat=Medium.density(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water density";
  parameter Modelica.Units.SI.ThermalConductivity kWat=Medium.thermalConductivity(Medium.setState_pTX(
    Medium.p_default,
    Medium.T_default,
    Medium.X_default)) "Water thermal conductivity";

  Buildings.Fluid.MixingVolumes.MixingVolume volCol[nVol](
    redeclare final package Medium = Medium,
    each final T_start=TCol_start,
    each final m_flow_nominal=m_flow_nominal,
    final V=VWat,
    each nPorts=2)
    "Array of fluid volumes representing the fluid flow in the cold side of the aquifer"

  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCapCol[nVol](C=C,
      each T(start=TCol_start, fixed=true))
    "Array of thermal capacitor in the cold side of the aquifer"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor theResCol[nVol](R=R)
    "Array of thermal resistances in the cold side of the aquifer"

  MixingVolumes.MixingVolume volHot[nVol](
    redeclare final package Medium = Medium,
    each T_start=THot_start,
    each m_flow_nominal=m_flow_nominal,
    V=VWat,
    each nPorts=2)
    "Array of fluid volumes representing the fluid flow in the warm side of the aquifer"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heaCapHot[nVol](C=C,
    each T(start=THot_start, fixed=true))
    "Array of thermal capacitor in the warm side of the aquifer"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor theResHot[nVol](R=R)
    "Array of thermal resistances in the warm side of the aquifer"

  Buildings.HeatTransfer.Sources.FixedTemperature groTemCol(
    final T=TGroCol)
    "Boundary condition ground temperature in the cold side of the aquifer"
  Buildings.HeatTransfer.Sources.FixedTemperature groTemHot(
    final T=TGroHot)
    "Boundary condition ground temperature in the warm side of the aquifer"

  Modelica.Blocks.Nonlinear.Limiter limCol(final uMax=1, final uMin=0) "Limiter for pump signal"
  Modelica.Blocks.Math.Gain gaiCon(final k=-1) "Inversion of control signal"
  Modelica.Blocks.Nonlinear.Limiter limHot(final uMax=1, final uMin=0) "Limiter for pump signal"

  Modelica.Blocks.Math.Gain gaiPum(final k=nPai) "Gain used to scale the pump electrical power"
initial equation
  assert(rWB < rMax, "In " + getInstanceName() + ": Require rWB < rMax");
  assert(0 < rWB, "In " + getInstanceName() + ": Require 0 < rWB");
  assert(rMax <= d/2, "In " + getInstanceName() + ": Require rMax <= d/2");

  cAqu=aquDat.dSoi*aquDat.cSoi*(1-aquDat.phi);
  kVol=kWat*aquDat.phi+aquDat.kSoi*(1-aquDat.phi);

  r[1] = rWB;
  for i in 2:nVol+1 loop
    r[i]= r[i-1] + (rMax - rWB)  * (1-griFac)/(1-griFac^(nVol)) * griFac^(i-2);
  end for;
  for i in 1:nVol loop
    rVol[i] = (r[i]+r[i+1])/2;
  end for;
  for i in 1:nVol loop
    C[i] = cAqu*h*3.14*(r[i+1]^2-r[i]^2);
  end for;
  for i in 1:nVol loop
    VWat[i] = aquDat.phi*h*3.14*(r[i+1]^2-r[i]^2);
  end for;

  R[nVol]=Modelica.Math.log(rMax/rVol[nVol])/(2*Modelica.Constants.pi*kVol*h);
  for i in 1:nVol-1 loop
    R[i] = Modelica.Math.log(rVol[i+1]/rVol[i])/(2*Modelica.Constants.pi*kVol*h);
  end for;

equation
  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(volCol[i].ports[2], volCol[i + 1].ports[1]);
    end for;
  end if;
  connect(resAqu.port_b, volCol[nVol].ports[2])
  connect(resAqu.port_a, volHot[nVol].ports[2])
  connect(pumCol.port_a, volCol[1].ports[1])
  connect(pumHot.port_a, volHot[1].ports[1])

  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(volHot[i].ports[2], volHot[i + 1].ports[1]);
    end for;
  end if;

  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(heaCapCol[i + 1].port, theResCol[i].port_b);
    end for;
  end if;

  if nVol > 1 then
    for i in 1:(nVol - 1) loop
      connect(heaCapHot[i + 1].port, theResHot[i].port_b);
    end for;
  end if;

  connect(groTemCol.port, theResCol[nVol].port_b)

  connect(volCol.heatPort, heaCapCol.port)
  connect(theResCol.port_a, volCol.heatPort)
  connect(groTemHot.port, theResHot[nVol].port_b)
  connect(volHot.heatPort, heaCapHot.port)
  connect(theResHot.port_a, volHot.heatPort)
  connect(pumCol.port_b, resWelCol.port_a)
  connect(resWelHot.port_a, pumHot.port_b)
  connect(limCol.y, pumCol.y)
  connect(gaiCon.y, limHot.u)
  connect(limHot.y, pumHot.y)
  connect(gaiCon.u, u)
  connect(limCol.u, u)
  connect(resWelCol.port_b, mulCol.port_a)
  connect(mulCol.port_b, port_Col)
  connect(mulHot.port_b, port_Hot)
  connect(resWelHot.port_b, mulHot.port_a)
  connect(pumCol.P, addPum.u1)
  connect(addPum.y, gaiPum.u)
  connect(pumHot.P, addPum.u2)
  connect(gaiPum.y, PTot)
  connect(dpWelCol.port_a, resWelCol.port_b)
  connect(dpWelCol.port_b, resWelCol.port_a)
  connect(dpWelCol.p_rel, resWelHot.dp_in)
end MultiWell;


within Buildings.Fluid.Geothermal;
package Aquifer "Package with aquifer thermal energy storage models"
  extends Modelica.Icons.Package;
end Aquifer;


within Buildings.Fluid.Geothermal.Aquifer;
package Data "Collection of data records for aquifer thermal energy storage"
  extends Modelica.Icons.MaterialPropertiesPackage;
end Data;


Medium model

within Buildings.Fluid.Geothermal.Aquifer.Examples;
model CoolingOffice
  extends Modelica.Icons.Example;
  package Medium = Buildings.Media.Water "Medium model";

  parameter Modelica.Units.SI.HeatFlowRate QCoo_flow_nominal=30000
    "Cooling power";
  parameter Modelica.Units.SI.TemperatureDifference deltaT=4 "Temperature difference at heat exchanger";
  parameter Modelica.Units.SI.SpecificHeatCapacity cpWat=4186 "Heat capacity of water";
  parameter Modelica.Units.SI.MassFlowRate mWat_flow_nominal=
    QCoo_flow_nominal/(deltaT*cpWat) "Nominal water mass flow rate";

  HeatExchangers.HeaterCooler_u hea(
    redeclare package Medium = Medium,
    m_flow_nominal=mWat_flow_nominal,
    dp_nominal=100,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    Q_flow_nominal=QCoo_flow_nominal) "Heat exchanger"
  Modelica.Blocks.Sources.Constant uPum(k=1) "Pump control signal"
  Modelica.Blocks.Sources.Constant uHea(k=1) "Heat load control signal"
  MultiWell aquWel(
    redeclare package Medium = Medium,
    nVol=80,
    h=20,
    d=1000,
    length=40,
    TCol_start=285.15,
    THot_start=285.15,
    aquDat=Buildings.Fluid.Geothermal.Aquifer.Data.Rock(),
    m_flow_nominal=mWat_flow_nominal,
    dpExt_nominal=5000)
                       "Acquifer well"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Pressure boundary condition"
equation
  connect(uHea.y, hea.u)
  connect(aquWel.port_Hot, hea.port_b)
  connect(aquWel.port_Col, hea.port_a)
  connect(bou.ports[1], hea.port_a)
  connect(uPum.y, aquWel.u)
end CoolingOffice;


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


within Buildings.Fluid.Geothermal.Aquifer.Validation;
model NumberWells
  "Test model for aquifer thermal energy storage with multiple wells"
  extends Modelica.Icons.Example;

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

  model MyWell = MultiWell (
    redeclare package Medium = Buildings.Media.Water,
    nVol=50,
    h=10,
    d=4800,
    length=200,
    THot_start=303.15,
    TGroCol=273.15,
    TGroHot=303.15,
    aquDat=Buildings.Fluid.Geothermal.Aquifer.Data.Rock(),
    m_flow_nominal=0.1,
    dpExt_nominal=0) "Well model";

  MyWell aquWel1(d=4800)
  "ATES with one pair of wells"

  MyWell aquWel2(
    d=4800,
     nPai=2,
     m_flow_nominal=0.2)
    "ATES with two pairs of wells"
  Modelica.Blocks.Sources.Constant uPum(k=1) "Pump control signal"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=2) "Sink"
  Modelica.Blocks.Sources.RealExpression temWel1(
    y=aquWel1.TAquHot[10])
    "Temperature output from aquifer model with one pair of wells"
  Modelica.Blocks.Sources.RealExpression temWel2(
    y=aquWel2.TAquHot[10])
    "Temperature output from aquifer model with two pairs of wells"
  Buildings.Utilities.Diagnostics.CheckEquality cheEqu
    "Assertion that checks for equality of results"
equation
  connect(uPum.y, aquWel1.u)
  connect(aquWel2.u,uPum. y)
  connect(aquWel2.port_Col, aquWel2.port_Hot)
  connect(aquWel1.port_Col, aquWel1.port_Hot)
  connect(bou.ports[1], aquWel1.port_Hot)
  connect(bou.ports[2], aquWel2.port_Hot)
  connect(cheEqu.u1, temWel1.y)
  connect(cheEqu.u2, temWel2.y)
end NumberWells;


within Buildings.Fluid.Geothermal.Aquifer;
package Validation "Validation models for aquifer thermal energy storage"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


within Buildings.Fluid.Geothermal.Aquifer.Validation;
model SimulationTest
  "Test model for aquifer thermal energy storage in comparison with other geothermal simulators"
  extends Modelica.Icons.Example;

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

  MultiWell aquWel(
    redeclare package Medium = Medium,
    nVol=232,
    h=200,
    d=4800,
    length=40,
    griFac=1.1,
    TCol_start=307.15,
    THot_start=393.15,
    TGroCol=307.15,
    TGroHot=393.15,
    aquDat=Buildings.Fluid.Geothermal.Aquifer.Data.Rock(),
    m_flow_nominal=1,
    dpExt_nominal=0) "Aquifer wells"
  Sources.Boundary_pT bou(
    redeclare package Medium = Medium,
    nPorts=1) "Boundary condition for pressure"
  Modelica.Blocks.Sources.CombiTimeTable uPum(table=[0.0,-1; 86400*120,-1; 86400
        *120,0; 86400*180,0; 86400*180,1; 86400*300,1; 86400*300,0])
    "Pump control signal"
equation
  connect(aquWel.port_Col, aquWel.port_Hot)
  connect(bou.ports[1], aquWel.port_Hot)
  connect(uPum.y[1], aquWel.u)
end SimulationTest;


Borefield model containing single U-tube boreholes. This model simulates a borefield containing one or many single U-tube boreholes
using the parameters in the borFieDat record.

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


Package with borefield models

within Buildings.Fluid.Geothermal;
package Borefields "Package with borefield models"
  extends Modelica.Icons.VariantsPackage;
end Borefields;


Borefield model containing double U-tube boreholes. This model simulates a borefield containing one or many double U-tube boreholes
using the parameters in the borFieDat record.

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


Package with type definitions

within Buildings.Fluid.Geothermal.Borefields;
package Types "Package with type definitions"
  extends Modelica.Icons.TypesPackage;

  type BoreholeConfiguration = enumeration(
      SingleUTube
    "Single U-tube configuration",
      DoubleUTubeParallel
    "Double U-tube configuration with pipes connected in parallel",
      DoubleUTubeSeries
    "Double U-tube configuration with pipes connected in series")
  "Enumaration to define the borehole configurations"
end Types;


User's Guide

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

end UsersGuide;


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


within Buildings.Fluid.Geothermal.Borefields.BaseClasses;
partial model PartialBorefield
  "Borefield model using single U-tube borehole heat exchanger configuration.Calculates the average fluid temperature T_fts of the borefield for a given (time dependent) load Q_flow"

  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface(
    final m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal);

  extends Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters(
    final dp_nominal=borFieDat.conDat.dp_nominal,
    final computeFlowResistance=(borFieDat.conDat.dp_nominal > Modelica.Constants.eps));

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

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

  // Assumptions
  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"

  // Simulation parameters
  parameter Modelica.Units.SI.Time tLoaAgg=300
    "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";
  parameter Boolean forceGFunCalc = false
    "Set to true to force the thermal response to be calculated at the start instead of checking whether this has been pre-computed"
  parameter Integer nSegGFun(min=1)=12 "Number of segments to use in the calculation of the g-function"
  parameter Integer nClu(min=1)=5
    "Number of borehole clusters to use in the calculation of the g-function"

  // General parameters of borefield
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Template borFieDat "Borefield data"

  // 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"

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

  Buildings.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.GroundTemperatureResponse groTemRes(
    final tLoaAgg=tLoaAgg,
    final nCel=nCel,
    final nSeg=nSegGFun,
    final nClu=nClu,
    final borFieDat=borFieDat,
    final forceGFunCalc=forceGFunCalc)
    "Ground temperature response"

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

protected
  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";

  Buildings.Fluid.BaseClasses.MassFlowRateMultiplier masFloDiv(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_input=false,
    final k=1/borFieDat.conDat.nBor)
                                   "Division of flow rate"

  Buildings.Fluid.BaseClasses.MassFlowRateMultiplier masFloMul(
    redeclare final package Medium = Medium,
    final allowFlowReversal=allowFlowReversal,
    final use_input=false,
    final k=borFieDat.conDat.nBor) "Mass flow multiplier"

  Modelica.Blocks.Math.Gain gaiQ_flow(k=borFieDat.conDat.nBor)
    "Gain to multiply the heat extracted by one borehole by the number of boreholes"
  Buildings.Utilities.Math.Average AveTBor(nin=nSeg)
    "Average temperature of all the borehole segments"

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

  Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor QBorHol[nSeg]
    "Heat flow rate of all segments of the borehole"

  Buildings.HeatTransfer.Sources.PrescribedTemperature TemBorWal[nSeg]
    "Borewall temperature"

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

  Modelica.Blocks.Math.Sum QTotSeg_flow(
    final nin=nSeg,
    final k = ones(nSeg))
    "Total heat flow rate for all segments of this borehole"

  Modelica.Blocks.Routing.Replicator repDelTBor(final nout=nSeg)
    "Signal replicator for temperature difference of the borehole"

equation
  connect(masFloMul.port_b, port_b)
  connect(masFloDiv.port_a, port_a)
  connect(masFloDiv.port_b, borHol.port_a)
  connect(borHol.port_b, masFloMul.port_a)
  connect(QBorHol.port_a, borHol.port_wall)
  connect(QBorHol.Q_flow, QTotSeg_flow.u)
  connect(groTemRes.delTBor, repDelTBor.u)
  connect(TSoiDis.u1, repDelTBor.y)
  connect(TSoiDis.u2, TSoiUnd.y)
  connect(QTotSeg_flow.y, gaiQ_flow.u)
  connect(gaiQ_flow.y, groTemRes.QBor_flow)
  connect(TSoiDis.y, TemBorWal.T)
  connect(QBorHol.port_b, TemBorWal.port)
  connect(TSoiDis.y, AveTBor.u)
  connect(AveTBor.y, TBorAve)
end PartialBorefield;


Single U-tube borehole heat exchanger

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes;
model OneUTube "Single U-tube borehole heat exchanger"
  extends Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialBorehole;

  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalHEXOneUTube
    intHex[nSeg](
    redeclare each final package Medium = Medium,
    each final hSeg=borFieDat.conDat.hBor/nSeg,
    each final from_dp1=from_dp,
    each final from_dp2=from_dp,
    each final linearizeFlowResistance1=linearizeFlowResistance,
    each final linearizeFlowResistance2=linearizeFlowResistance,
    each final deltaM1=deltaM,
    each final deltaM2=deltaM,
    each final energyDynamics=energyDynamics,
    each final mSenFac=mSenFac,
    final dp1_nominal={if i == 1 then dp_nominal else 0 for i in 1:nSeg},
    each final dp2_nominal=0,
    each final m1_flow_nominal=m_flow_nominal,
    each final m2_flow_nominal=m_flow_nominal,
    each final borFieDat=borFieDat,
    each final allowFlowReversal1=allowFlowReversal,
    each final allowFlowReversal2=allowFlowReversal,
    each final show_T=show_T,
    each final p1_start=p_start,
    each final p2_start=p_start,
    final TFlu_start=TFlu_start,
    final TGro_start=TGro_start) "Borehole segments"

equation
  connect(port_a, intHex[1].port_a1)
  connect(port_b, intHex[1].port_b2)
  connect(intHex[nSeg].port_b1, intHex[nSeg].port_a2)
  for i in 1:nSeg - 1 loop
    connect(intHex[i].port_b1, intHex[i + 1].port_a1)
    connect(intHex[i].port_a2, intHex[i + 1].port_b2)
  end for;
  connect(intHex.port_wall, port_wall)
end OneUTube;


Package with borehole heat exchangers

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


Double U-tube borehole heat exchanger

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes;
model TwoUTube "Double U-tube borehole heat exchanger"
  extends Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialBorehole;

  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalHEXTwoUTube
    intHex[nSeg](
    redeclare each final package Medium = Medium,
    each final borFieDat=borFieDat,
    each final hSeg=borFieDat.conDat.hBor/nSeg,
    final dp1_nominal={if i == 1 and borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then dp_nominal elseif i == 1 and borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeSeries
         then dp_nominal/2 else 0 for i in 1:nSeg},
    final dp3_nominal={if i == 1 and borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then dp_nominal elseif i == 1 and borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeSeries
         then dp_nominal/2 else 0 for i in 1:nSeg},
    each final dp2_nominal=0,
    each final dp4_nominal=0,
    each final show_T=show_T,
    each final energyDynamics=energyDynamics,
    each final m1_flow_nominal=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then m_flow_nominal/2 else m_flow_nominal,
    each final m2_flow_nominal=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then m_flow_nominal/2 else m_flow_nominal,
    each final m3_flow_nominal=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then m_flow_nominal/2 else m_flow_nominal,
    each final m4_flow_nominal=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then m_flow_nominal/2 else m_flow_nominal,
    each final m1_flow_small=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then borFieDat.conDat.mBor_flow_small/2 else borFieDat.conDat.mBor_flow_small,
    each final m2_flow_small=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then borFieDat.conDat.mBor_flow_small/2 else borFieDat.conDat.mBor_flow_small,
    each final m3_flow_small=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then borFieDat.conDat.mBor_flow_small/2 else borFieDat.conDat.mBor_flow_small,
    each final m4_flow_small=if borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
         then borFieDat.conDat.mBor_flow_small/2 else borFieDat.conDat.mBor_flow_small,
    each final mSenFac=mSenFac,
    each final allowFlowReversal1=allowFlowReversal,
    each final allowFlowReversal2=allowFlowReversal,
    each final allowFlowReversal3=allowFlowReversal,
    each final allowFlowReversal4=allowFlowReversal,
    each final from_dp1=from_dp,
    each final linearizeFlowResistance1=linearizeFlowResistance,
    each final deltaM1=deltaM,
    each final from_dp2=from_dp,
    each final linearizeFlowResistance2=linearizeFlowResistance,
    each final deltaM2=deltaM,
    each final from_dp3=from_dp,
    each final linearizeFlowResistance3=linearizeFlowResistance,
    each final deltaM3=deltaM,
    each final from_dp4=from_dp,
    each final linearizeFlowResistance4=linearizeFlowResistance,
    each final deltaM4=deltaM,
    each final p1_start=p_start,
    each final p2_start=p_start,
    each final p3_start=p_start,
    each final p4_start=p_start,
    final TFlu_start=TFlu_start,
    final TGro_start=TGro_start) "Discretized borehole segments"

equation
  // Couple borehole port_a and port_b to first borehole segment.
  connect(port_a, intHex[1].port_a1)
  connect(port_b, intHex[1].port_b4)
  if borFieDat.conDat.borCon == Types.BoreholeConfiguration.DoubleUTubeParallel then
    // 2U-tube in parallel: couple both U-tube to each other.
    connect(port_a, intHex[1].port_a3)
    connect(port_b, intHex[1].port_b2)
  elseif borFieDat.conDat.borCon == Types.BoreholeConfiguration.DoubleUTubeSeries then
    // 2U-tube in serie: couple both U-tube to each other.
    connect(intHex[1].port_b2, intHex[1].port_a3)
  end if;

  // Couple each layer to the next one
  for i in 1:nSeg - 1 loop
    connect(intHex[i].port_b1, intHex[i + 1].port_a1)
    connect(intHex[i].port_a2, intHex[i + 1].port_b2)
    connect(intHex[i].port_b3, intHex[i + 1].port_a3)
    connect(intHex[i].port_a4, intHex[i + 1].port_b4)
  end for;

  // Close U-tube at bottom layer
  connect(intHex[nSeg].port_b1, intHex[nSeg].port_a2)
  connect(intHex[nSeg].port_b3, intHex[nSeg].port_a4)

  connect(intHex.port_wall, port_wall)
end TwoUTube;


within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
model InternalHEXOneUTube
  "Internal heat exchanger of a borehole for a single U-tube configuration"
  extends
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialInternalHEX;
  extends Buildings.Fluid.Interfaces.FourPortHeatMassExchanger(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    T1_start=TFlu_start,
    T2_start=TFlu_start,
    final tau1=VTubSeg*rho1_nominal/m1_flow_nominal,
    final tau2=VTubSeg*rho2_nominal/m2_flow_nominal,
    redeclare final Buildings.Fluid.MixingVolumes.MixingVolume vol1(
      final energyDynamics=energyDynamics,
      final massDynamics=energyDynamics,
      final prescribedHeatFlowRate=false,
      final m_flow_small=m1_flow_small,
      final V=VTubSeg,
      final mSenFac=mSenFac),
    redeclare final Buildings.Fluid.MixingVolumes.MixingVolume vol2(
      final energyDynamics=energyDynamics,
      final massDynamics=energyDynamics,
      final prescribedHeatFlowRate=false,
      final m_flow_small=m2_flow_small,
      final V=VTubSeg,
      final mSenFac=mSenFac));

protected
  parameter Real Rgg_val(fixed=false) "Thermal resistance between the two grout zones";

public
  Modelica.Blocks.Sources.RealExpression RVol1(y=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.convectionResistanceCircularPipe(
      hSeg=hSeg,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      kMed=kMed,
      muMed=muMed,
      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=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.convectionResistanceCircularPipe(
      hSeg=hSeg,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      kMed=kMed,
      muMed=muMed,
      cpMed=cpMed,
      m_flow=m2_flow,
      m_flow_nominal=m2_flow_nominal))
    "Convective and thermal resistance at fluid 2"

  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalResistancesOneUTube
    intResUTub(
      hSeg=hSeg,
      energyDynamics=energyDynamics,
      Rgb_val=Rgb_val,
      Rgg_val=Rgg_val,
      RCondGro_val=RCondGro_val,
      borFieDat=borFieDat,
      T_start=TGro_start)
    "Internal resistances for a single U-tube configuration"
  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv2
    "Pipe convective resistance"
  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv1
    "Pipe convective resistance"
initial equation
  (x, Rgb_val, Rgg_val, RCondGro_val) =
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.internalResistancesOneUTube(
      hSeg=hSeg,
      rBor=borFieDat.conDat.rBor,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      sha=borFieDat.conDat.xC,
      kFil=borFieDat.filDat.kFil,
      kSoi=borFieDat.soiDat.kSoi,
      kTub=borFieDat.conDat.kTub,
      use_Rb=borFieDat.conDat.use_Rb,
      Rb=borFieDat.conDat.Rb,
      kMed=kMed,
      muMed=muMed,
      cpMed=cpMed,
      m_flow_nominal=m1_flow_nominal,
      instanceName=getInstanceName());

equation
    assert(borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.SingleUTube,
  "This model should be used for single U-type borefield, not double U-type.
  Check that the conDat record has been correctly parametrized");
  connect(RVol2.y, RConv2.Rc)
  connect(RVol1.y, RConv1.Rc)
  connect(vol1.heatPort, RConv1.fluid)
  connect(RConv1.solid, intResUTub.port_1)
  connect(RConv2.fluid, vol2.heatPort)
  connect(RConv2.solid, intResUTub.port_2)
  connect(intResUTub.port_wall, port_wall)
end InternalHEXOneUTube;


within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
model InternalHEXTwoUTube
  "Internal heat exchanger of a borehole for a double U-tube configuration. In loop 1, fluid 1 streams from a1 to b1 and comes back from a2 to b2. In loop 2: fluid 2 streams from a3 to b3 and comes back from a4 to b4."

  extends
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialInternalHEX;
  extends Buildings.Fluid.Interfaces.EightPortHeatMassExchanger(
    redeclare final package Medium1 = Medium,
    redeclare final package Medium2 = Medium,
    redeclare final package Medium3 = Medium,
    redeclare final package Medium4 = Medium,
    T1_start=TFlu_start,
    T2_start=TFlu_start,
    T3_start=TFlu_start,
    T4_start=TFlu_start,
    final tau1=VTubSeg*rho1_nominal/m1_flow_nominal,
    final tau2=VTubSeg*rho2_nominal/m2_flow_nominal,
    final tau3=VTubSeg*rho3_nominal/m3_flow_nominal,
    final tau4=VTubSeg*rho4_nominal/m4_flow_nominal,
    vol1(
      final energyDynamics=energyDynamics,
      final massDynamics=energyDynamics,
      final prescribedHeatFlowRate=false,
      final allowFlowReversal=allowFlowReversal1,
      final m_flow_small=m1_flow_small,
      final V=VTubSeg,
      final mSenFac=mSenFac),
    vol2(
      final energyDynamics=energyDynamics,
      final massDynamics=energyDynamics,
      final prescribedHeatFlowRate=false,
      final m_flow_small=m2_flow_small,
      final V=VTubSeg,
      final mSenFac=mSenFac),
    vol3(
      final energyDynamics=energyDynamics,
      final massDynamics=energyDynamics,
      final prescribedHeatFlowRate=false,
      final allowFlowReversal=allowFlowReversal3,
      final m_flow_small=m3_flow_small,
      final V=VTubSeg,
      final mSenFac=mSenFac),
    vol4(
      final energyDynamics=energyDynamics,
      final massDynamics=energyDynamics,
      final prescribedHeatFlowRate=false,
      final m_flow_small=m4_flow_small,
      final V=VTubSeg,
      final mSenFac=mSenFac));

  Modelica.Blocks.Sources.RealExpression RVol1(y=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.convectionResistanceCircularPipe(
      hSeg=hSeg,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      kMed=kMed,
      muMed=muMed,
      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=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.convectionResistanceCircularPipe(
      hSeg=hSeg,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      kMed=kMed,
      muMed=muMed,
      cpMed=cpMed,
      m_flow=m2_flow,
      m_flow_nominal=m2_flow_nominal))
    "Convective and thermal resistance at fluid 2"
  Modelica.Blocks.Sources.RealExpression RVol3(y=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.convectionResistanceCircularPipe(
      hSeg=hSeg,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      kMed=kMed,
      muMed=muMed,
      cpMed=cpMed,
      m_flow=m3_flow,
      m_flow_nominal=m3_flow_nominal))
    "Convective and thermal resistance at fluid 1"

  Modelica.Blocks.Sources.RealExpression RVol4(y=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.convectionResistanceCircularPipe(
      hSeg=hSeg,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      kMed=kMed,
      muMed=muMed,
      cpMed=cpMed,
      m_flow=m1_flow,
      m_flow_nominal=m4_flow_nominal))
    "Convective and thermal resistance at fluid 1"
  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalResistancesTwoUTube intRes2UTub(
    hSeg=hSeg,
    borFieDat=borFieDat,
    Rgb_val=Rgb_val,
    Rgg1_val=Rgg1_val,
    Rgg2_val=Rgg2_val,
    RCondGro_val=RCondGro_val,
    energyDynamics=energyDynamics,
    T_start=TGro_start)
                   "Internal resistances for a double U-tube configuration"
  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv1
    "Pipe convective resistance"

  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv2
    "Pipe convective resistance"
  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv3
    "Pipe convective resistance"
  Modelica.Thermal.HeatTransfer.Components.ConvectiveResistor RConv4
    "Pipe convective resistance"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_wall

protected
  parameter Real Rgg1_val(fixed=false);
  parameter Real Rgg2_val(fixed=false);

initial equation
  (x,Rgb_val,Rgg1_val,Rgg2_val,RCondGro_val) =
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.internalResistancesTwoUTube(
      hSeg=hSeg,
      rBor=borFieDat.conDat.rBor,
      rTub=borFieDat.conDat.rTub,
      eTub=borFieDat.conDat.eTub,
      sha=borFieDat.conDat.xC,
      kFil=borFieDat.filDat.kFil,
      kSoi=borFieDat.soiDat.kSoi,
      kTub=borFieDat.conDat.kTub,
      use_Rb=borFieDat.conDat.use_Rb,
      Rb=borFieDat.conDat.Rb,
      kMed=kMed,
      muMed=muMed,
      cpMed=cpMed,
      m_flow_nominal=m1_flow_nominal,
      instanceName=getInstanceName());

equation
  assert(borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel
     or borFieDat.conDat.borCon == Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeSeries,
    "This model should be used for double U-type borefield, not single U-type.
  Check that the conDat record has been correctly parametrized");
  connect(RVol1.y, RConv1.Rc)

  connect(RConv1.fluid, vol1.heatPort)

  connect(RConv1.solid, intRes2UTub.port_1)
  connect(RConv2.fluid, vol2.heatPort)
  connect(RConv2.solid, intRes2UTub.port_2)
  connect(RConv3.fluid, vol3.heatPort)
  connect(RConv3.solid, intRes2UTub.port_3)
  connect(RConv4.fluid, vol4.heatPort)
  connect(RConv4.solid, intRes2UTub.port_4)
  connect(RVol4.y, RConv4.Rc)
  connect(RVol3.y, RConv3.Rc)
  connect(RVol2.y, RConv2.Rc)
  connect(intRes2UTub.port_wall, port_wall)
end InternalHEXTwoUTube;


Internal resistance model for single U-tube borehole segments.. This model simulates the internal thermal resistance network of a borehole segment in
the case of a single U-tube borehole using the method of Bauer et al. (2011)
and computing explicitely the fluid-to-ground thermal resistance
Rb and the
grout-to-grout resistance
Ra as defined by Claesson and Hellstrom (2011)
using the multipole method.

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
model InternalResistancesOneUTube
  "Internal resistance model for single U-tube borehole segments."
  extends
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialInternalResistances;

  parameter Modelica.Units.SI.ThermalResistance Rgg_val
    "Thermal resistance between the two grout zones";
  parameter Modelica.Units.SI.HeatCapacity Co_fil=borFieDat.filDat.dFil*
      borFieDat.filDat.cFil*hSeg*Modelica.Constants.pi*(borFieDat.conDat.rBor^2
       - 2*borFieDat.conDat.rTub^2)
    "Heat capacity of the whole filling material";

  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg1(R=RCondGro_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb1(R=Rgb_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil1(
    C=Co_fil/2,
    T(start=T_start, fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
    der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)))
    if not borFieDat.filDat.steadyState
    "Heat capacity of the filling material"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil2(
    C=Co_fil/2,
    T(start=T_start, fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
    der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)))
    if not borFieDat.filDat.steadyState
    "Heat capacity of the filling material"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg(R=Rgg_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg2(R=RCondGro_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb2(R=Rgb_val)
    "Grout thermal resistance"

equation
  connect(port_1, Rpg1.port_a)
  connect(Rpg1.port_b, Rgb1.port_a)
  connect(Rgb1.port_b, port_wall)
  connect(capFil1.port, Rgb1.port_a)
  connect(Rgg.port_a, Rgb1.port_a)
  connect(port_2, Rpg2.port_b)
  connect(Rgg.port_b, Rpg2.port_a)
  connect(Rgb2.port_b, Rpg2.port_a)
  connect(Rgb2.port_a, port_wall)
  connect(capFil2.port, Rgg.port_b)
end InternalResistancesOneUTube;


Internal resistance model for double U-tube borehole segments.. This model simulates the internal thermal resistance network of a borehole segment in
the case of a double U-tube borehole using the method of Bauer et al. (2011)
and computing explicitely the fluid-to-ground thermal resistance
Rb and the
grout-to-grout resistance
Ra as defined by Claesson and Hellstrom (2011)
using the multipole method.

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
model InternalResistancesTwoUTube
  "Internal resistance model for double U-tube borehole segments."
  extends
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialInternalResistances;

  parameter Modelica.Units.SI.ThermalResistance Rgg1_val
    "Thermal resistance between two neightbouring grout capacities, as defined by Bauer et al (2010)";
  parameter Modelica.Units.SI.ThermalResistance Rgg2_val
    "Thermal resistance between two  grout capacities opposite to each other, as defined by Bauer et al (2010)";
  parameter Modelica.Units.SI.HeatCapacity Co_fil=
     if borFieDat.filDat.steadyState then 0
     else borFieDat.filDat.dFil*borFieDat.filDat.cFil*hSeg*Modelica.Constants.pi*
          (borFieDat.conDat.rBor^2 - 4*borFieDat.conDat.rTub^2)
    "Heat capacity of the whole filling material";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_3
    "Thermal connection for borehole wall"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_4
    "Thermal connection for borehole wall"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg1(R=RCondGro_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb1(R=Rgb_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg14(R=Rgg1_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg21(R=Rgg2_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg11(R=Rgg1_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg2(R=RCondGro_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg12(R=Rgg1_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb2(R=Rgb_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb3(R=Rgb_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgb4(R=Rgb_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg4(R=RCondGro_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg13(R=Rgg2_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rpg3(R=RCondGro_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.ThermalResistor Rgg22(R=Rgg1_val)
    "Grout thermal resistance"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil1(T(start=
          T_start, fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
      der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)),
    C=Co_fil/4)  if not borFieDat.filDat.steadyState "Heat capacity of the filling material"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil2(T(start=
          T_start, fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
      der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)),
    C=Co_fil/4)  if not borFieDat.filDat.steadyState "Heat capacity of the filling material"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil3(T(start=
          T_start, fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
      der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)),
    C=Co_fil/4)  if not borFieDat.filDat.steadyState "Heat capacity of the filling material"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor capFil4(T(start=
          T_start, fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.FixedInitial)),
      der_T(fixed=(energyDynamics == Modelica.Fluid.Types.Dynamics.SteadyStateInitial)),
    C=Co_fil/4)  if not borFieDat.filDat.steadyState "Heat capacity of the filling material"
equation
  connect(Rpg1.port_a, port_1)
  connect(Rpg1.port_b, Rgb1.port_a)
  connect(Rgb1.port_b, port_wall)
  connect(port_wall, Rgb3.port_b)
  connect(Rpg3.port_a, port_3)
  connect(Rpg2.port_a, port_2)
  connect(Rgb2.port_a, Rpg2.port_b)
  connect(port_wall, Rgb2.port_b)
  connect(port_wall, Rgb4.port_b)
  connect(Rgb4.port_a, Rpg4.port_b)
  connect(Rpg4.port_a, port_4)
  connect(Rgg14.port_b, Rpg4.port_b)
  connect(Rgg13.port_a, Rpg4.port_b)
  connect(Rgg13.port_b, Rpg3.port_b)
  connect(Rgg13.port_b, Rgb3.port_a)
  connect(Rgg13.port_b, Rgg11.port_b)
  connect(Rgg13.port_b, Rgg12.port_b)
  connect(Rgb2.port_a, Rgg12.port_a)
  connect(Rgg21.port_a, Rpg2.port_b)
  connect(Rpg1.port_b, Rgg11.port_a)
  connect(Rpg1.port_b, Rgg21.port_b)
  connect(Rgg14.port_a, Rgb1.port_a)
  connect(Rgg22.port_a, Rpg4.port_b)
  connect(Rgg22.port_b, Rpg2.port_b)
  connect(capFil1.port, Rgb1.port_a)
  connect(capFil2.port, Rpg2.port_b)
  connect(capFil3.port, Rpg3.port_b)
  connect(capFil4.port, Rpg4.port_b)
end InternalResistancesTwoUTube;


Base classes for Borehole

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


within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
partial model PartialBorehole
  "Partial model to implement multi-segment boreholes"
  extends Buildings.Fluid.Interfaces.PartialTwoPortInterface;

  extends Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters(
    computeFlowResistance=dp_nominal > Modelica.Constants.eps);

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

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

  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";
  parameter Modelica.Units.SI.Temperature TGro_start[nSeg]
    "Start value of grout temperature"

  parameter Modelica.Units.SI.Temperature TFlu_start[nSeg]=TGro_start
    "Start value of fluid temperature"

  // Assumptions
  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"

  parameter Data.Borefield.Template borFieDat "Borefield parameters"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_wall[nSeg]
    "Thermal connection for borehole wall"
end PartialBorehole;


within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
partial model PartialInternalHEX
  "Partial model to implement the internal heat exchanger of a borehole segment"
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Template
    borFieDat "Borefield parameters"
  replaceable package Medium =
    Modelica.Media.Interfaces.PartialMedium "Medium"
  constant Real mSenFac=1
    "Factor for scaling the sensible thermal mass of the volume";

  parameter Modelica.Units.SI.Length hSeg
    "Length of the internal heat exchanger";
  parameter Modelica.Units.SI.Volume VTubSeg=hSeg*Modelica.Constants.pi*(
      borFieDat.conDat.rTub - borFieDat.conDat.eTub)^2
    "Fluid volume in each tube";
  parameter Modelica.Units.SI.Temperature TFlu_start
    "Start value of fluid temperature"
  parameter Modelica.Units.SI.Temperature TGro_start
    "Start value of grout temperature"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_wall
    "Thermal connection for borehole wall"
protected
  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 muMed=Medium.dynamicViscosity(
      Medium.setState_pTX(
      Medium.p_default,
      Medium.T_default,
      Medium.X_default)) "Dynamic viscosity of the fluid";
  parameter Real Rgb_val(fixed=false)
    "Thermal resistance between grout zone and borehole wall";
  parameter Real RCondGro_val(fixed=false)
    "Thermal resistance between: pipe wall to capacity in grout";
  parameter Real x(fixed=false) "Capacity location";
initial equation
  assert(borFieDat.conDat.rBor > borFieDat.conDat.xC + borFieDat.conDat.rTub and
         0 < borFieDat.conDat.xC - borFieDat.conDat.rTub,
         "The borehole geometry is not physical. Check the borefield data record
         to ensure that the shank spacing is larger than the outer tube radius
         and that the borehole radius is sufficiently large.");
end PartialInternalHEX;


Partial model to implement borehole segment internal resistance models. Partial model to implement the inner resistance network of a borehole segment.

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
partial model PartialInternalResistances
  "Partial model to implement borehole segment internal resistance models"
  parameter Modelica.Units.SI.Length hSeg
    "Length of the internal heat exchanger";
  parameter Modelica.Units.SI.Temperature T_start
    "Initial temperature of the filling material";
  parameter Data.Borefield.Template borFieDat "Borefield data"
  parameter Modelica.Units.SI.ThermalResistance Rgb_val
    "Thermal resistance between grout zone and borehole wall";
  parameter Modelica.Units.SI.ThermalResistance RCondGro_val
    "Thermal resistance between: pipe wall to capacity in grout";
  parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial
    "Type of energy balance: dynamic (3 initialization options) or steady state"

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_1
    "Thermal connection for pipe 1"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_wall
    "Thermal connection for pipe 2"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_2
    "Thermal connection for borehole wall"
end PartialInternalResistances;


within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Examples;
model InternalHEXOneUTube
  "Comparison of the effective borehole thermal resistance  from the thermal network of Bauer et al. with the resistance calculated by singleUTubeResistances (ref)"
  extends Modelica.Icons.Example;

  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";
  parameter Modelica.Units.SI.Length hSeg=borFieDat.conDat.hBor/nSeg
    "Length of the internal heat exchanger";

  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;
  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalHEXOneUTube
    intHex(
    redeclare package Medium = Medium,
    hSeg=hSeg,
    dp1_nominal=10,
    dp2_nominal=10,
    borFieDat=borFieDat,
    m1_flow_nominal=borFieDat.conDat.mBor_flow_nominal,
    m2_flow_nominal=borFieDat.conDat.mBor_flow_nominal,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TFlu_start=285.15,
    TGro_start=285.15)

  Buildings.HeatTransfer.Sources.FixedTemperature fixedTemperature(T=273.15
         + 12)
  Buildings.Fluid.Sources.MassFlowSource_T boundary(nPorts=1,
    redeclare package Medium = Medium,
    m_flow=borFieDat.conDat.mBor_flow_nominal,
    T=293.15)
  Buildings.Fluid.Sources.MassFlowSource_T boundary1(nPorts=1,
    redeclare package Medium = Medium,
    m_flow=borFieDat.conDat.mBor_flow_nominal,
    T=293.15)
  Buildings.Fluid.Sources.Boundary_pT bou(nPorts=2, redeclare package Medium = Medium)
  Real Rb_sim(unit="(m.K)/W") = ((senTem.T + senTem1.T)/2 - intHex.port_wall.T)/max(-intHex.port_wall.Q_flow / hSeg,1);
  Buildings.Fluid.Sensors.TemperatureTwoPort senTem(redeclare package Medium =
        Medium, m_flow_nominal=borFieDat.conDat.mBor_flow_nominal)
  Buildings.Fluid.Sensors.TemperatureTwoPort senTem1(redeclare package Medium =
        Medium, m_flow_nominal=borFieDat.conDat.mBor_flow_nominal)
  Modelica.Blocks.Sources.RealExpression realExpression(y=Rb_sim)
  Modelica.Blocks.Sources.Constant Rb_ref(k=0.082399)
  Modelica.Blocks.Math.Add error(k2=-1)
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Example
    borFieDat(conDat=
        Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Example(
        use_Rb=false))
    "Borefield data"
equation

  connect(boundary1.ports[1], intHex.port_a2)
  connect(boundary.ports[1], intHex.port_a1)
  connect(bou.ports[1], senTem.port_b)
  connect(senTem.port_a, intHex.port_b1)
  connect(senTem1.port_a, bou.ports[2])
  connect(senTem1.port_b, intHex.port_b2)
  connect(realExpression.y, error.u1)
  connect(Rb_ref.y, error.u2)
  connect(fixedTemperature.port, intHex.port_wall)
end InternalHEXOneUTube;


within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Examples;
model InternalHEXTwoUTube
  "Comparison of the effective borehole thermal resistance from the thermal network of Bauer et al. with the resistance calculated by doubleUTubeResistances (ref)"
  extends Modelica.Icons.Example;

  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";
  parameter Modelica.Units.SI.Length hSeg=borFieDat.conDat.hBor/nSeg
    "Length of the internal heat exchanger";

  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;
  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalHEXTwoUTube
    intHex(
    TFlu_start(displayUnit="K") = 285.15,
    redeclare package Medium = Medium,
    hSeg=hSeg,
    dp1_nominal=10,
    dp2_nominal=10,
    dp3_nominal=10,
    dp4_nominal=10,
    borFieDat=borFieDat,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TGro_start(displayUnit="K") = 285.15,
    m1_flow_nominal=borFieDat.conDat.mBor_flow_nominal/2,
    m2_flow_nominal=borFieDat.conDat.mBor_flow_nominal/2,
    m3_flow_nominal=borFieDat.conDat.mBor_flow_nominal/2,
    m4_flow_nominal=borFieDat.conDat.mBor_flow_nominal/2)

  Buildings.HeatTransfer.Sources.FixedTemperature fixedTemperature(T=273.15
         + 12)
  Buildings.Fluid.Sources.MassFlowSource_T boundary(nPorts=2,
    redeclare package Medium = Medium,
    m_flow=borFieDat.conDat.mBor_flow_nominal,
    T=293.15)
  Buildings.Fluid.Sources.MassFlowSource_T boundary1(nPorts=2,
    redeclare package Medium = Medium,
    m_flow=borFieDat.conDat.mBor_flow_nominal,
    T=288.15)
  Buildings.Fluid.Sources.Boundary_pT bou(nPorts=4, redeclare package Medium = Medium)
  Real Rb_sim(unit="(m.K)/W") = ((senTem.T + senTem1.T + senTem2.T + senTem3.T)/4 - intHex.port_wall.T)/max(-intHex.port_wall.Q_flow / hSeg,1);
  Buildings.Fluid.Sensors.TemperatureTwoPort senTem(redeclare package Medium =
        Medium, m_flow_nominal=borFieDat.conDat.mBor_flow_nominal)
  Buildings.Fluid.Sensors.TemperatureTwoPort senTem1(redeclare package Medium =
        Medium, m_flow_nominal=borFieDat.conDat.mBor_flow_nominal)
  Modelica.Blocks.Sources.RealExpression realExpression(y=Rb_sim)
  Modelica.Blocks.Sources.Constant Rb_ref(k=0.0443802)
  Modelica.Blocks.Math.Add error(k2=-1)
  Buildings.Fluid.Sensors.TemperatureTwoPort senTem2(redeclare package Medium =
        Medium, m_flow_nominal=borFieDat.conDat.mBor_flow_nominal)
  Buildings.Fluid.Sensors.TemperatureTwoPort senTem3(redeclare package Medium =
        Medium, m_flow_nominal=borFieDat.conDat.mBor_flow_nominal)
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Example
    borFieDat(conDat=
        Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Example(
        borCon=Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel,
        use_Rb=false))
    "Borefield data"
equation

  connect(fixedTemperature.port, intHex.port_wall)
  connect(boundary.ports[1], intHex.port_a1)
  connect(bou.ports[1], senTem.port_b)
  connect(senTem.port_a, intHex.port_b1)
  connect(realExpression.y, error.u1)
  connect(Rb_ref.y, error.u2)

  connect(boundary.ports[2], intHex.port_a3)
  connect(boundary1.ports[1], intHex.port_a4)
  connect(boundary1.ports[2], intHex.port_a2)
  connect(intHex.port_b2, senTem1.port_a)
  connect(senTem1.port_b, bou.ports[2])
  connect(intHex.port_b4, senTem2.port_a)
  connect(senTem2.port_b, bou.ports[3])
  connect(intHex.port_b3, senTem3.port_a)
  connect(senTem3.port_b, bou.ports[4])
end InternalHEXTwoUTube;


Validation of InternalResistancesOneUTube.

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Examples;
model InternalResistancesOneUTube "Validation of InternalResistancesOneUTube"
  extends Modelica.Icons.Example;

  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";
  parameter Modelica.Units.SI.Length hSeg=borFieDat.conDat.hBor/nSeg
    "Length of the internal heat exchanger";
  parameter Modelica.Units.SI.ThermalResistance Rgb_val=0.0430511
    "Grout node to borehole wall thermal resistance";
  parameter Modelica.Units.SI.ThermalResistance Rgg_val=0.00605573
    "Grout node to grout node thermal resistance";
  parameter Modelica.Units.SI.ThermalResistance RCondGro_val=0.14285
    "Pipe to grout node thermal resistance";
  parameter Modelica.Units.SI.Temperature T_start=298.15 "Initial temperature";

  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalResistancesOneUTube
    intRes1UTub(
    hSeg=hSeg,
    T_start=T_start,
    borFieDat=borFieDat,
    Rgb_val=Rgb_val,
    RCondGro_val=RCondGro_val,
    Rgg_val=Rgg_val,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
                     "Thermal resistance and capacitances of the borehole"
  Buildings.HeatTransfer.Sources.FixedTemperature TWal(T=T_start)
    "Borehole wall temperature"
  Buildings.HeatTransfer.Sources.FixedTemperature TPip2(T=T_start + 15)
    "Wall temperature of pipe 2"
  Buildings.HeatTransfer.Sources.PrescribedTemperature TPip1
    "Wall temperature of pipe 1"
  Modelica.Blocks.Sources.Ramp T_ramp(
    height=20,
    duration=600,
    offset=T_start)
                   "Temperature ramp of pipe 1"
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Example
    borFieDat(conDat=
        Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Example(
        use_Rb=false))
    "Borefield data"
equation

  connect(TWal.port, intRes1UTub.port_wall)
  connect(TPip2.port, intRes1UTub.port_2)
  connect(TPip1.port, intRes1UTub.port_1)
  connect(T_ramp.y, TPip1.T)
end InternalResistancesOneUTube;


Validation of InternalResistancesTwoUTube.

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Examples;
model InternalResistancesTwoUTube "Validation of InternalResistancesTwoUTube"
  extends Modelica.Icons.Example;

  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";
  parameter Modelica.Units.SI.Length hSeg=borFieDat.conDat.hBor/nSeg
    "Length of the internal heat exchanger";
  parameter Modelica.Units.SI.ThermalResistance Rgb_val=0.572601
    "Grout node to borehole wall thermal resistance";
  parameter Modelica.Units.SI.ThermalResistance Rgg1_val=0.0406121
    "Grout node to grout node thermal resistance";
  parameter Modelica.Units.SI.ThermalResistance Rgg2_val=0.216904
    "Thermal resistance between two grout nodes opposite to each other";
  parameter Modelica.Units.SI.ThermalResistance RCondGro_val=0.195099
    "Pipe to grout node thermal resistance";
  parameter Modelica.Units.SI.Temperature T_start=298.15 "Initial temperature";

  Buildings.HeatTransfer.Sources.FixedTemperature TWal(T=T_start)
    "Borehole wall temperature"
  Buildings.HeatTransfer.Sources.FixedTemperature TPip2(T=T_start + 15)
    "Wall temperature of pipe 2"
  Buildings.HeatTransfer.Sources.PrescribedTemperature TPip1
    "Wall temperature of pipe 1"
  Modelica.Blocks.Sources.Ramp T_ramp(
    height=20,
    duration=600,
    offset=T_start)
                   "Temperature ramp of pipe 1"
  parameter Buildings.Fluid.Geothermal.Borefields.Data.Borefield.Example
    borFieDat(conDat=
        Buildings.Fluid.Geothermal.Borefields.Data.Configuration.Example(
        borCon=Buildings.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel,
        use_Rb=false))
    "Borefield data"
  Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.InternalResistancesTwoUTube
    intRes2UTub(
    hSeg=hSeg,
    T_start=T_start,
    borFieDat=borFieDat,
    Rgb_val=Rgb_val,
    RCondGro_val=RCondGro_val,
    Rgg1_val=Rgg1_val,
    Rgg2_val=Rgg2_val,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial)
                       "Thermal resistance and capacitances of the borehole"
  Buildings.HeatTransfer.Sources.FixedTemperature TPip3(T=T_start + 25)
    "Wall temperature of pipe 2"
  Buildings.HeatTransfer.Sources.FixedTemperature TPip4(T=T_start + 10)
    "Wall temperature of pipe 2"
equation

  connect(T_ramp.y,TPip1. T)
  connect(TPip1.port, intRes2UTub.port_1)
  connect(TPip2.port, intRes2UTub.port_2)
  connect(TWal.port, intRes2UTub.port_wall)
  connect(TPip4.port, intRes2UTub.port_4)
  connect(TPip3.port, intRes2UTub.port_3)
end InternalResistancesTwoUTube;


Example models to test base classes

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


Thermal resistance from the fluid in pipes and the grout zones (Bauer et al. 2011). This model computes the convection resistance in the pipes of a borehole segment
with heigth hSeg using correlations suggested by Bergman et al. (2011).

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions;
function convectionResistanceCircularPipe
  "Thermal resistance from the fluid in pipes and the grout zones (Bauer et al. 2011)"
  extends Modelica.Icons.Function;

  // Geometry of the borehole
  input Modelica.Units.SI.Height hSeg "Height of the element";
  input Modelica.Units.SI.Radius rTub "Tube radius";
  input Modelica.Units.SI.Length eTub "Tube thickness";
  // thermal properties
  input Modelica.Units.SI.ThermalConductivity kMed
    "Thermal conductivity of the fluid";
  input Modelica.Units.SI.DynamicViscosity muMed
    "Dynamic viscosity of the fluid";
  input Modelica.Units.SI.SpecificHeatCapacity cpMed
    "Specific heat capacity of the fluid";
  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 RFluPip
    "Convection resistance (or conduction in fluid if no mass flow)";

protected
  Modelica.Units.SI.Radius rTub_in=rTub - eTub "Pipe inner radius";
  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";
  Modelica.Units.SI.MassFlowRate m_flow_abs=
      Buildings.Utilities.Math.Functions.spliceFunction(
      m_flow,
      -m_flow,
      m_flow,
      m_flow_nominal/30);
  Real Re "Reynolds number";
  Real NuTurb "Nusselt at Re=2400";
  Real Nu "Nusselt";

algorithm
  // Convection resistance and Reynolds number
  k := 2/(muMed*Modelica.Constants.pi*rTub_in);
  Re := m_flow_abs*k;

  if Re>=2400 then
    // Turbulent, fully-developped flow in a smooth circular pipe with the
    // Dittus-Boelter correlation: 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)
    Nu := 0.023*(cpMed*muMed/kMed)^(0.35)*
      Buildings.Utilities.Math.Functions.regNonZeroPower(
        x=Re,
        n=0.8,
        delta=0.01*m_flow_nominal*k);
  else
    // Laminar, fully-developped flow in a smooth circular pipe with uniform
    // imposed temperature: Nu=3.66 for Re<=2300. For 2300<Re<2400, a smooth
    // transition is created with the splice function.
    NuTurb := 0.023*(cpMed*muMed/kMed)^(0.35)*(2400)^(0.8);
    Nu := Buildings.Utilities.Math.Functions.spliceFunction(NuTurb,3.66,Re-(2300+2400)/2,((2300+2400)/2)-2300);
  end if;
  h := Nu*kMed/(2*rTub_in);

  RFluPip := 1/(2*Modelica.Constants.pi*rTub_in*hSeg*h);
end convectionResistanceCircularPipe;


Thermal resistances for single U-tube, according to Bauer et al. (2011). This model computes the different thermal resistances present in 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 Claesson and Hellstrom (2011) using the multipole method.

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions;
function internalResistancesOneUTube
  "Thermal resistances for single U-tube, according to Bauer et al. (2011)"
  extends
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.partialInternalResistances;

  // Outputs
  output Modelica.Units.SI.ThermalResistance Rgb
    "Thermal resistance between grout zone and borehole wall";
  output Modelica.Units.SI.ThermalResistance Rgg
    "Thermal resistance between the two grout zones";
  output Modelica.Units.SI.ThermalResistance RCondGro
    "Thermal resistance between: pipe wall to capacity in grout";
protected
  Real[2,2] RDelta(each unit="(m.K)/W") "Delta-circuit thermal resistances";
  Real[2,2] R(each unit="(m.K)/W") "Internal thermal resistances";
  Modelica.Units.SI.Position[2] xPip={-sha,sha} "x-Coordinates of pipes";
  Modelica.Units.SI.Position[2] yPip={0.,0.} "y-Coordinates of pipes";
  Modelica.Units.SI.Radius[2] rPip={rTub,rTub} "Outer radius of pipes";
  Real[2] RFluPip(each unit="(m.K)/W") = {RCondPipe+RConv, RCondPipe+RConv} "Fluid to pipe wall thermal resistances";
  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";

  Real Ra(unit="(m.K)/W")
    "Grout-to-grout resistance (2D) as defined by Hellstrom. Interaction between the different grout parts";

algorithm
  // Internal thermal resistances
  (RDelta, R) :=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.multipoleThermalResistances(
      2, 3, xPip, yPip, rBor, rPip, kFil, kSoi, RFluPip);

  // Rb and Ra
  Rb_multipole := 1./(1./RDelta[1,1] + 1./RDelta[2,2]);
  Rb_internal := if use_Rb then Rb else Rb_multipole;
  // The short-circuit resistance in weigthed by the ratio between the used
  // value of Rb and the theoretical value
  Ra := (R[1,1] + R[2,2] - 2*R[1,2])*Rb_internal/Rb_multipole;

  // Conversion of Rb (resp. Ra) to Rg (resp. Rar) of Bauer:
  Rg  :=(2*Rb_internal-RCondPipe-RConv)/hSeg;
  Rar :=(Ra-2*(RCondPipe + RConv))/hSeg;

  // If any of the internal delta-circuit resistances is negative, then
  // the location (x) of the thermal capacity is set to zero to limit
  // instabilities in the calculations. Otherwise, calculations follow the
  // method of Bauer et al. (2011).
  if (RDelta[1,2] < 0) then
    //Thermal resistance between the grout zone and borehole wall
    Rgb := Rg;

    //Conduction resistance in grout from pipe wall to capacity in grout
    RCondGro := RCondPipe/hSeg;

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

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

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

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

      //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)^(-1) > 0);
      i := i + 1;
    end while;
  end if;
  assert(test, "In " + instanceName + ":\n" +
  "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.HeatExchangers.Boreholes.BaseClasses.singleUTubeResistances
  is
           hSeg = " + String(hSeg) + " m
           rBor = " + String(rBor) + " m
           rTub = " + String(rTub) + " m
           eTub = " + String(eTub) + " m
           kSoi = " + String(kSoi) + " W/m/K
           kFil = " + String(kFil) + " W/m/K
           kTub = " + String(kTub) + " W/m/K
           i = "  + String(i) + "
  Computed x    = " + String(x) + " K/W
           Rgb  = " + String(Rgb) + " K/W
           Rgg  = " + String(Rgg) + " K/W");
end internalResistancesOneUTube;


Thermal resistances for double U-tube, according to Bauer et al (2011). This model computes the different thermal resistances present in a double 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 Claesson and Hellstrom (2011) using the multipole method.

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions;
function internalResistancesTwoUTube
  "Thermal resistances for double U-tube, according to Bauer et al (2011)"
  extends
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.partialInternalResistances;

  // Outputs
  output Modelica.Units.SI.ThermalResistance Rgb
    "Thermal resistance between a grout capacity and the borehole wall, as defined by Bauer et al (2010)";
  output Modelica.Units.SI.ThermalResistance Rgg1
    "Thermal resistance between two neightbouring grout capacities, as defined by Bauer et al (2010)";
  output Modelica.Units.SI.ThermalResistance Rgg2
    "Thermal resistance between two  grout capacities opposite to each other, as defined by Bauer et al (2010)";
  output Modelica.Units.SI.ThermalResistance RCondGro
    "Thermal resistance between a pipe wall and the grout capacity, as defined by Bauer et al (2010)";
protected
  Real[4,4] RDelta(each unit="(m.K)/W") "Delta-circuit thermal resistances";
  Real[4,4] R(each unit="(m.K)/W") "Internal thermal resistances";
  Modelica.Units.SI.Position[4] xPip={-sha,sha,0.,0.} "x-Coordinates of pipes";
  Modelica.Units.SI.Position[4] yPip={0.,0.,-sha,sha} "y-Coordinates of pipes";
  Modelica.Units.SI.Radius[4] rPip={rTub,rTub,rTub,rTub}
    "Outer radius of pipes";
  Real[4] RFluPip(each unit="(m.K)/W") = {RCondPipe+RConv, RCondPipe+RConv, RCondPipe+RConv, RCondPipe+RConv} "Fluid to pipe wall thermal resistances";

  Real Ra( unit="(m.K)/W")
    "Grout-to-grout resistance (2D) as defined by Hellstrom. Interaction between the different grout parts";

  Modelica.Units.SI.ThermalResistance Rg
    "Thermal resistance between outer borehole wall and one tube";
  Modelica.Units.SI.ThermalResistance Rar1
    "Thermal resistance between the two closest pipe outer walls";
  Modelica.Units.SI.ThermalResistance Rar2
    "Thermal resistance between the two farthest pipe outer walls";

algorithm
  // Internal thermal resistances
  (RDelta, R) :=
    Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.multipoleThermalResistances(
      4, 3, xPip, yPip, rBor, rPip, kFil, kSoi, RFluPip);

  // Rb and Ra
  Rb_multipole := 1./(1./RDelta[1,1] + 1./RDelta[2,2] + 1./RDelta[3,3] + 1./RDelta[4,4]);
  Rb_internal := if use_Rb then Rb else Rb_multipole;
  // The short-circuit resistance in weigthed by the ratio between the used
  // value of Rb and the theoretical value
  Ra := (R[1,1] + R[3,3] - 2*R[1,3])*Rb_internal/Rb_multipole;

  // ------ Calculation according to Bauer et al. (2010)
  Rg := (4*Rb_internal - RCondPipe - RConv)/hSeg;
  Rar1 := ((2 + sqrt(2))*Rg*hSeg*(Ra - RCondPipe)/(Rg*hSeg + Ra - RCondPipe))/hSeg;
  Rar2 := sqrt(2)*Rar1;

  // If any of the internal delta-circuit resistances is negative, then
  // the location (x) of the thermal capacity is set to zero to limit
  // instabilities in the calculations. Otherwise, calculations follow the
  // method of Bauer et al. (2011).
  if (RDelta[1,2] < 0) or (RDelta[1,3] < 0) then
    //Thermal resistance between the grout zone and borehole wall
    Rgb := Rg;

    //Conduction resistance in grout from pipe wall to capacity in grout
    RCondGro := RCondPipe/hSeg;

    //Thermal resistance between the two grout zones
    Rgg1 := 2*Rgb*(Rar1)/(2*Rgb - Rar1);
    Rgg2 := 2*Rgb*(Rar2)/(2*Rgb - Rar2);

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

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

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

      //Thermal resistance between the two grout zones
      Rgg1 := 2*Rgb*(Rar1 - 2*x*Rg)/(2*Rgb - Rar1 + 2*x*Rg);
      Rgg2 := 2*Rgb*(Rar2 - 2*x*Rg)/(2*Rgb - Rar2 + 2*x*Rg);

      // Thermodynamic test to check if negative R values make sense. If not, decrease x-value.
      test := (((1/Rgg1 + 1/2/Rgb)^(-1) > 0) and ((1/Rgg2 + 1/2/Rgb)^(-1) > 0));
      i := i + 1;
    end while;
  end if;
  assert(test, "In " + instanceName + ":\n" +
  "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.HeatExchangers.Boreholes.BaseClasses.doubleUTubeResistances
  is
            hSeg = " + String(hSeg) + " m
            rBor = " + String(rBor) + " m
            rTub = " + String(rTub) + " m
            eTub = " + String(eTub) + " m
            sha = " + String(sha) + " m
            kSoi = " + String(kSoi) + " W/m/K
            kFil = " + String(kFil) + " W/m/K
            kTub = " + String(kTub) + " W/m/K
  Computed x    = " + String(x) + " m
            Rgb  = " + String(Rgb) + " K/W
            Rgg1  = " + String(Rgg1) + " K/W
            Rgg2  = " + String(Rgg2) + " K/W");
end internalResistancesTwoUTube;


Fluid temperatures from multipole solution. This model evaluates the fluid temperatures using the multipole method of Claesson and Hellstrom (2011).

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions;
function multipoleFluidTemperature "Fluid temperatures from multipole solution"
  extends Modelica.Icons.Function;

  input Integer nPip "Number of pipes";
  input Integer J "Number of multipoles";
  input Modelica.Units.SI.Position xPip[nPip] "x-Coordinates of pipes";
  input Modelica.Units.SI.Position yPip[nPip] "y-Coordinates of pipes";
  input Real QPip_flow[nPip](each unit="W/m") "Heat flow in pipes";
  input Modelica.Units.SI.Temperature TBor "Average borehole wall temperature";
  input Modelica.Units.SI.Radius rBor "Borehole radius";
  input Modelica.Units.SI.Radius rPip[nPip] "Outter radius of pipes";
  input Modelica.Units.SI.ThermalConductivity kFil
    "Thermal conductivity of grouting material";
  input Modelica.Units.SI.ThermalConductivity kSoi
    "Thermal conductivity of soil material";
  input Real RFluPip[nPip](each unit="(m.K)/W") "Fluid to pipe wall thermal resistances";
  input Real eps=1.0e-5 "Iteration relative accuracy";
  input Integer it_max=100 "Maximum number of iterations";

  output Modelica.Units.SI.Temperature TFlu[nPip] "Fluid temperature in pipes";

protected
  Real pikFil(unit="(m.K)/W")=1/(2*Modelica.Constants.pi*kFil) "Coefficient based on grout thermal conductivity";
  Real sigma=(kFil - kSoi)/(kFil + kSoi) "Thermal conductivity ratio";
  Real betaPip[nPip]=2*Modelica.Constants.pi*kFil*RFluPip "Dimensionless fluid to outter pipe wall thermal resistance";
  Complex zPip_i "Position of pipe i";
  Complex zPip_j "Position of pipe j";
  Complex P_nj "Multipole of order j for pipe n";
  Real PRea[nPip,J] "Matrix of real part of multipoles";
  Real PIma[nPip,J] "Matrix of imaginary part of multipole";
  Complex P_nj_new "New value of multipole of order j for pipe n";
  Real PRea_new[nPip,J] "New value of real part of multipoles";
  Real PIma_new[nPip,J] "New value of imaginary part of multipoles";
  Complex F_mk "Coefficient F of order k of pipe m";
  Real FRea[nPip,J] "Real part of matrix F_mk";
  Real FIma[nPip,J] "Imaginary part of matrix F_mk";
  Real R0[nPip,nPip](each unit="(m.K)/W") "Line source approximation of thermal resistances";
  Complex deltaTFlu "Fluid temperature difference with line source approximation";
  Real rbm "Intermediate coefficient";
  Modelica.Units.SI.Distance dz "Pipe to pipe distance";
  Real coeff[nPip,J] "Coefficient for multiplication with matrix F_mk";
  Real diff "Difference in subsequent multipole evaluations";
  Real diff_max "Maximum difference in subsequent multipole evaluations";
  Real diff_min "Minimum difference in subsequent multipole evaluations";
  Real diff0 "Difference in subsequent multipole evaluations";
  Integer it "Iteration counter";
  Real eps_max "Convergence variable";

algorithm
  // Thermal resistance matrix from 0th order multipole
  for i in 1:nPip loop
    zPip_i := Complex(xPip[i], yPip[i]);
    rbm :=rBor^2/(rBor^2 - Modelica.ComplexMath.abs(zPip_i)^2);
    R0[i, i] := pikFil*(log(rBor/rPip[i]) + betaPip[i] + sigma*log(rbm));
    for j in 1:nPip loop
      zPip_j := Complex(xPip[j], yPip[j]);
      if i <> j then
        dz :=Modelica.ComplexMath.abs(zPip_i - zPip_j);
        rbm :=rBor^2/Modelica.ComplexMath.abs(rBor^2 - zPip_j*
          Modelica.ComplexMath.conj(zPip_i));
        R0[i, j] := pikFil*(log(rBor/dz) + sigma*log(rbm));
      end if;
    end for;
  end for;

  // Initialize maximum error and iteration counter
  eps_max := 1.0e99;
  it := 0;
  // Multipoles
  if J > 0 then
    for m in 1:nPip loop
      for k in 1:J loop
        coeff[m, k] := -(1 - k*betaPip[m])/(1 + k*betaPip[m]);
        PRea[m, k] := 0;
        PIma[m, k] := 0;
      end for;
    end for;
    while eps_max > eps and it < it_max loop
      it := it + 1;
      (FRea, FIma) :=
        Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.multipoleFmk(
        nPip,
        J,
        QPip_flow,
        PRea,
        PIma,
        rBor,
        rPip,
        xPip,
        yPip,
        kFil,
        kSoi);
      for m in 1:nPip loop
        for k in 1:J loop
          F_mk := Complex(FRea[m, k], FIma[m, k]);
          P_nj_new := coeff[m, k]*Modelica.ComplexMath.conj(F_mk);
          PRea_new[m, k] := Modelica.ComplexMath.real(P_nj_new);
          PIma_new[m, k] := Modelica.ComplexMath.imag(P_nj_new);
        end for;
      end for;
      diff_max := 0;
      diff_min := 1e99;
      for m in 1:nPip loop
        for k in 1:J loop
          P_nj := Complex(PRea[m, k], PIma[m, k]);
          P_nj_new := Complex(PRea_new[m, k], PIma_new[m, k]);
          diff_max :=max(diff_max, Modelica.ComplexMath.abs(P_nj_new - P_nj));
          diff_min :=min(diff_min, Modelica.ComplexMath.abs(P_nj_new - P_nj));
        end for;
      end for;
      diff := diff_max - diff_min;
      if it == 1 then
        diff0 :=diff;
      end if;
      eps_max := diff/diff0;
      PRea := PRea_new;
      PIma := PIma_new;
    end while;
  end if;

  // Fluid Temperatures
  TFlu := TBor .+ R0*QPip_flow;
  if J > 0 then
    for m in 1:nPip loop
      zPip_i :=Complex(xPip[m], yPip[m]);
      deltaTFlu := Complex(0, 0);
      for n in 1:nPip loop
        zPip_j :=Complex(xPip[n], yPip[n]);
        for j in 1:J loop
          P_nj := Complex(PRea[n, j], PIma[n, j]);
          if n <> m then
            // Second term
            deltaTFlu := deltaTFlu + P_nj*(rPip[n]/(zPip_i - zPip_j))^j;
          end if;
          // Third term
          deltaTFlu := deltaTFlu + sigma*P_nj*(rPip[n]*
            Modelica.ComplexMath.conj(zPip_i)/(rBor^2 - zPip_j*
            Modelica.ComplexMath.conj(zPip_i)))^j;
        end for;
      end for;
      TFlu[m] := TFlu[m] + Modelica.ComplexMath.real(deltaTFlu);
    end for;
  end if;
end multipoleFluidTemperature;


Complex matrix F_mk from Claesson and Hellstrom (2011). This model evaluates the complex coefficient matrix F_mk from Claesson and Hellstrom (2011).

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions;
function multipoleFmk "Complex matrix F_mk from Claesson and Hellstrom (2011)"
  extends Modelica.Icons.Function;

  input Integer nPip "Number of pipes";
  input Integer J "Number of multipoles";
  input Real QPip_flow[nPip](each unit="W/m") "Heat flow in pipes";
  input Real PRea[nPip,J] "Multipoles (Real part)";
  input Real PIma[nPip,J] "Multipoles (Imaginary part)";
  input Modelica.Units.SI.Radius rBor "Borehole radius";
  input Modelica.Units.SI.Radius rPip[nPip] "Outter radius of pipes";
  input Modelica.Units.SI.Position xPip[nPip] "x-Coordinates of pipes";
  input Modelica.Units.SI.Position yPip[nPip] "y-Coordinates of pipes";
  input Modelica.Units.SI.ThermalConductivity kFil
    "Thermal conductivity of grouting material";
  input Modelica.Units.SI.ThermalConductivity kSoi
    "Thermal conductivity of soil material";

  output Real FRea[nPip,J] "Multipole coefficients";
  output Real FIma[nPip,J] "Multipole coefficients";

protected
  Complex zPip_i "Position of pipe i";
  Complex zPip_j "Position of pipe j";
  Complex P_nj "Multipole of order j for pipe n";
  Real pikFil(unit="(m.K)/W")=1/(2*Modelica.Constants.pi*kFil) "Coefficient based on grout thermal conductivity";
  Real sigma=(kFil - kSoi)/(kFil + kSoi) "Thermal conductivity ratio";
  Complex f "Intermedia value of multipole coefficient";
  Integer j_pend "Maximum loop index in fourth term";

algorithm

  for m in 1:nPip loop
    zPip_i := Complex(xPip[m], yPip[m]);
    for k in 1:J loop
      f := Complex(0, 0);
      for n in 1:nPip loop
        zPip_j := Complex(xPip[n], yPip[n]);
        // First term
        if m <> n then
          f := f + QPip_flow[n]*pikFil/k*(rPip[m]/(zPip_j - zPip_i))^k;
        end if;
        // Second term
        f := f + sigma*QPip_flow[n]*pikFil/k*(rPip[m]*Modelica.ComplexMath.conj(
          zPip_j)/(rBor^2 - zPip_i*Modelica.ComplexMath.conj(zPip_j)))^k;
        for j in 1:J loop
          P_nj := Complex(PRea[n, j], PIma[n, j]);
          // Third term
          if m <> n then
            f := f + P_nj*Buildings.Utilities.Math.Functions.binomial(j + k - 1, j -
              1)*rPip[n]^j*(-rPip[m])^k/(zPip_i - zPip_j)^(j + k);
          end if;
          //Fourth term
          j_pend := min(k, j);
          for jp in 0:j_pend loop
            f := f + sigma*Modelica.ComplexMath.conj(P_nj)*
              Buildings.Utilities.Math.Functions.binomial(j, jp)*
              Buildings.Utilities.Math.Functions.binomial(j + k - jp - 1, j - 1)*
              rPip[n]^j*rPip[m]^k*zPip_i^(j - jp)*Modelica.ComplexMath.conj(
              zPip_j)^(k - jp)/(rBor^2 - zPip_i*Modelica.ComplexMath.conj(
              zPip_j))^(k + j - jp);
          end for;
        end for;
      end for;
      FRea[m, k] := Modelica.ComplexMath.real(f);
      FIma[m, k] := Modelica.ComplexMath.imag(f);
    end for;
  end for;
end multipoleFmk;


Thermal resistances from multipole solution. This model evaluates the delta-circuit borehole thermal resistances using the multipole method of Claesson and Hellstrom (2011).

within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions;
function multipoleThermalResistances
  "Thermal resistances from multipole solution"
  extends Modelica.Icons.Function;

  input Integer nPip "Number of pipes";
  input Integer J "Number of multipoles";
  input Modelica.Units.SI.Position xPip[nPip] "x-Coordinates of pipes";
  input Modelica.Units.SI.Position yPip[nPip] "y-Coordinates of pipes";
  input Modelica.Units.SI.Radius rBor "Borehole radius";
  input Modelica.Units.SI.Radius rPip[nPip] "Outter radius of pipes";
  input Modelica.Units.SI.ThermalConductivity kFil
    "Thermal conductivity of grouting material";
  input Modelica.Units.SI.ThermalConductivity kSoi
    "Thermal conductivity of soil material";
  input Real RFluPip[nPip](each unit="(m.K)/W") "Fluid to pipe wall thermal resistances";
  input Modelica.Units.SI.Temperature TBor=0
    "Average borehole wall temperature";

  output Real RDelta[nPip,nPip](each unit="(m.K)/W") "Delta-circuit thermal resistances";
  output Real R[nPip,nPip](each unit="(m.K)/W") "Internal thermal resistances";

protected
  Real QPip_flow[nPip](each unit="W/m") "Pipe heat transfer rates";
  Modelica.Units.SI.Temperature TFlu[nPip] "Fluid temperatures";
  Real K[nPip,nPip](each unit="W/(m.K)") "Internal thermal conductances";

algorithm
  for m in 1:nPip loop
    for n in 1:nPip loop
      if n == m then
        QPip_flow[n] := 1;
      else
        QPip_flow[n] := 0;
      end if;
    end for;
    TFlu :=
      Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.Functions.multipoleFluidTemperature(
      nPip,
      J,
      xPip,
      yPip,
      QPip_flow,
      TBor,
      rBor,
      rPip,
      kFil,
      kSoi,
      RFluPip);
    for n in 1:nPip loop
      R[n, m] := TFlu[n];
    end for;
  end for;
  K := -Modelica.Math.Matrices.inv(R);
  for m in 1:nPip loop
    K[m, m] := -K[m, m];
    for n in 1:nPip loop
      if m <> n then
        K[m, m] := K[m, m] - K[m, n];
      end if;
    end for;
  end for;
  for m in 1:nPip loop
    for n in 1:nPip loop
      RDelta[m, n] := 1./K[m, n];
    end for;
  end for;
end multipoleThermalResistances;


within Buildings.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses;
package Functions "Package with functions for evaluation of borehole thermal resistances"
extends Modelica.Icons.VariantsPackage;
end Functions;