within AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples;
model BoreholeDynamics "Example model for different borehole models and dynamics"
  extends Modelica.Icons.Example;
  package Medium = AixLib.Media.Water;

  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";
  parameter Modelica.Units.SI.Temperature T_start=273.15 + 22
    "Initial soil temperature";

  AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.OneUTube
    borHolOneUTubDyn(
    redeclare package Medium = Medium,
    borFieDat=borFieUTubDat,
    m_flow_nominal=borFieUTubDat.conDat.mBor_flow_nominal,
    dp_nominal=borFieUTubDat.conDat.dp_nominal,
    nSeg=nSeg,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TGro_start = {T_start for i in 1:nSeg},
    TFlu_start={Medium.T_default for i in 1:nSeg})
    "Borehole with U-Tub configuration and grout dynamics"
  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFieUTubDat.conDat.mBor_flow_nominal,
    T=303.15) "Source"
  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"
  parameter
    AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Example
    borFieUTubDat "Borefield parameters with UTube borehole configuration"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBorIn(m_flow_nominal=borFieUTubDat.conDat.mBor_flow_nominal,
    redeclare package Medium = Medium,
    tau=0)
    "Inlet borehole temperature"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBor1UTubDyn(
    m_flow_nominal=borFieUTubDat.conDat.mBor_flow_nominal,
    redeclare package Medium = Medium,
    tau=0)
    "Outlet borehole temperature"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector therCol1(m=nSeg)
   "Thermal collector"
  Modelica.Blocks.Sources.Constant TGroUn(k=T_start)
    "Undisturbed ground temperature"
  AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.OneUTube borHolOneUTubSteSta(
    redeclare package Medium = Medium,
    borFieDat=borFieUTubDatSteSta,
    m_flow_nominal=borFieUTubDatSteSta.conDat.mBor_flow_nominal,
    dp_nominal=borFieUTubDatSteSta.conDat.dp_nominal,
    nSeg=nSeg,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    TGro_start = {T_start for i in 1:nSeg},
    TFlu_start={Medium.T_default for i in 1:nSeg})
    "Borehole with U-Tub configuration and steady state grout"
  AixLib.Fluid.Sources.MassFlowSource_T sou1(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFieUTubDat.conDat.mBor_flow_nominal,
    T=303.15) "Source"
  AixLib.Fluid.Sources.Boundary_pT sin1(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBorIn1(
    m_flow_nominal=borFieUTubDat.conDat.mBor_flow_nominal,
    redeclare package Medium = Medium,
    tau=0) "Inlet borehole temperature"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBor1UTubSteSta(
    m_flow_nominal=borFieUTubDat.conDat.mBor_flow_nominal,
    redeclare package Medium = Medium,
    tau=0) "Outlet borehole temperature"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector therCol2(m=nSeg)
    "Thermal collector"

  AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.TwoUTube borHol2UTubDyn(
    redeclare package Medium = Medium,
    dp_nominal=borFie2UTubDat.conDat.dp_nominal,
    m_flow_nominal=borFie2UTubDat.conDat.mBor_flow_nominal,
    borFieDat=borFie2UTubDat,
    nSeg=nSeg,
    energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial,
    TGro_start = {T_start for i in 1:nSeg},
    TFlu_start={Medium.T_default for i in 1:nSeg})
    "Borehole with 2U-Tub configuration and grout dynamics"
  AixLib.Fluid.Sources.MassFlowSource_T sou2(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFie2UTubDat.conDat.mBor_flow_nominal,
    T=303.15) "Source"
  AixLib.Fluid.Sources.Boundary_pT sin2(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"
  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Example borFie2UTubDat(
    conDat=AixLib.Fluid.Geothermal.Borefields.Data.Configuration.Example(
      borCon=AixLib.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel))
    "Borefield parameters with UTube borehole configuration"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBorIn2(redeclare package Medium =
        Medium, m_flow_nominal=borFie2UTubDat.conDat.mBor_flow_nominal,
    tau=0)
    "Inlet borehole temperature"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBor2UTubDyn(redeclare package Medium =
        Medium, m_flow_nominal=borFie2UTubDat.conDat.mBor_flow_nominal,
    tau=0)
    "Outlet borehole temperature"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector therCol3(m=nSeg)
    "Thermal collector"

  AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.TwoUTube borHol2UTubSteSta(
    redeclare package Medium = Medium,
    dp_nominal=borFie2UTubDatSteSta.conDat.dp_nominal,
    m_flow_nominal=borFie2UTubDatSteSta.conDat.mBor_flow_nominal,
    borFieDat=borFie2UTubDatSteSta,
    nSeg=nSeg,
    energyDynamics=Modelica.Fluid.Types.Dynamics.SteadyState,
    TGro_start = {T_start for i in 1:nSeg},
    TFlu_start={Medium.T_default for i in 1:nSeg})
    "Borehole with 2U-Tub configuration and steady states grout"
  AixLib.Fluid.Sources.MassFlowSource_T sou3(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFie2UTubDat.conDat.mBor_flow_nominal,
    T=303.15) "Source"
  AixLib.Fluid.Sources.Boundary_pT sin3(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBorIn3(
    redeclare package Medium = Medium,
    m_flow_nominal=borFie2UTubDat.conDat.mBor_flow_nominal,
    tau=0)
    "Inlet borehole temperature"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBor2UTubSteSta(
    redeclare package Medium = Medium,
    m_flow_nominal=borFie2UTubDat.conDat.mBor_flow_nominal,
    tau=0)
    "Outlet borehole temperature"
  Modelica.Thermal.HeatTransfer.Components.ThermalCollector therCol4(m=nSeg)
                                    "Thermal collector"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature
    prescribedTemperature
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature
    prescribedTemperature1
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature
    prescribedTemperature2
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature
    prescribedTemperature3
  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Example borFieUTubDatSteSta(filDat=
        AixLib.Fluid.Geothermal.Borefields.Data.Filling.Bentonite(
        steadyState=true))
    "Borefield parameters with UTube borehole configuration and steady-state filling"
  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Example borFie2UTubDatSteSta(
    filDat=AixLib.Fluid.Geothermal.Borefields.Data.Filling.Bentonite(
        steadyState=true),
    conDat=AixLib.Fluid.Geothermal.Borefields.Data.Configuration.Example(
        borCon=AixLib.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel))
    "Borefield parameters with UTube borehole configuration and steady-state filling"
equation
  connect(sou.ports[1],TBorIn. port_a)
  connect(TBorIn.port_b, borHolOneUTubDyn.port_a)
  connect(borHolOneUTubDyn.port_b, TBor1UTubDyn.port_a)
  connect(TBor1UTubDyn.port_b, sin.ports[1])
  connect(therCol1.port_a, borHolOneUTubDyn.port_wall)
  connect(sou1.ports[1], TBorIn1.port_a)
  connect(TBorIn1.port_b, borHolOneUTubSteSta.port_a)
  connect(borHolOneUTubSteSta.port_b, TBor1UTubSteSta.port_a)
  connect(TBor1UTubSteSta.port_b, sin1.ports[1])
  connect(therCol2.port_a, borHolOneUTubSteSta.port_wall)
  connect(sou2.ports[1], TBorIn2.port_a)
  connect(TBorIn2.port_b, borHol2UTubDyn.port_a)
  connect(borHol2UTubDyn.port_b, TBor2UTubDyn.port_a)
  connect(TBor2UTubDyn.port_b, sin2.ports[1])
  connect(therCol3.port_a, borHol2UTubDyn.port_wall)
  connect(sou3.ports[1], TBorIn3.port_a)
  connect(TBorIn3.port_b, borHol2UTubSteSta.port_a)
  connect(borHol2UTubSteSta.port_b, TBor2UTubSteSta.port_a)
  connect(TBor2UTubSteSta.port_b, sin3.ports[1])
  connect(therCol4.port_a, borHol2UTubSteSta.port_wall)
  connect(TGroUn.y, prescribedTemperature.T)
  connect(therCol1.port_b, prescribedTemperature.port)
  connect(therCol2.port_b, prescribedTemperature1.port)
  connect(TGroUn.y, prescribedTemperature1.T)
  connect(therCol3.port_b, prescribedTemperature2.port)
  connect(prescribedTemperature2.T, prescribedTemperature1.T)
  connect(therCol4.port_b, prescribedTemperature3.port)
  connect(prescribedTemperature3.T, prescribedTemperature1.T)
end BoreholeDynamics;


Test for the single U-tube borehole model. This example illustrates the use of the

AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.OneUTubes
model. It simulates the behavior of a borehole with a prescribed
borehole wall temperature boundary condition.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples;
model BoreholeOneUTube "Test for the single U-tube borehole model"
  extends Modelica.Icons.Example;
  extends
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples.BaseClasses.PartialBorehole(
      redeclare
      AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.OneUTube
      borHol(
        energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial));
end BoreholeOneUTube;


Test for the double U-tube borehole model. This example illustrates the use of the

AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.TwoUTube
model. It simulates the behavior of a borehole with a prescribed
borehole wall boundary condition.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples;
model BoreholeTwoUTube "Test for the double U-tube borehole model"
  extends Modelica.Icons.Example;
  extends AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples.BaseClasses.PartialBorehole(
      redeclare AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.TwoUTube
        borHol(energyDynamics=Modelica.Fluid.Types.Dynamics.FixedInitial),
      borFieDat(
        conDat=AixLib.Fluid.Geothermal.Borefields.Data.Configuration.Example(
          borCon=AixLib.Fluid.Geothermal.Borefields.Types.BoreholeConfiguration.DoubleUTubeParallel)));
end BoreholeTwoUTube;


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


within AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples;
package BaseClasses "Package with base classes for AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples"
  extends Modelica.Icons.BasesPackage;
end BaseClasses;


within AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.Examples.BaseClasses;
partial model PartialBorehole "Partial model for borehole example models"
  package Medium = Modelica.Media.Water.ConstantPropertyLiquidWater;

  parameter Integer nSeg(min=1) = 10
    "Number of segments to use in vertical discretization of the boreholes";
  parameter Modelica.Units.SI.Temperature T_start=273.15 + 22
    "Initial soil temperature";

  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Example
    borFieDat "Borefield parameters"

  replaceable AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialBorehole borHol
    constrainedby
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.Boreholes.BaseClasses.PartialBorehole(
    redeclare package Medium = Medium,
    borFieDat=borFieDat,
    m_flow_nominal=borFieDat.conDat.mBor_flow_nominal,
    dp_nominal=borFieDat.conDat.dp_nominal,
    nSeg=nSeg,
    TGro_start={T_start for i in 1:nSeg},
    TFlu_start={Medium.T_default for i in 1:nSeg})
    "Borehole connected to a discrete ground model"

  AixLib.Fluid.Sources.MassFlowSource_T sou(
    redeclare package Medium = Medium,
    nPorts=1,
    use_T_in=false,
    m_flow=borFieDat.conDat.mBor_flow_nominal,
    T=303.15) "Source"

  AixLib.Fluid.Sources.Boundary_pT sin(
    redeclare package Medium = Medium,
    use_p_in=false,
    use_T_in=false,
    nPorts=1,
    p=101330,
    T=283.15) "Sink"

  AixLib.Fluid.Sensors.TemperatureTwoPort TBorIn(m_flow_nominal=borFieDat.conDat.mBor_flow_nominal,
      redeclare package Medium = Medium,
    tau=0) "Inlet borehole temperature"
  AixLib.Fluid.Sensors.TemperatureTwoPort TBorOut(m_flow_nominal=borFieDat.conDat.mBor_flow_nominal,
      redeclare package Medium = Medium,
    tau=0) "Outlet borehole temperature"
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature preTem[nSeg](each T=T_start)
    "Prescribed temperature"
equation
  connect(sou.ports[1], TBorIn.port_a)
  connect(TBorIn.port_b, borHol.port_a)
  connect(borHol.port_b, TBorOut.port_a)
  connect(TBorOut.port_b, sin.ports[1])

  connect(borHol.port_wall, preTem.port)
end PartialBorehole;


Heat conduction in a cylinder using the radial discretization as advised by Eskilson. Model for radial heat transfer in a hollow cylinder.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer;
model Cylindrical
  "Heat conduction in a cylinder using the radial discretization as advised by Eskilson"

  parameter AixLib.Fluid.Geothermal.Borefields.Data.Soil.Template soiDat
  parameter Modelica.Units.SI.Height h "Height of the cylinder";
  parameter Modelica.Units.SI.Radius r_a "Internal radius";
  parameter Modelica.Units.SI.Radius r_b "External radius";
  parameter Integer nSta(min=1) = 10 "Number of state variables";
  parameter Modelica.Units.SI.Temperature TInt_start
    "Initial temperature at port_a, used if steadyStateInitial = false"
  parameter Modelica.Units.SI.Temperature TExt_start
    "Initial temperature at port_b, used if steadyStateInitial = false"
  parameter Boolean steadyStateInitial=false
    "true initializes dT(0)/dt=0, false initializes T(0) at fixed temperature using T_a_start and T_b_start"

  parameter Real gridFac(min=1) = 2 "Grid factor for spacing";

  parameter Modelica.Units.SI.Radius r[nSta + 1](each fixed=false)
    "Radius to the boundary of the i-th domain";

  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_a port_a(T(start=TInt_start))
    "Heat port at surface a"
  Modelica.Thermal.HeatTransfer.Interfaces.HeatPort_b port_b(T(start=TExt_start))
    "Heat port at surface b"

  Modelica.Units.SI.Temperature T[nSta](start={TInt_start + (TExt_start -
        TInt_start)/Modelica.Math.log(r_b/r_a)*Modelica.Math.log((r_a + (r_b -
        r_a)/(nSta)*(i - 0.5))/r_a) for i in 1:nSta})
    "Temperature of the states";

  Modelica.Units.SI.TemperatureDifference dT "port_a.T - port_b.T";

  Modelica.Units.SI.HeatFlowRate Q_flow[nSta + 1]
    "Heat flow rate from state i to i+1";

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

  final parameter Modelica.Units.SI.SpecificHeatCapacity c=soiDat.cSoi
    "Specific heat capacity";
  final parameter Modelica.Units.SI.ThermalConductivity k=soiDat.kSoi
    "Thermal conductivity of the material";
  final parameter Modelica.Units.SI.Density d=soiDat.dSoi
    "Density of the material";

  parameter Modelica.Units.SI.ThermalConductance G[nSta + 1](each fixed=false)
    "Heat conductance between the temperature nodes";
  parameter Modelica.Units.SI.HeatCapacity C[nSta](each fixed=false)
    "Heat capacity of each state";

  parameter Real gridFac_sum(fixed=false);
  parameter Real gridFac_sum_old(fixed=false);

initial algorithm
  for i in 0:nSta - 3 - 1 loop
    if i == 0 then
      gridFac_sum := gridFac^i;
      gridFac_sum_old := gridFac_sum;
    else
      gridFac_sum := gridFac_sum_old + gridFac^i;
      gridFac_sum_old := gridFac_sum;
    end if;
  end for;

initial equation
  assert(r_a < r_b, "Error: Model requires r_a < r_b.");
  assert(0 < r_a, "Error: Model requires 0 < r_a.");

  // ****************** Comments *********************:
  // The layer is divided into nSta segments. The radius of each segment increase exponentially with base 'griFac':
  // r_b - r_a = sum( a * griFac^k - a , k=2..nSta) => from this we find a = (r_b - r_a)/(griFac^(n+1) - griFac)
  // Using this a, we can now find the r[i]:
  // r[1] := r_a
  // r[i] = r[i-1] + a * griFac^i = r[i-1] + griFac^i * (r_b - r_a) / (griFac^(n+1) - griFac)
  //
  // This can also be writing as:
  // r[i]= r[i-1] + ( r_b - r_a)  * (1-griFac)/(1-griFac^(nSta)) * griFac^(i-2);

  r[1] = r_a;
  r[2] = r_a + sqrt(k/c/d*60) "eskilson minimum length";
  r[3] = r_a + 2*sqrt(k/c/d*60);
  r[4] = r_a + 3*sqrt(k/c/d*60);

  for i in 5:nSta + 1 loop
    r[i] = r[i - 1] + (r_b - r[4])/gridFac_sum*gridFac^(i - 5);
  end for;
  assert(abs(r[nSta + 1] - r_b) < 1E-3,
    "Error: Wrong computation of radius. r[nSta+1]=" + String(r[nSta + 1]));

  // Radii at middle of resistance
  for i in 1:nSta loop
    rC[i] = (r[i] + r[i + 1])/2;
  end for;

  // Conductance between nodes (which are in the center of the domain)
  G[1] = 2*Modelica.Constants.pi*k*h/Modelica.Math.log(rC[1]/r_a);
  G[nSta + 1] = 2*Modelica.Constants.pi*k*h/Modelica.Math.log(r_b/rC[nSta]);
  for i in 2:nSta loop
    G[i] = 2*Modelica.Constants.pi*k*h/Modelica.Math.log(rC[i]/rC[i - 1]);
  end for;

  // Heat capacity of each segment
  for i in 1:nSta loop
    C[i] = (d*Modelica.Constants.pi*c*h*((r[i + 1])^2 - (r[i])^2));
  end for;
  // The initialization is only done for materials that store energy.
  if not soiDat.steadyState then
    if steadyStateInitial then
      der(T) = zeros(nSta);
    else
      for i in 1:nSta loop
        T[i] = TInt_start + (TExt_start - TInt_start)/Modelica.Math.log(r_b/r_a)
          *Modelica.Math.log(rC[i]/r_a);
      end for;
    end if;
  end if;
equation
  dT = port_a.T - port_b.T;
  port_a.Q_flow = +Q_flow[1];
  port_b.Q_flow = -Q_flow[nSta + 1];
  Q_flow[1] = G[1]*(port_a.T - T[1]);
  Q_flow[nSta + 1] = G[nSta + 1]*(T[nSta] - port_b.T);
  for i in 2:nSta loop
    Q_flow[i] = G[i]*(T[i - 1] - T[i]);
    // Q_flow[i] represents the heat flowing between two nodes
  end for;
  if soiDat.steadyState then
    for i in 2:nSta + 1 loop
      Q_flow[i] = Q_flow[1];
    end for;
  else
    for i in 1:nSta loop
      der(T[i]) = (Q_flow[i] - Q_flow[i + 1])/C[i];
    end for;
  end if;
end Cylindrical;


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

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer;
model GroundTemperatureResponse "Model calculating discrete load aggregation"
  parameter Modelica.Units.SI.Time tLoaAgg(final min=Modelica.Constants.eps)=
    3600 "Time resolution of load aggregation";
  parameter Integer nCel(min=1)=5 "Number of cells per aggregation level";
  parameter Integer nSeg=12
    "Number of segments per borehole for g-function calculation";
  parameter Integer nClu=5 "Number of clusters for g-function calculation";
  parameter Boolean forceGFunCalc = false
    "Set to true to force the thermal response to be calculated at the start instead of checking whether it has been pre-computed";
  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Template borFieDat
    "Record containing all the parameters of the borefield model"

  Modelica.Blocks.Interfaces.RealOutput delTBor(unit="K")
    "Temperature difference current borehole wall temperature minus initial borehole wall temperature"
  Modelica.Blocks.Interfaces.RealInput QBor_flow(unit="W")
    "Heat flow from all boreholes combined (positive if heat from fluid into soil)"

protected
  constant Integer nTimSho = 26 "Number of time steps in short time region";
  constant Integer nTimLon = 50 "Number of time steps in long time region";
  constant Real ttsMax = exp(5) "Maximum non-dimensional time for g-function calculation";
  constant Integer nTimTot = nTimSho+nTimLon
    "Total length of g-function vector";
  constant Real lvlBas = 2 "Base for exponential cell growth between levels";

  parameter String SHAgfun=
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.shaGFunction(
      nBor=borFieDat.conDat.nBor,
      cooBor=borFieDat.conDat.cooBor,
      hBor=borFieDat.conDat.hBor,
      dBor=borFieDat.conDat.dBor,
      rBor=borFieDat.conDat.rBor,
      aSoi=borFieDat.soiDat.aSoi,
      nSeg=nSeg,
      nClu=nClu,
      nTimSho=nTimSho,
      nTimLon=nTimLon,
      ttsMax=ttsMax) "String with encrypted g-function arguments";
  parameter Modelica.Units.SI.Time timFin=(borFieDat.conDat.hBor^2/(9*borFieDat.soiDat.aSoi))
      *ttsMax "Final time for g-function calculation";
  parameter Integer i(min=1)=
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.countAggregationCells(
      lvlBas=lvlBas,
      nCel=nCel,
      timFin=timFin,
      tLoaAgg=tLoaAgg)
      "Number of aggregation cells";
  final parameter Real[nTimTot,2] timSer(each fixed=false)
    "g-function input from matrix, with the second column as temperature Tstep";
  final parameter Modelica.Units.SI.Time t_start(fixed=false)
    "Simulation start time";
  final parameter Modelica.Units.SI.Time[i] nu(each fixed=false)
    "Time vector for load aggregation";
  final parameter Real[i] kappa(each fixed=false)
    "Weight factor for each aggregation cell";
  final parameter Real[i] rCel(each fixed=false) "Cell widths";

  discrete Modelica.Units.SI.HeatFlowRate[i] QAgg_flow
    "Vector of aggregated loads";
  discrete Modelica.Units.SI.HeatFlowRate[i] QAggShi_flow
    "Shifted vector of aggregated loads";
  discrete Integer curCel "Current occupied cell";

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

  Modelica.Units.SI.Heat U "Accumulated heat flow from all boreholes";
  discrete Modelica.Units.SI.Heat U_old
    "Accumulated heat flow from all boreholes at last aggregation step";

initial equation
  QAgg_flow = zeros(i);
  curCel = 1;
  delTBor = 0;
  QAggShi_flow = QAgg_flow;
  delTBor0 = 0;
  U = 0;
  U_old = 0;
  derDelTBor0 = 0;

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

  t_start = time;

  kappa = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.aggregationWeightingFactors(
    i=i,
    nTimTot=nTimTot,
    TStep=timSer,
    nu=nu);

  dTStepdt = kappa[1]/tLoaAgg;

  timSer =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.temperatureResponseMatrix(
      nBor=borFieDat.conDat.nBor,
      cooBor=borFieDat.conDat.cooBor,
      hBor=borFieDat.conDat.hBor,
      dBor=borFieDat.conDat.dBor,
      rBor=borFieDat.conDat.rBor,
      aSoi=borFieDat.soiDat.aSoi,
      kSoi=borFieDat.soiDat.kSoi,
      nSeg=nSeg,
      nClu=nClu,
      nTimSho=nTimSho,
      nTimLon=nTimLon,
      nTimTot=nTimTot,
      ttsMax=ttsMax,
      sha=SHAgfun,
      forceGFunCalc=forceGFunCalc);

equation
  der(delTBor) = dTStepdt*QBor_flow + derDelTBor0;
  der(U) = QBor_flow;

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

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

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

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


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


within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer;
package LoadAggregation "Package with functions for load aggregation"
  extends Modelica.Icons.BasesPackage;
end LoadAggregation;


This validation case shows the construction of the aggregation time and size vectors. This validation case builds the aggregation vectors (rCel and nu)
for a fictional case with 6 cells, built in 3 layers of 2 cells which double in size
each level. The timFin input to the function called is lower than the aggregation
time of the 6th cell, and the 6th cell must therefore be truncated from a size of 4 to a size of 2.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.Validation;
model AggregationCellTimes
  "This validation case shows the construction of the aggregation time and size vectors"
  extends Modelica.Icons.Example;

  parameter Integer i = 6 "Number of aggregation cells";
  parameter Modelica.Units.SI.Time tLoaAgg=10
    "Time resolution of load aggregation";
  final parameter Modelica.Units.SI.Time[i] nu(each fixed=false)
    "Time vector for load aggregation";
  final parameter Modelica.Units.SI.Time[i] rCel(each fixed=false)
    "Cell widths";

  Modelica.Units.SI.Time nu_error;
  Modelica.Units.SI.Time rCel_error "Error on chosen values";

initial equation
  (nu,rCel) = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.aggregationCellTimes(
    i=i,
    lvlBas=2,
    nCel=2,
    tLoaAgg=tLoaAgg,
    timFin=12*tLoaAgg);

equation
  nu_error = 100.0-nu[i-1];
  rCel_error = 2.0-rCel[i];
end AggregationCellTimes;


This validation case verifies the calculation of the weighting factors kappa. This validation case uses the first few values of a borehole temperature reponse
time series to construct the weighting factors kappa. The aggregation
cells are chosen such that there is a change in levels (and therefore a doubling
in the size of the cells) from cell 5 to 6. Therefore, kappa[5] is lower than
kappa[4] and kappa[7] is lower than kappa[6], but
kappa[6] is higher than kappa[5].

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.Validation;
model AggregationWeightingFactors
  "This validation case verifies the calculation of the weighting factors kappa"
  extends Modelica.Icons.Example;

  parameter Real[6,2] timSer=
    [0, 0;
    0.999999999999999, 7.96581783184631e-06;
    2.30986142530843, 1.36683711896241e-05;
    4.02559837881946, 1.89652463558340e-05;
    6.27297603019976, 2.43435015306157e-05;
    9.21672932384307, 3.00295537091117e-05]
    "Complete time matrix with TStep";
  Modelica.Units.SI.ThermalResistance[10] kappa
    "Weight factor for each aggregation cell";

equation
  kappa = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.aggregationWeightingFactors(
    i=10,
    nTimTot=6,
    TStep=timSer,
    nu=cat(1,linspace(0.4,2,5),linspace(2.8,6,5)));
end AggregationWeightingFactors;


This validation case verifies the counting of the required length of aggregation vectors. This validation case counts the required length of the aggregation vectors for the
same fictional case as in

AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.Validation.AggregationCellTimes.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.Validation;
model CountAggregationCells "This validation case verifies the counting of the required length of aggregation vectors"
  extends Modelica.Icons.Example;

  Integer i "Number of aggregation cells";

equation
  i = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.countAggregationCells(
      lvlBas=2,
      nCel=2,
      timFin=120,
      tLoaAgg=10);
end CountAggregationCells;


Validation models for LoadAggregation

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation;
package Validation "Validation models for LoadAggregation"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


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

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

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

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

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


This validation case test the calculation, writing and reading of the temperature step response. This validation case calculates the g-function for a defined single borehole, turns it
into a temperature step response, saves it as "TemperatureResponseMatrix_validationTStep.mat",
reads this .mat file and shows the evolution of the temperature step reponse over
the course of the first year.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.Validation;
model TemperatureResponseMatrix
  "This validation case test the calculation, writing and reading of the temperature step response"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Time timSer[26 + 50,2]=
      AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.temperatureResponseMatrix(
      nBor=1,
      cooBor={{0,0}},
      hBor=150,
      dBor=4,
      rBor=0.075,
      aSoi=1e-6,
      kSoi=3,
      nSeg=12,
      nClu=1,
      nTimSho=26,
      nTimLon=50,
      nTimTot=26 + 50,
      ttsMax=exp(5),
      sha="TemperatureResponseMatrix_validation",
      forceGFunCalc=true) "Resulting temperature response matrix";
  Modelica.Units.SI.ThermalResistance TStep "Temperature step response";

equation
  TStep = Modelica.Math.Vectors.interpolate(timSer[:,1],timSer[:,2],time);
end TemperatureResponseMatrix;


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

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

  parameter Modelica.Units.SI.TemperatureDifference supPos=
      AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.LoadAggregation.temporalSuperposition(
      i=6,
      QAgg_flow={2,1e6,3,1e6,1e6},
      kappa={0.4,0,0.2,1,0},
      curCel=3) "Temporal superposition";
  Modelica.Units.SI.TemperatureDifference supPosErr;

equation
  supPosErr = abs((2*0.4+3*0.2)-supPos);
end TemporalSuperposition;


Infinite line source model for borehole heat exchangers. This function evaluates the infinite line source solution. This solution gives
the relation between the constant heat transfer rate (per unit length) injected
by a line heat source of infinite length and the temperature raise in the
medium. The infinite line source solution is defined by

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors;
function infiniteLineSource
  "Infinite line source model for borehole heat exchangers"
  extends Modelica.Icons.Function;

  input Real t "Time";
  input Real aSoi "Ground thermal diffusivity";
  input Real dis "Radial distance between borehole axes";

  output Real h_ils "Thermal response factor of borehole 1 on borehole 2";

algorithm
  h_ils := if t > 0.0 then
              AixLib.Utilities.Math.Functions.exponentialIntegralE1(dis^2/(4*aSoi*t))
           else
              0.0;
end infiniteLineSource;


within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer;
package ThermalResponseFactors "Models for heat transfer outside boreholes"
extends Modelica.Icons.BasesPackage;
end ThermalResponseFactors;


Clustering of a field of 100 boreholes. This example uses a rectangular field of 10 by 10 boreholes to test the
identification borehole clusters.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model ClusterBoreholes_100boreholes
  "Clustering of a field of 100 boreholes"
  extends Modelica.Icons.Example;

  parameter Integer nBor = 100 "Number of boreholes";
  parameter Modelica.Units.SI.Position cooBor[nBor, 2] = {{7.5*mod(i-1,10), 7.5*floor((i-1)/10)} for i in 1:nBor}
    "Coordinates of boreholes";
  parameter Modelica.Units.SI.Height hBor=150 "Borehole length";
  parameter Modelica.Units.SI.Height dBor=4 "Borehole buried depth";
  parameter Modelica.Units.SI.Radius rBor=0.075 "Borehole radius";
  parameter Integer k=4 "Number of clusters to be generated";

  parameter Integer labels[nBor](each fixed=false) "Cluster label associated with each data point";
  parameter Integer cluSiz[k](each fixed=false) "Size of the clusters";

  parameter Integer labelsExp[nBor]=
    {3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 2, 2, 2, 2, 4, 4, 3, 3, 4, 2, 2, 1,
     1, 2, 2, 4, 3, 4, 2, 2, 1, 1, 1, 1, 2, 2, 4, 4, 2, 1, 1, 1, 1, 1, 1, 2, 4,
     4, 2, 1, 1, 1, 1, 1, 1, 2, 4, 4, 2, 2, 1, 1, 1, 1, 2, 2, 4, 3, 4, 2, 2, 1,
     1, 2, 2, 4, 3, 3, 4, 4, 2, 2, 2, 2, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3}
    "Expected cluster labels";

  // Comparison result
  Boolean cmp "Comparison result";

initial equation
  (labels, cluSiz) = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.clusterBoreholes(nBor,cooBor,hBor,dBor,rBor,k);

equation
  cmp = Modelica.Math.Vectors.isEqual(labels, labelsExp);
end ClusterBoreholes_100boreholes;


Test case for cylindrical heat source. This example demonstrates the use of the function for the evaluation of the
cylindrical heat source solution.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model CylindricalHeatSource "Test case for cylindrical heat source"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=1.0e-6
    "Ground thermal diffusivity";
  parameter Modelica.Units.SI.Radius rSource=0.075 "Radius of cylinder source";
  parameter Modelica.Units.SI.Radius[5] r={rSource,2*rSource,5*rSource,10*
      rSource,20*rSource} "Radial position of evaluation of the solution";
  Modelica.Units.SI.Time t "Time";
  Real[5] G "Cylindrical heat source solution";

equation
  t = exp(time) - 1.0;
  for k in 1:5 loop
    G[k] = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.cylindricalHeatSource(
      t = t,
      aSoi = aSoi,
      dis = r[k],
      rBor = rSource);
  end for;
end CylindricalHeatSource;


Test case for cylindrical heat source integrand function. This example demonstrates the evaluation of the
cylindrical heat source integrand function.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model CylindricalHeatSource_Integrand
  "Test case for cylindrical heat source integrand function"
  extends Modelica.Icons.Example;

  parameter Real Fo = 1.0 "Fourier time";
  parameter Real[4] p = {1, 2, 5, 10} "Ratio of distance over borehole radius";
  Real u "Integration variable";
  Real[4] y "Cylindrical heat source integrand";

equation
  u = time;
  for k in 1:4 loop
    y[k] = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.cylindricalHeatSource_Integrand(
      u = u,
      Fo = Fo,
      p = p[k]);
  end for;
end CylindricalHeatSource_Integrand;


Test case for finite line source. This example demonstrates the use of the function for the evaluation of the
finite line source solution. The solution is evaluated at different positions
and averaged over different lengths around line heat sources.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model FiniteLineSource "Test case for finite line source"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=1.0e-6
    "Ground thermal diffusivity";
  parameter Modelica.Units.SI.Distance[2] r={0.075,7.0}
    "Radial position of evaluation of the solution";
  parameter Modelica.Units.SI.Height len1=12.5 "Length of emitting source";
  parameter Modelica.Units.SI.Height burDep1=29.0
    "Buried depth of emitting source";
  parameter Modelica.Units.SI.Height[7] len2={12.5,8.0,15.0,14.0,6.0,20.0,3.0}
    "Length of receiving line";
  parameter Modelica.Units.SI.Height[7] burDep2={29.0,25.0,34.0,2.0,32.0,27.0,
      44.0} "Buried depth of receiving line";
  Modelica.Units.SI.Time t "Time";
  Real[2,7] hRea "Finite line source solution (Real part)";
  Real[2,7] hMir "Finite line source solution (Mirror part)";
  Real[2,7] h "Finite line source solution";

equation

  t = exp(time) - 1.0;

  for i in 1:2 loop
    for j in 1:7 loop
        hRea[i,j] = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource(
          t = t,
          aSoi = aSoi,
          dis = r[i],
          len1 = len1,
          burDep1 = burDep1,
          len2 = len2[j],
          burDep2 = burDep2[j],
          includeMirrorSource=false);
        hMir[i,j] = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource(
          t = t,
          aSoi = aSoi,
          dis = r[i],
          len1 = len1,
          burDep1 = burDep1,
          len2 = len2[j],
          burDep2 = burDep2[j],
          includeRealSource=false);
        h[i,j] = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource(
          t = t,
          aSoi = aSoi,
          dis = r[i],
          len1 = len1,
          burDep1 = burDep1,
          len2 = len2[j],
          burDep2 = burDep2[j]);
    end for;
  end for;
end FiniteLineSource;


Test case for finite line source for equivalent boreholes. This example demonstrates the use of the function for the evaluation of the
finite line source solution for equivalent boreholes. The solution is evaluated
for the interactions between 3 interacting boreholes on a right triangle pattern
with coordinates (x,y) = {(0,0), (0,7), (7,0)}.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model FiniteLineSource_Equivalent
  "Test case for finite line source for equivalent boreholes"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=1.0e-6
    "Ground thermal diffusivity";
  parameter Modelica.Units.SI.Distance[2] dis={7.0, 7.0*sqrt(2)}
    "Radial distance between borehole axes";
  parameter Integer[2] wDis={4, 2}
    "Number of occurences of each distance";
  parameter Modelica.Units.SI.Height len1=150.0 "Length of emitting sources";
  parameter Modelica.Units.SI.Height burDep1=4.0
    "Buried depth of emitting sources";
  parameter Modelica.Units.SI.Height len2=150.0 "Length of receiving lines";
  parameter Modelica.Units.SI.Height burDep2=4.0
    "Buried depth of receiving lines";
  parameter Integer nBor2=3 "Number of receiving lines";
  parameter Integer n_dis=2 "Number of unique distances";

  Modelica.Units.SI.Time t "Time";
  Real hRea "Finite line source solution (Real part)";
  Real hMir "Finite line source solution (Mirror part)";
  Real h "Finite line source solution";

equation

  t = exp(time) - 1.0;
  hRea =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Equivalent(
    t=t,
    aSoi=aSoi,
    dis=dis,
    wDis=wDis,
    n_dis=n_dis,
    len1=len1,
    burDep1=burDep1,
    len2=len2,
    burDep2=burDep2,
    nBor2=nBor2,
    includeMirrorSource=false);
  hMir =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Equivalent(
    t=t,
    aSoi=aSoi,
    dis=dis,
    wDis=wDis,
    n_dis=n_dis,
    len1=len1,
    burDep1=burDep1,
    len2=len2,
    burDep2=burDep2,
    nBor2=nBor2,
    includeRealSource=false);
  h =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Equivalent(
    t=t,
    aSoi=aSoi,
    dis=dis,
    wDis=wDis,
    n_dis=n_dis,
    len1=len1,
    burDep1=burDep1,
    len2=len2,
    burDep2=burDep2,
    nBor2=nBor2);
end FiniteLineSource_Equivalent;


Test case for the evaluation of the integral of the error function. This example demonstrates the evaluation of the
integral of the error function.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model FiniteLineSource_Erfint
  "Test case for the evaluation of the integral of the error function"
  extends Modelica.Icons.Example;

  Real u "Independent variable";
  Real erfint "Integral of the error function";
  Real erfint_num "Numerical integral of the error function";
  Real err "Difference between analytical and numerical evaluations";

initial equation
  erfint_num=0.0;

equation
  u = time;
  erfint = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Erfint(u=u);
  der(erfint_num) = Modelica.Math.Special.erf(u);
  err = erfint - erfint_num;
end FiniteLineSource_Erfint;


Test case for finite line source integrand function. This example demonstrates the evaluation of the
finite line source integrand function.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model FiniteLineSource_Integrand
  "Test case for finite line source integrand function"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Distance dis=0.075
    "Radial distance between borehole axes";
  parameter Modelica.Units.SI.Height len1=150.0 "Length of emitting borehole";
  parameter Modelica.Units.SI.Height burDep1=4.0
    "Buried depth of emitting borehole";
  parameter Modelica.Units.SI.Height len2=150.0 "Length of receiving borehole";
  parameter Modelica.Units.SI.Height burDep2=4.0
    "Buried depth of receiving borehole";
  Real u "Integration variable";
  Real y "Finite line source integrand";

equation
  u = time;
  y = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand(
    u=u,
    dis=dis,
    len1=len1,
    burDep1=burDep1,
    len2=len2,
    burDep2=burDep2);
end FiniteLineSource_Integrand;


Test case for finite line source integrand function for equivalent boreholes. This example demonstrates the use of the function for the evaluation of the
finite line source integrand for equivalent boreholes. The solution is evaluated
for the interactions between 3 interacting boreholes on a right triangle pattern
with coordinates (x,y) = {(0,0), (0,7), (7,0)}.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model FiniteLineSource_Integrand_Equivalent
  "Test case for finite line source integrand function for equivalent boreholes"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=1.0e-6
    "Ground thermal diffusivity";
  parameter Modelica.Units.SI.Distance[2] dis={7.0, 7.0*sqrt(2)}
    "Radial distance between borehole axes";
  parameter Integer[2] wDis={4, 2}
    "Number of occurences of each distance";
  parameter Modelica.Units.SI.Height len1=150.0 "Length of emitting sources";
  parameter Modelica.Units.SI.Height burDep1=4.0
    "Buried depth of emitting sources";
  parameter Modelica.Units.SI.Height len2=150.0 "Length of receiving lines";
  parameter Modelica.Units.SI.Height burDep2=4.0
    "Buried depth of receiving lines";
  parameter Integer nBor2=3 "Number of receiving lines";
  parameter Integer n_dis=2 "Number of unique distances";

  Real u "Integration variable";
  Real yRea "Finite line source integrand (Real part)";
  Real yMir "Finite line source integrand (Mirror part)";
  Real y "Finite line source integrand";

equation
  u = time;
  yRea =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand_Equivalent(
    u=u,
    dis=dis,
    wDis=wDis,
    len1=len1,
    burDep1=burDep1,
    len2=len2,
    burDep2=burDep2,
    nBor2=nBor2,
    n_dis=n_dis,
    includeMirrorSource=false);
  yMir =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand_Equivalent(
    u=u,
    dis=dis,
    wDis=wDis,
    len1=len1,
    burDep1=burDep1,
    len2=len2,
    burDep2=burDep2,
    nBor2=nBor2,
    n_dis=n_dis,
    includeRealSource=false);
  y =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand_Equivalent(
    u=u,
    dis=dis,
    wDis=wDis,
    len1=len1,
    burDep1=burDep1,
    len2=len2,
    burDep2=burDep2,
    nBor2=nBor2,
    n_dis=n_dis);
end FiniteLineSource_Integrand_Equivalent;


within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model FiniteLineSource_Integrand_Length
  "Test case for finite line source integrand function"
  extends Modelica.Icons.Example;

  parameter Real dis_over_len = 0.0005 "Radial distance between borehole axes";
  parameter Modelica.Units.SI.Height len150=150.0 "Length of emitting borehole";
  parameter Modelica.Units.SI.Height len75=75.0 "Length of emitting borehole";
  parameter Modelica.Units.SI.Height len25=25.0 "Length of emitting borehole";
  parameter Modelica.Units.SI.Height len5=5.0 "Length of emitting borehole";
  parameter Modelica.Units.SI.Height len1=1.0 "Length of emitting borehole";
  parameter Modelica.Units.SI.Height burDep=4.
    "Buried depth of emitting borehole";
  Real u "Integration variable";
  Real logy150 "Logarithm of finite line source integrand";
  Real logy75 "Logarithm of finite line source integrand";
  Real logy25 "Logarithm of finite line source integrand";
  Real logy5 "Logarithm of finite line source integrand";
  Real logy1 "Logarithm of finite line source integrand";

equation
  u = time;
  logy150 = log10(max(Modelica.Constants.small,
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand(
      u=u,
      dis=dis_over_len*len150,
      len1=len150,
      burDep1=burDep,
      len2=len150,
      burDep2=burDep)));
  logy75 = log10(max(Modelica.Constants.small,
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand(
      u=u,
      dis=dis_over_len*len75,
      len1=len75,
      burDep1=burDep,
      len2=len75,
      burDep2=burDep)));
  logy25 = log10(max(Modelica.Constants.small,
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand(
      u=u,
      dis=dis_over_len*len25,
      len1=len25,
      burDep1=burDep,
      len2=len25,
      burDep2=burDep)));
  logy5 = log10(max(Modelica.Constants.small,
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand(
      u=u,
      dis=dis_over_len*len5,
      len1=len5,
      burDep1=burDep,
      len2=len5,
      burDep2=burDep)));
  logy1 = log10(max(Modelica.Constants.small,
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_Integrand(
      u=u,
      dis=dis_over_len*len1,
      len1=len1,
      burDep1=burDep,
      len2=len1,
      burDep2=burDep)));
end FiniteLineSource_Integrand_Length;


Test case for steady-state solution of the finite line source. This example demonstrates the use of the function for the evaluation of the
steady-state finite line source solution. The solution is evaluated at different
distances between the emitting and receiving boreholes.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model FiniteLineSource_SteadyState
  "Test case for steady-state solution of the finite line source"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Height len1=150.0 "Length of emitting source";
  parameter Modelica.Units.SI.Height burDep1=4.0
    "Buried depth of emitting source";
  parameter Modelica.Units.SI.Height len2=125.0
    "Length of receiving line";
  parameter Modelica.Units.SI.Height burDep2=3.5
    "Buried depth of receiving line";

  Modelica.Units.SI.Distance dis "Radial distance";
  Real hRea "Finite line source solution (Real part)";
  Real hMir "Finite line source solution (Mirror part)";
  Real h "Finite line source solution";

protected
  constant Real uniCon(unit="m/s") = 1 "Constant to satisify unit check";
equation

  dis = 0.075 + uniCon*time;

  hRea = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_SteadyState(
    dis = dis,
    len1 = len1,
    burDep1 = burDep1,
    len2 = len2,
    burDep2 = burDep2,
    includeMirrorSource=false);
  hMir = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_SteadyState(
    dis = dis,
    len1 = len1,
    burDep1 = burDep1,
    len2 = len2,
    burDep2 = burDep2,
    includeRealSource=false);
  h = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.finiteLineSource_SteadyState(
    dis = dis,
    len1 = len1,
    burDep1 = burDep1,
    len2 = len2,
    burDep2 = burDep2);
end FiniteLineSource_SteadyState;


g-Function calculation for a field of 10 by 10 boreholes. This example checks the implementation of functions that evaluate the
g-function of a borefield of 100 boreholes in a 10 by 10 configuration.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model GFunction_100boreholes
  "g-Function calculation for a field of 10 by 10 boreholes"
  extends Modelica.Icons.Example;

  parameter Integer nBor = 100 "Number of boreholes";
  parameter Modelica.Units.SI.Position cooBor[nBor,2]={{7.5*mod(i - 1, 10),7.5*
      floor((i - 1)/10)} for i in 1:nBor} "Coordinates of boreholes";
  parameter Modelica.Units.SI.Height hBor=150 "Borehole length";
  parameter Modelica.Units.SI.Height dBor=4 "Borehole buried depth";
  parameter Modelica.Units.SI.Radius rBor=0.075 "Borehole radius";
  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=1e-6
    "Ground thermal diffusivity used in g-function evaluation";
  parameter Integer nSeg = 12 "Number of line source segments per borehole";
  parameter Integer nTimSho = 26 "Number of time steps in short time region";
  parameter Integer nTimLon = 50 "Number of time steps in long time region";
  parameter Real ttsMax = exp(5) "Maximum non-dimensional time for g-function calculation";

  final parameter Integer nTimTot=nTimSho+nTimLon;
  final parameter Real[nTimTot] gFun(each fixed=false);
  final parameter Real[nTimTot] lntts(each fixed=false);
  final parameter Modelica.Units.SI.Time[nTimTot] tGFun(each fixed=false);
  final parameter Real[nTimTot] dspline(each fixed=false);

  parameter Integer nClu=5 "Number of clusters to be generated";
  parameter Integer labels[nBor](each fixed=false) "Cluster label associated with each data point";
  parameter Integer cluSiz[nClu](each fixed=false) "Size of the clusters";

  Real gFun_int "Interpolated value of g-function";
  Real lntts_int "Non-dimensional logarithmic time for interpolation";

  discrete Integer k "Current interpolation interval";
  discrete Modelica.Units.SI.Time t1 "Previous value of time for interpolation";
  discrete Modelica.Units.SI.Time t2 "Next value of time for interpolation";
  discrete Real gFun1 "Previous g-function value for interpolation";
  discrete Real gFun2 "Next g-function value for interpolation";
  parameter Modelica.Units.SI.Time ts=hBor^2/(9*aSoi)
    "Bore field characteristic time";

initial equation
  // Evaluate g-function for the specified bore field configuration
  (labels, cluSiz) = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.clusterBoreholes(
    nBor = nBor,
    cooBor = cooBor,
    hBor = hBor,
    dBor = dBor,
    rBor = rBor,
    nClu = nClu);
  (tGFun,gFun) =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.gFunction(
      nBor = nBor,
      cooBor = cooBor,
      hBor = hBor,
      dBor = dBor,
      rBor = rBor,
      aSoi = aSoi,
      nSeg = nSeg,
      nTimSho = nTimSho,
      nTimLon = nTimLon,
      ttsMax = ttsMax,
      nClu = nClu,
      labels = labels,
      cluSiz = cluSiz);
  lntts = log(tGFun/ts .+ Modelica.Constants.small);
  // Initialize parameters for interpolation
  dspline = AixLib.Utilities.Math.Functions.splineDerivatives(
    x = tGFun,
    y = gFun);
  k = 1;
  t1 = tGFun[1];
  t2 = tGFun[2];
  gFun1 = gFun[1];
  gFun2 = gFun[2];

equation
  // Dimensionless logarithmic time
  lntts_int = log(AixLib.Utilities.Math.Functions.smoothMax(time, 1e-6, 2e-6)/ts);
  // Interpolate g-function
  gFun_int = AixLib.Utilities.Math.Functions.cubicHermiteLinearExtrapolation(
    x = time,
    x1 = t1,
    x2 = t2,
    y1 = gFun1,
    y2 = gFun2,
    y1d = dspline[pre(k)],
    y2d = dspline[pre(k)+1]);
  // Update interpolation parameters, when needed
  when time >= pre(t2) then
    k = min(pre(k) + 1, nTimTot);
    t1 = tGFun[k];
    t2 = tGFun[k+1];
    gFun1 = gFun[k];
    gFun2 = gFun[k+1];
  end when;
end GFunction_100boreholes;


g-Function calculation for a field of 1 borehole. This example checks the implementation of functions that evaluate the
g-function of a borefield of 100 boreholes in a 1 configuration.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model GFunction_1borehole_5meters
  "g-Function calculation for a field of 1 borehole"
  extends Modelica.Icons.Example;

  parameter Integer nBor = 1 "Number of boreholes";
  parameter Modelica.Units.SI.Position cooBor[nBor,2]={{5.*mod(i - 1, 3),5.*
      floor((i - 1)/3)} for i in 1:nBor} "Coordinates of boreholes";
  parameter Modelica.Units.SI.Height hBor=5 "Borehole length";
  parameter Modelica.Units.SI.Height dBor=1 "Borehole buried depth";
  parameter Modelica.Units.SI.Radius rBor=0.075 "Borehole radius";
  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=1e-6
    "Ground thermal diffusivity used in g-function evaluation";
  parameter Integer nSeg = 12 "Number of line source segments per borehole";
  parameter Integer nTimSho = 26 "Number of time steps in short time region";
  parameter Integer nTimLon = 50 "Number of time steps in long time region";
  parameter Real ttsMax = exp(5) "Maximum non-dimensional time for g-function calculation";

  final parameter Integer nTimTot=nTimSho+nTimLon;
  final parameter Real[nTimTot] gFun(each fixed=false);
  final parameter Real[nTimTot] lntts(each fixed=false);
  final parameter Modelica.Units.SI.Time[nTimTot] tGFun(each fixed=false);
  final parameter Real[nTimTot] dspline(each fixed=false);

  parameter Integer nClu=1 "Number of clusters to be generated";
  parameter Integer labels[nBor](each fixed=false) "Cluster label associated with each data point";
  parameter Integer cluSiz[nClu](each fixed=false) "Size of the clusters";

  Real gFun_int "Interpolated value of g-function";
  Real lntts_int "Non-dimensional logarithmic time for interpolation";

  discrete Integer k "Current interpolation interval";
  discrete Modelica.Units.SI.Time t1 "Previous value of time for interpolation";
  discrete Modelica.Units.SI.Time t2 "Next value of time for interpolation";
  discrete Real gFun1 "Previous g-function value for interpolation";
  discrete Real gFun2 "Next g-function value for interpolation";
  parameter Modelica.Units.SI.Time ts=hBor^2/(9*aSoi)
    "Bore field characteristic time";

initial equation
  // Evaluate g-function for the specified bore field configuration
  (labels, cluSiz) = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.clusterBoreholes(
    nBor = nBor,
    cooBor = cooBor,
    hBor = hBor,
    dBor = dBor,
    rBor = rBor,
    nClu = nClu);
  (tGFun,gFun) =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.gFunction(
      nBor = nBor,
      cooBor = cooBor,
      hBor = hBor,
      dBor = dBor,
      rBor = rBor,
      aSoi = aSoi,
      nSeg = nSeg,
      nTimSho = nTimSho,
      nTimLon = nTimLon,
      ttsMax = ttsMax,
      nClu = nClu,
      labels = labels,
      cluSiz = cluSiz);
  lntts = log(tGFun/ts .+ Modelica.Constants.small);
  // Initialize parameters for interpolation
  dspline = AixLib.Utilities.Math.Functions.splineDerivatives(
    x = tGFun,
    y = gFun);
  k = 1;
  t1 = tGFun[1];
  t2 = tGFun[2];
  gFun1 = gFun[1];
  gFun2 = gFun[2];

equation
  // Dimensionless logarithmic time
  lntts_int = log(AixLib.Utilities.Math.Functions.smoothMax(time, 1e-6, 2e-6)/ts);
  // Interpolate g-function
  gFun_int = AixLib.Utilities.Math.Functions.cubicHermiteLinearExtrapolation(
    x = time,
    x1 = t1,
    x2 = t2,
    y1 = gFun1,
    y2 = gFun2,
    y1d = dspline[pre(k)],
    y2d = dspline[pre(k)+1]);
  // Update interpolation parameters, when needed
  when time >= pre(t2) then
    k = min(pre(k) + 1, nTimTot);
    t1 = tGFun[k];
    t2 = tGFun[k+1];
    gFun1 = gFun[k];
    gFun2 = gFun[k+1];
  end when;
end GFunction_1borehole_5meters;


g-Function calculation for the small scale validation case. This example checks the implementation of functions that evaluate the
g-function of the borehole used in the small-scale experiment of Cimmino and
Bernier (2015).

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model GFunction_SmallScaleValidation
  "g-Function calculation for the small scale validation case"
  extends Modelica.Icons.Example;

  parameter AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Validation.BaseClasses.SmallScale_Borefield borFieDat
    "Record of borehole configuration data";
  parameter Integer nBor = borFieDat.conDat.nBor "Number of boreholes";
  parameter Modelica.Units.SI.Position cooBor[nBor,2]=borFieDat.conDat.cooBor
    "Coordinates of boreholes";
  parameter Modelica.Units.SI.Height hBor=borFieDat.conDat.hBor
    "Borehole length";
  parameter Modelica.Units.SI.Height dBor=borFieDat.conDat.dBor
    "Borehole buried depth";
  parameter Modelica.Units.SI.Radius rBor=borFieDat.conDat.rBor
    "Borehole radius";
  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=borFieDat.soiDat.kSoi/(
      borFieDat.soiDat.dSoi*borFieDat.soiDat.cSoi)
    "Ground thermal diffusivity used in g-function evaluation";
  parameter Integer nSeg = 12 "Number of line source segments per borehole";
  parameter Integer nTimSho = 26 "Number of time steps in short time region";
  parameter Integer nTimLon = 50 "Number of time steps in long time region";
  parameter Real ttsMax = exp(5) "Maximum non-dimensional time for g-function calculation";

  final parameter Integer nTimTot=nTimSho+nTimLon;
  final parameter Real[nTimTot] gFun(each fixed=false);
  final parameter Real[nTimTot] lntts(each fixed=false);
  final parameter Modelica.Units.SI.Time[nTimTot] tGFun(each fixed=false);
  final parameter Real[nTimTot] dspline(each fixed=false);

  Real gFun_int "Interpolated value of g-function";
  Real lntts_int "Non-dimensional logarithmic time for interpolation";

  parameter Integer nClu=1 "Number of clusters to be generated";
  parameter Integer labels[nBor](each fixed=false) "Cluster label associated with each data point";
  parameter Integer cluSiz[nClu](each fixed=false) "Size of the clusters";

  discrete Integer k "Current interpolation interval";
  discrete Modelica.Units.SI.Time t1 "Previous value of time for interpolation";
  discrete Modelica.Units.SI.Time t2 "Next value of time for interpolation";
  discrete Real gFun1 "Previous g-function value for interpolation";
  discrete Real gFun2 "Next g-function value for interpolation";
  parameter Modelica.Units.SI.Time ts=hBor^2/(9*aSoi)
    "Bore field characteristic time";

initial equation
  // Evaluate g-function for the specified bore field configuration
  (labels, cluSiz) = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.clusterBoreholes(
    nBor = nBor,
    cooBor = cooBor,
    hBor = hBor,
    dBor = dBor,
    rBor = rBor,
    nClu = nClu);
  (tGFun,gFun) =
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.gFunction(
      nBor = nBor,
      cooBor = cooBor,
      hBor = hBor,
      dBor = dBor,
      rBor = rBor,
      aSoi = aSoi,
      nSeg = nSeg,
      nTimSho = nTimSho,
      nTimLon = nTimLon,
      ttsMax = ttsMax,
      nClu = nClu,
      labels = labels,
      cluSiz = cluSiz);
  lntts = log(tGFun/ts .+ Modelica.Constants.small);
  // Initialize parameters for interpolation
  dspline = AixLib.Utilities.Math.Functions.splineDerivatives(
    x = tGFun,
    y = gFun);
  k = 1;
  t1 = tGFun[1];
  t2 = tGFun[2];
  gFun1 = gFun[1];
  gFun2 = gFun[2];

equation
  // Dimensionless logarithmic time
  lntts_int = log(AixLib.Utilities.Math.Functions.smoothMax(time, 1e-6, 2e-6)/ts);
  // Interpolate g-function
  gFun_int = AixLib.Utilities.Math.Functions.cubicHermiteLinearExtrapolation(
    x = time,
    x1 = t1,
    x2 = t2,
    y1 = gFun1,
    y2 = gFun2,
    y1d = dspline[pre(k)],
    y2d = dspline[pre(k)+1]);
  // Update interpolation parameters, when needed
  when time >= pre(t2) then
    k = min(pre(k) + 1, nTimTot);
    t1 = tGFun[k];
    t2 = tGFun[k+1];
    gFun1 = gFun[k];
    gFun2 = gFun[k+1];
  end when;
end GFunction_SmallScaleValidation;


Test case for infinite line source. This example demonstrates the use of the function for the evaluation of the
infinite line source solution.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model InfiniteLineSource "Test case for infinite line source"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.ThermalDiffusivity aSoi=1.0e-6
    "Ground thermal diffusivity";
  parameter Modelica.Units.SI.Radius rSource=0.075 "Minimum radius";
  parameter Modelica.Units.SI.Radius[5] r={rSource,2*rSource,5*rSource,10*
      rSource,20*rSource} "Radial position of evaluation of the solution";
  Modelica.Units.SI.Time t "Time";
  Real[5] E "Infinite line source solution";

equation

  t = exp(time) - 1.0;

  for k in 1:5 loop
    E[k] = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.infiniteLineSource(t, aSoi, r[k]);
  end for;
end InfiniteLineSource;


within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors;
package Validation "Example models for ThermalResponseFactors"
extends Modelica.Icons.ExamplesPackage;
end Validation;


Verifies the SHA-1 encryption of a single borehole. This example uses a typical single borehole to test the SHA1-encryption of the
arguments required to determine the borehole's thermal response factor.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model ShaGFunction
  "Verifies the SHA-1 encryption of a single borehole"
  extends Modelica.Icons.Example;

  //Input
  parameter String strIn=
    AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.shaGFunction(
    1,
    {{0,0}},
    150,
    4,
    0.075,
    1e-6,
    12,
    1,
    26,
    50,
    exp(5)) "SHA1-encrypted g-function inputs";

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

  //Comparison result
  Boolean cmp "Comparison result";

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


Test case for geometric expansion of time vector. This example demonstrates the construction of vector of geometrically expanding
time values.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.Validation;
model TimeGeometric
  "Test case for geometric expansion of time vector"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Duration dt=2.0 "Minimum time step";
  parameter Modelica.Units.SI.Time t_max=20.0 "Maximum value of time";
  parameter Integer nTim = 5 "Number of time values";

  final parameter Modelica.Units.SI.Time[nTim] t(each fixed=false)
    "Time vector";

initial equation
  t = AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.timeGeometric(
    dt=dt,
    t_max=t_max,
    nTim=nTim);
end TimeGeometric;


Long term validation of ground temperature response model. This validation case applies the asymetrical synthetic load profile developed
by Pinel (2003) over a 20 year period by directly injecting the heat at the
borehole wall in the ground temperature response model. The difference between
the resulting borehole wall temperature and the same temperature precalculated
by using a fast Fourier transform is calculated with the add
component. The fast Fourier transform calculation was done using the same
g-function as was calculated by

AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.ThermalResponseFactors.gFunction.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Validation;
model Analytic_20Years
  "Long term validation of ground temperature response model"
  extends Modelica.Icons.Example;

  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Template
    borFieDat(conDat(
      borCon=Types.BoreholeConfiguration.SingleUTube,
      nBor=1,
      cooBor={{0,0}},
      mBor_flow_nominal=0.3,
      dp_nominal=5e4,
      hBor=100,
      rBor=0.05,
      dBor=4,
      rTub=0.02,
      kTub=0.5,
      eTub=0.002,
      xC=0.05),
    soiDat(
      kSoi=1,
      cSoi=1,
      dSoi=1e6),
    filDat(
      kFil=0,
      cFil=Modelica.Constants.small,
      dFil=Modelica.Constants.small,
      steadyState=true))
      "Borefield parameters"

  AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.GroundTemperatureResponse groTemRes(
    tLoaAgg=3600,
    nCel=5,
    nSeg=12,
    borFieDat=borFieDat,
    forceGFunCalc=true) "Ground temperature response of borehole"

  Modelica.Blocks.Sources.CombiTimeTable timTabQ(
    tableOnFile=true,
    tableName="tab1",
    columns={2},
    smoothness=Modelica.Blocks.Types.Smoothness.ConstantSegments,
    fileName=Modelica.Utilities.Files.loadResource(
      "modelica://AixLib/Resources/Data/Fluid/Geothermal/Borefields/HeatTransfer/Validation/Analytic_20Years.txt"))
        "Table for heat injected, using constant segments"

  Modelica.Blocks.Math.Add add(
    y(unit="K"), k1=-1)
    "Difference between FFT method and ground temperature response model"
  Modelica.Blocks.Sources.CombiTimeTable timTabT(
    tableOnFile=true,
    tableName="tab1",
    columns={3},
    smoothness=Modelica.Blocks.Types.Smoothness.LinearSegments,
    fileName=Modelica.Utilities.Files.loadResource(
      "modelica://AixLib/Resources/Data/Fluid/Geothermal/Borefields/HeatTransfer/Validation/Analytic_20Years.txt"),
    y(each unit="K",
      each displayUnit="degC"))
      "Table for resulting wall temperature using FFT and linearly interpolated"

  Modelica.Blocks.Sources.Constant const(k=273.15)
    "Cosntant ground temperature"
  Modelica.Blocks.Math.Add TBorWal(y(unit="K")) "Borewall temperature"
equation

  connect(TBorWal.u2, const.y)
  connect(groTemRes.delTBor, TBorWal.u1)
  connect(add.u1, TBorWal.y)
  connect(add.u2, timTabT.y[1])
  connect(groTemRes.QBor_flow, timTabQ.y[1])
end Analytic_20Years;


Comparison of the Cylindrical with the GroundTemperatureResponse. This example demonstrates the use of

AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Cylindrical.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Validation;
model Cylindrical
  "Comparison of the Cylindrical with the GroundTemperatureResponse"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Temperature T_start=295.15
    "Initial soil temperature";
  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Template borFieDat=
    AixLib.Fluid.Geothermal.Borefields.Validation.BaseClasses.SandBox_Borefield()

  AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Cylindrical soi(
    final steadyStateInitial=false,
    final soiDat=borFieDat.soiDat,
    final h=1,
    final r_a=borFieDat.conDat.rBor,
    final r_b=3,
    final TInt_start=T_start,
    final TExt_start=T_start) "Heat conduction in the soil"
  Modelica.Thermal.HeatTransfer.Sources.FixedTemperature preTem(T=T_start)
    "Prescribed temperature"
  Modelica.Blocks.Sources.Step heaFlo(
    offset=0,
    startTime=1000,
    height=1056/18.3) "Heat flow to soil"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow to soil"
equation
  connect(soi.port_b, preTem.port)
  connect(heaFlo.y, preHeaFlo.Q_flow)
  connect(preHeaFlo.port, soi.port_a)
end Cylindrical;


Short term validation of ground temperature response model. This example validates the implementation of

AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.GroundTemperatureResponse
for the evaluation of the borehole wall temperature at a short time scale.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Validation;
model FiniteDifference_1Week
  "Short term validation of ground temperature response model"
  extends Modelica.Icons.Example;

  parameter Modelica.Units.SI.Temperature T_start=283.15
    "Initial soil temperature";
  AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Cylindrical soi(
    final steadyStateInitial=false,
    final soiDat=borFieDat.soiDat,
    final h=borFieDat.conDat.hBor,
    final r_a=borFieDat.conDat.rBor,
    final r_b=3,
    final TInt_start=T_start,
    final TExt_start=T_start,
    gridFac=1.2,
    final nSta=50) "Heat conduction in the soil"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedHeatFlow preHeaFlo
    "Prescribed heat flow to soil for the discretized model"

  parameter AixLib.Fluid.Geothermal.Borefields.Data.Borefield.Template borFieDat(
    soiDat(
      kSoi=1,
      cSoi=1,
      dSoi=1e6),
    filDat(
      kFil=0,
      cFil=Modelica.Constants.small,
      dFil=Modelica.Constants.small,
      steadyState=true),
    conDat(
      borCon=Types.BoreholeConfiguration.SingleUTube,
      nBor=1,
      cooBor={{0,0}},
      mBor_flow_nominal=0.3,
      dp_nominal=5e4,
      hBor=1e6,
      rBor=0.05,
      dBor=4,
      rTub=0.02,
      kTub=0.5,
      eTub=0.002,
      xC=0.05))
      "Borefield parameters"

  Modelica.Blocks.Sources.Sine sine(
    f=1/(24*3600),
    startTime=21600,
    amplitude=1e8) "Heat flow signal"
  AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.GroundTemperatureResponse groTemRes(
    nSeg=12,
    borFieDat=borFieDat,
    nCel=5,
    tLoaAgg=30) "Heat conduction in the soil"
  Modelica.Blocks.Sources.Constant groTem(k=T_start)
    "Ground temperature signal"
  Modelica.Thermal.HeatTransfer.Sources.PrescribedTemperature preTem
    "Ground temperature for discretized model"
  Modelica.Blocks.Math.Add deltaT(
    k2=-1,
    y(unit="K"))
    "Temperature difference between borehole with discrete ground and borehole with analytical ground"
  Modelica.Thermal.HeatTransfer.Sensors.TemperatureSensor temSenDis
    "Borehole wall temperature sensor for the discretized model"
  Modelica.Blocks.Math.Add TSoiAna(y(unit="K"))
    "Ground temperature from ground response model"
equation
  connect(preHeaFlo.port, soi.port_a)
  connect(sine.y, preHeaFlo.Q_flow)
  connect(preTem.port, soi.port_b)
  connect(preTem.T, groTem.y)
  connect(temSenDis.T, deltaT.u1)
  connect(soi.port_a, temSenDis.port)

  connect(groTemRes.delTBor, TSoiAna.u1)
  connect(groTem.y, TSoiAna.u2)
  connect(deltaT.u2, TSoiAna.y)
  connect(groTemRes.QBor_flow, sine.y)
end FiniteDifference_1Week;


Long term validation of ground temperature response model using the small scale experiment of Cimmino and Bernier (2015). This validation case simulates the experiment of Cimmino and Bernier (2015).
The experiment consists in the injection of heat at an average rate of 8.67 W
in a 0.40 m long borehole over a period of 168 h. Borehole wall tempratures
were measured by a series of 22 thermocouples welded to the stainless steel
pipe that contains the borehole and acts as the borehole wall.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Validation;
model Measured_SmallScale
  "Long term validation of ground temperature response model using the small scale experiment of Cimmino and Bernier (2015)"
  extends Modelica.Icons.Example;

  parameter Real sizFac=375.0 "Scaling factor of the experiment";
  parameter AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Validation.BaseClasses.SmallScale_Borefield
    borFieDat "Borefield parameters"

  AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.GroundTemperatureResponse groTemRes(
    nCel=5,
    nSeg=12,
    borFieDat=borFieDat,
    forceGFunCalc=true,
    tLoaAgg=360000) "Ground temperature response of borehole"

  Modelica.Blocks.Sources.CombiTimeTable meaDat(
    tableOnFile=true,
    timeScale=sizFac^2,
    fileName=Modelica.Utilities.Files.loadResource(
      "modelica://AixLib/Resources/Data/Fluid/Geothermal/Borefields/HeatTransfer/Validation/Cimmino_Bernier_2015_SmallScale.txt"),
    columns={2,3,4,5,6,7,8,9},
    tableName="data",
    offset={0,0,0,273.15,273.15,273.15,273.15,273.15})
                     "Measurement data"

  Modelica.Blocks.Math.Add TBorHol(y(unit="K")) "Borehole temperature"

  Modelica.Blocks.Math.Gain scaFac(k=sizFac)
    "Scaling factor of the experiment"
  Modelica.Blocks.Math.Add add(k2=-1)
    "Difference between experiment data and ground temperature response model"
equation

  connect(meaDat.y[3], scaFac.u)
  connect(add.u1, meaDat.y[4])
  connect(add.u2, TBorHol.y)
  connect(TBorHol.u2, meaDat.y[8])
  connect(TBorHol.u1, groTemRes.delTBor)
  connect(groTemRes.QBor_flow, scaFac.y)
end Measured_SmallScale;


within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer;
package Validation "Validation models for GroundHeatTransfer"
  extends Modelica.Icons.ExamplesPackage;
end Validation;


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


Configuration data record for the Cimmino and Bernier (2015) experiment. This record contains the configuration data of the Cimmino and
Bernier (2015) experiment. Since the model is not adapted to the simulation of
small scale boreholes, the borehole dimensions are multiplied by a factor 375
to obtain a scaled-up 150.0 m long borehole.

within AixLib.Fluid.Geothermal.Borefields.BaseClasses.HeatTransfer.Validation.BaseClasses;
record SmallScale_Configuration
  "Configuration data record for the Cimmino and Bernier (2015) experiment"
  extends
    AixLib.Fluid.Geothermal.Borefields.Data.Configuration.Template(
      borCon = Types.BoreholeConfiguration.SingleUTube,
      nBor=1,
      cooBor={{0,0}},
      mBor_flow_nominal=0.0303/60,
      dp_nominal=5e4,
      hBor=0.4*375,
      rBor=0.00629*375,
      dBor=0.019*375,
      rTub=0.125*0.0254/2*375,
      kTub=401.0,
      eTub=0.06*0.0254*375,
      xC=0.0050/2*375);
end SmallScale_Configuration;


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


Collection of borefield data records

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


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


Collection of records of filling data

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


Collection of records of soil data

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


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

  package Medium = AixLib.Media.Water;

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

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

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

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


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


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

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

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

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

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

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

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

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

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

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

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

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


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

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

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


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


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

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

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

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

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


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


Heat exchanger with constant effectiveness. Model for a heat exchanger with constant effectiveness.

within AixLib.Fluid.HeatExchangers;
model ConstantEffectiveness "Heat exchanger with constant effectiveness"
  extends AixLib.Fluid.HeatExchangers.BaseClasses.PartialEffectiveness(
    sensibleOnly1 = true,
    sensibleOnly2 = true,
    final prescribedHeatFlowRate1=true,
    final prescribedHeatFlowRate2=true,
    Q1_flow = eps * QMax_flow,
    Q2_flow = -Q1_flow,
    mWat1_flow = 0,
    mWat2_flow = 0);

  parameter Modelica.Units.SI.Efficiency eps(max=1) = 0.8
    "Heat exchanger effectiveness";
end ConstantEffectiveness;


Heat exchanger with effectiveness - NTU relation and no moisture condensation. Model of a coil without humidity condensation.
This model transfers heat in the amount of

within AixLib.Fluid.HeatExchangers;
model DryCoilEffectivenessNTU
  "Heat exchanger with effectiveness - NTU relation and no moisture condensation"
  extends AixLib.Fluid.HeatExchangers.BaseClasses.PartialEffectivenessNTU(
    UA = 1/(1/hA.hA_1 + 1/hA.hA_2));

  parameter Real r_nominal(
    min=0,
    max=1) = 2/3
    "Ratio between air-side and water-side convective heat transfer (hA-value) at nominal condition";

  AixLib.Fluid.HeatExchangers.BaseClasses.HADryCoil hA(
    final r_nominal=r_nominal,
    final UA_nominal=UA_nominal,
    final m_flow_nominal_w=m1_flow_nominal,
    final m_flow_nominal_a=m2_flow_nominal,
    waterSideTemperatureDependent=false,
    airSideTemperatureDependent=false)
    "Model for convective heat transfer coefficient";

equation
  // Convective heat transfer coefficient
  hA.m1_flow = m1_flow;
  hA.m2_flow = m2_flow;
  hA.T_1 = T_in1;
  hA.T_2 = T_in2;
end DryCoilEffectivenessNTU;


Simple dynamic heat exchanger model. This is a simple dynamic heat exchanger with a heat capacity and
  convection. The heat exchanger is discretized in n elements and the
  volume elements are replaceable.

within AixLib.Fluid.HeatExchangers;
model DynamicHX "Simple dynamic heat exchanger model"
  extends Interfaces.FourPortHeatExchanger;

  parameter Modelica.Units.SI.Time tau_C=10
    "Time constant of heat capacity at nominal heat flow and temperature difference."
  parameter Modelica.Units.SI.TemperatureDifference dT_nom
    "Temperature difference at nominal conditions (used to calculate Gc)"
  Modelica.Thermal.HeatTransfer.Components.HeatCapacitor heatCapacitor[nNodes](
    each final C=Q_nom/dT_nom*tau_C/nNodes,
    each final T(fixed=true, start=TCapacity_start))
  parameter Modelica.Units.SI.HeatFlowRate Q_nom "Temperature difference at nominal conditions (used to calculate Gc)"
  parameter Modelica.Units.SI.Temperature TCapacity_start=(T1_start + T2_start)/2
    "Start value of temperature"
  parameter Modelica.Units.SI.ThermalConductance Gc1 = Q_nom/dT_nom*2/nNodes
    "Signal representing the convective thermal conductance in [W/K]"
  parameter Modelica.Units.SI.ThermalConductance Gc2 = Q_nom/dT_nom*2/nNodes
    "Signal representing the convective thermal conductance in [W/K]"
  Modelica.Blocks.Sources.RealExpression Gc1_Expression[nNodes](
  each final y=Gc1)
  Modelica.Thermal.HeatTransfer.Components.Convection convection1[nNodes]
  Modelica.Blocks.Sources.RealExpression Gc2_Expression[nNodes](
  each final y=Gc2)
  Modelica.Thermal.HeatTransfer.Components.Convection convection2[nNodes]
equation
  connect(convection2.solid, vol2.heatPort)

  connect(convection1[nNodes:-1:1].solid, vol1[1:nNodes].heatPort)

  connect(convection1.fluid, heatCapacitor.port)
  connect(convection2.fluid, heatCapacitor.port)
  connect(Gc1_Expression.y, convection1.Gc)
  connect(Gc2_Expression.y, convection2.Gc)
end DynamicHX;