Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses

Base classes used in Buildings.Fluid.Geothermal.Borefields.TOUGH

Information

Package with base classes for geothermal borefields with the ground response calculated by the simulator TOUGH.

Extends from Modelica.Icons.BasesPackage (Icon for packages containing base classes).

Package Content

Name Description
Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.GroundResponse GroundResponse Ground response calculated by the TOUGH simulator
Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.PartialBorefield PartialBorefield Borefield model using single U-tube borehole heat exchanger configuration.Calculates the average fluid temperature T_fts of the borefield for a given (time dependent) load Q_flow
Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Real_Real Real_Real Block that exchanges a vector of real values with a Python function
Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Boreholes Boreholes Package with borehole heat exchangers
Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Validation Validation  

Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.GroundResponse Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.GroundResponse

Ground response calculated by the TOUGH simulator

Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.GroundResponse

Information

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.

The instance pyt finds the ground response with the TOUGH simulator through the Python interface Buildings.Utilities.IO.Python_3_12.Real_Real. See Buildings.Fluid.Geothermal.Borefields.TOUGH.UsersGuide for instructions.

Parameters

TypeNameDefaultDescription
HeighthBor Total height of the borehole [m]
IntegernSeg Total number of segments
IntegernInt Number of points in the ground to be investigated
IntegernTouSeg Total number of grids along the entire borehole in the TOUGH mesh
StringtouWorDir TOUGH working directory name
Sampling
TimesamplePeriod Sample period of component [s]

Connectors

TypeNameDescription
input RealInputQBor_flow[nSeg]Heat flow from boreholes (positive if heat from fluid into soil) [W]
input RealInputTBorWal_start[nSeg]Initial borehole outer wall temperature at the beginning of the simulation [K]
input RealInputTOutOutdoor air temperature [K]
output RealOutputTBorWal[nSeg]Temperature of current borehole wall temperature [K]
output RealOutputpInt[nInt]Pressure of the interested points in the ground
output RealOutputxInt[nInt]Saturation of the interested points in the ground
output RealOutputTInt[nInt]Temperature at the interested points in the ground [K]

Modelica definition

model GroundResponse "Ground response calculated by the TOUGH simulator" parameter Modelica.Units.SI.Height hBor "Total height of the borehole"; parameter Integer nSeg "Total number of segments"; parameter Integer nInt "Number of points in the ground to be investigated"; parameter Integer nTouSeg "Total number of grids along the entire borehole in the TOUGH mesh"; parameter String touWorDir "TOUGH working directory name"; parameter Modelica.Units.SI.Time samplePeriod "Sample period of component"; final parameter Real startTime(fixed=false) "Simulation start time"; Modelica.Blocks.Interfaces.RealInput QBor_flow[nSeg]( each final unit="W") "Heat flow from boreholes (positive if heat from fluid into soil)"; Modelica.Blocks.Interfaces.RealInput TBorWal_start[nSeg]( each final unit="K", each displayUnit="degC", each quantity="ThermodynamicTemperature") "Initial borehole outer wall temperature at the beginning of the simulation"; Modelica.Blocks.Interfaces.RealInput TOut( final unit="K", displayUnit="degC", quantity="ThermodynamicTemperature") "Outdoor air temperature"; Modelica.Blocks.Interfaces.RealOutput TBorWal[nSeg]( each final unit="K", each displayUnit="degC", each quantity="ThermodynamicTemperature") "Temperature of current borehole wall temperature"; Modelica.Blocks.Interfaces.RealOutput pInt[nInt] "Pressure of the interested points in the ground"; Modelica.Blocks.Interfaces.RealOutput xInt[nInt] "Saturation of the interested points in the ground"; Modelica.Blocks.Interfaces.RealOutput TInt[nInt]( each final unit="K", each displayUnit="degC", each quantity="ThermodynamicTemperature") "Temperature at the interested points in the ground"; Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Real_Real pyt( final startTime=startTime, final moduleName="GroundResponse", final functionName="doStep", final nDblRea=nSeg+3*nInt, final nDblWri=2*nSeg + 6, final samplePeriod=samplePeriod, final strWri={touWorDir}, final flag=0, final passPythonObject=true) "Python interface model to call TOUGH simulator"; Modelica.Blocks.Routing.Multiplex mul( final n=2*nSeg + 6) "Multiplex"; Buildings.Utilities.Time.ModelTime clock "Model time"; Buildings.Controls.OBC.CDL.Reals.Sources.Constant griNum[3]( final k={nSeg,nTouSeg,nInt}) "Total number of grids"; Buildings.Controls.OBC.CDL.Reals.Sources.Constant borHei(final k=hBor) "Total height of the borehole"; initial equation startTime = time; equation // Delete the TOUGH temporary working folder when {initial(), terminal()} then Modelica.Utilities.Files.remove(touWorDir); end when; connect(pyt.yR[1:nSeg], TBorWal); connect(mul.y, pyt.uR); connect(pyt.yR[nSeg + 1:nSeg + nInt], pInt); connect(pyt.yR[nSeg+nInt+1:nSeg+2*nInt], xInt); connect(pyt.yR[nSeg+2*nInt+1:nSeg+3*nInt], TInt); connect(griNum.y, mul.u[1:3]); connect(QBor_flow, mul.u[4:nSeg+3]); connect(TBorWal_start, mul.u[nSeg+4:2*nSeg+3]); connect(TOut, mul.u[2*nSeg+4]); connect(clock.y, mul.u[2*nSeg+5]); connect(borHei.y, mul.u[2*nSeg+6]); end GroundResponse;

Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.PartialBorefield Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.PartialBorefield

Borefield model using single U-tube borehole heat exchanger configuration.Calculates the average fluid temperature T_fts of the borefield for a given (time dependent) load Q_flow

Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.PartialBorefield

Information

This model simulates a borefield containing one or multiple boreholes using the parameters in the borFieDat record.

Heat transfer to the soil is modeled using only one borehole heat exchanger (To be added in an extended model). The fluid mass flow rate into the borehole is divided to reflect the per-borehole fluid mass flow rate. The borehole model calculates the dynamics within the borehole itself using an axial discretization and a resistance-capacitance network for the internal thermal resistances between the individual pipes and between each pipe and the borehole wall.

The thermal interaction between the borehole wall and the surrounding soil is modeled using Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.GroundResponse.

Extends from Buildings.Fluid.Interfaces.PartialTwoPortInterface (Partial model with two ports and declaration of quantities that are used by many models), Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters (Parameters for flow resistance for models with two ports).

Parameters

TypeNameDefaultDescription
replaceable package MediumPartialMediumMedium in the component
TemplateborFieDat Borefield data
PartialBoreholeborHolredeclare Buildings.Fluid.Ge...Borehole
Nominal condition
MassFlowRatem_flow_nominalborFieDat.conDat.mBorFie_flo...Nominal mass flow rate [kg/s]
PressureDifferencedp_nominalborFieDat.conDat.dp_nominalPressure difference [Pa]
Ground response
IntegernTouSeg Total number of grids along the entire borehole in the TOUGH mesh
IntegernSeg Number of segments to use in vertical discretization of the boreholes
IntegernInt Number of points in the ground to be investigated
TimesamplePeriod Sample period of component [s]
StringtouWorDir TOUGH working directory name
Assumptions
BooleanallowFlowReversaltrue= false to simplify equations, assuming, but not enforcing, no flow reversal
Advanced
MassFlowRatem_flow_small1E-4*abs(m_flow_nominal)Small mass flow rate for regularization of zero flow [kg/s]
Diagnostics
Booleanshow_Tfalse= true, if actual temperature at port is computed
Flow resistance
BooleancomputeFlowResistance(borFieDat.conDat.dp_nominal...=true, compute flow resistance. Set to false to assume no friction
Booleanfrom_dpfalse= true, use m_flow = f(dp) else dp = f(m_flow)
Realn2Flow exponent, n=1 for laminar, n=2 for turbulent
BooleanlinearizeFlowResistancefalse= true, use linear relation between m_flow and dp for any flow rate
RealdeltaM0.1Fraction of nominal flow rate where flow transitions to laminar
Dynamics
Equations
DynamicsenergyDynamicsModelica.Fluid.Types.Dynamic...Type of energy balance: dynamic (3 initialization options) or steady state
Initialization
AbsolutePressurep_startMedium.p_defaultStart value of pressure [Pa]
TemperatureTFlu_start[nSeg]TGro_startStart value of fluid temperature [K]
Soil
TemperatureTExt0_start283.15Initial far field temperature [K]
TemperatureTExt_start[nSeg]{if z[i] >= z0 then TExt0_st...Temperature of the undisturbed ground [K]
Filling material
TemperatureTGro_start[nSeg]TExt_startStart value of grout temperature [K]
Temperature profile
Heightz010Depth below which the temperature gradient starts [m]
RealdT_dz0.01Vertical temperature gradient of the undisturbed soil for h below z0 [K/m]

Connectors

TypeNameDescription
FluidPort_aport_aFluid connector a (positive design flow direction is from port_a to port_b)
FluidPort_bport_bFluid connector b (positive design flow direction is from port_a to port_b)
replaceable package MediumMedium in the component
output RealOutputTBorAveAverage borehole wall temperature in the borefield [K]
input RealInputTOutOutdoor air temperature [K]

Modelica definition

partial model PartialBorefield "Borefield model using single U-tube borehole heat exchanger configuration.Calculates the average fluid temperature T_fts of the borefield for a given (time dependent) load Q_flow" extends Buildings.Fluid.Interfaces.PartialTwoPortInterface( final m_flow_nominal=borFieDat.conDat.mBorFie_flow_nominal); extends Buildings.Fluid.Interfaces.TwoPortFlowResistanceParameters( final dp_nominal=borFieDat.conDat.dp_nominal, final computeFlowResistance=(borFieDat.conDat.dp_nominal > Modelica.Constants.eps)); replaceable package Medium = Modelica.Media.Interfaces.PartialMedium "Medium in the component"; constant Real mSenFac(min=1)=1 "Factor for scaling the sensible thermal mass of the volume"; // Assumptions parameter Modelica.Fluid.Types.Dynamics energyDynamics=Modelica.Fluid.Types.Dynamics.DynamicFreeInitial "Type of energy balance: dynamic (3 initialization options) or steady state"; // Initialization parameter Medium.AbsolutePressure p_start = Medium.p_default "Start value of pressure"; // General parameters of borefield parameter Buildings.Fluid.Geothermal.Borefields.TOUGH.Data.Borefield.Template borFieDat "Borefield data"; // Temperature gradient in undisturbed soil parameter Modelica.Units.SI.Temperature TExt0_start=283.15 "Initial far field temperature"; parameter Modelica.Units.SI.Temperature TExt_start[nSeg]={if z[i] >= z0 then TExt0_start + (z[i] - z0)*dT_dz else TExt0_start for i in 1:nSeg} "Temperature of the undisturbed ground"; parameter Modelica.Units.SI.Temperature TGro_start[nSeg]=TExt_start "Start value of grout temperature"; parameter Modelica.Units.SI.Temperature TFlu_start[nSeg]=TGro_start "Start value of fluid temperature"; parameter Modelica.Units.SI.Height z0=10 "Depth below which the temperature gradient starts"; parameter Real dT_dz( min=0, final unit="K/m")=0.01 "Vertical temperature gradient of the undisturbed soil for h below z0"; parameter Integer nTouSeg "Total number of grids along the entire borehole in the TOUGH mesh"; parameter Integer nSeg(min=1) "Number of segments to use in vertical discretization of the boreholes"; parameter Integer nInt "Number of points in the ground to be investigated"; parameter Modelica.Units.SI.Time samplePeriod "Sample period of component"; parameter String touWorDir "TOUGH working directory name"; Modelica.Blocks.Interfaces.RealOutput TBorAve( final quantity="ThermodynamicTemperature", final unit="K", displayUnit = "degC", start=TExt0_start) "Average borehole wall temperature in the borefield"; replaceable Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Boreholes.BaseClasses.PartialBorehole borHol constrainedby Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Boreholes.BaseClasses.PartialBorehole ( redeclare final package Medium = Medium, final borFieDat=borFieDat, final nSeg=nSeg, final m_flow_nominal=m_flow_nominal/borFieDat.conDat.nBor, final dp_nominal=dp_nominal, final allowFlowReversal=allowFlowReversal, final m_flow_small=m_flow_small, final show_T=show_T, final computeFlowResistance=computeFlowResistance, final from_dp=from_dp, final n=n, final linearizeFlowResistance=linearizeFlowResistance, final deltaM=deltaM, final energyDynamics=energyDynamics, final p_start=p_start, final mSenFac=mSenFac, final TFlu_start=TFlu_start, final TGro_start=TGro_start) "Borehole"; Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.GroundResponse touRes( final hBor=borFieDat.conDat.hBor, final nSeg=nSeg, final nInt=nInt, final nTouSeg=nTouSeg, final touWorDir=touWorDir, final samplePeriod=samplePeriod) "Ground response calculated by TOUGH simulator"; Modelica.Blocks.Interfaces.RealInput TOut( final unit="K", displayUnit="degC", quantity="ThermodynamicTemperature") "Outdoor air temperature"; protected parameter Modelica.Units.SI.Height z[nSeg]={borFieDat.conDat.hBor/nSeg*(i - 0.5) for i in 1:nSeg} "Distance from the surface to the considered segment"; Buildings.Fluid.BaseClasses.MassFlowRateMultiplier masFloDiv( redeclare final package Medium = Medium, final k=borFieDat.conDat.nBor) "Division of flow rate"; Buildings.Fluid.BaseClasses.MassFlowRateMultiplier masFloMul( redeclare final package Medium = Medium, final k=borFieDat.conDat.nBor) "Mass flow multiplier"; Buildings.Utilities.Math.Average AveTBor(nin=nSeg) "Average temperature of all the borehole segments"; Modelica.Blocks.Sources.Constant TBorHol_start[nSeg]( final k=TExt_start, y(each unit="K", each displayUnit="degC")) "Borehole outer wall start temperature"; Modelica.Thermal.HeatTransfer.Sensors.HeatFlowSensor QBorHol[nSeg] "Heat flow rate of all segments of the borehole"; Buildings.HeatTransfer.Sources.PrescribedTemperature TemBorWal[nSeg] "Borewall temperature"; equation connect(masFloMul.port_b, port_b); connect(masFloDiv.port_b, port_a); connect(masFloDiv.port_a, borHol.port_a); connect(borHol.port_b, masFloMul.port_a); connect(QBorHol.port_a, borHol.port_wall); connect(QBorHol.port_b, TemBorWal.port); connect(AveTBor.y, TBorAve); connect(touRes.TBorWal, AveTBor.u); connect(touRes.TBorWal, TemBorWal.T); connect(TBorHol_start.y, touRes.TBorWal_start); connect(QBorHol.Q_flow, touRes.QBor_flow); connect(touRes.TOut, TOut); end PartialBorefield;

Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Real_Real Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Real_Real

Block that exchanges a vector of real values with a Python function

Buildings.Fluid.Geothermal.Borefields.TOUGH.BaseClasses.Real_Real

Information

Block that exchanges data with a Python function that does not need to pass an object from one call to the next.

For each element in the input vector uR[nDblWri], the value of the flag flaDblWri[nDblWri] determines whether the current value, the average over the sampling interval or the integral over the sampling interval is sent to Python. The following three options are allowed:


flaDblWri[i] Value sent to Python
0 Current value of uR[i]
1 Average value of uR[i] over the sampling interval
2 Integral of uR[i] over the sampling interval

If the function needs to pass an object from one invocation to the next, set passPythonObject = true. Otherwise, leave it at its default value passPythonObject = false.

Note that the model is same as Buildings.Utilities.IO.Python_3_12.Real_Real, except the model propagates the parameters for strings writing to Python.

Extends from Modelica.Blocks.Interfaces.DiscreteBlock (Base class of discrete control blocks).

Parameters

TypeNameDefaultDescription
TimesamplePeriod Sample period of component [s]
TimestartTime0First sample time instant [s]
StringmoduleName Name of the python module that contains the function
StringfunctionNamemoduleNameName of the python function
IntegernDblWri Number of double values to write to Python
IntegernDblRea Number of double values to be read from the Python
StringstrWri[max(1, nStrWri)]{""}String values to write
Integerflag0Flag for double values (0: use current value, 1: use average over interval, 2: use integral over interval)
BooleanpassPythonObjectfalseSet to true if the Python function returns and receives an object, see User's Guide

Connectors

TypeNameDescription
input RealInputuR[nDblWri]Real inputs to be sent to Python
output RealOutputyR[nDblRea]Real outputs received from Python

Modelica definition

model Real_Real "Block that exchanges a vector of real values with a Python function" extends Modelica.Blocks.Interfaces.DiscreteBlock( startTime=0, firstTrigger(fixed=true, start=false)); parameter String moduleName "Name of the python module that contains the function"; parameter String functionName=moduleName "Name of the python function"; parameter Integer nDblWri(min=1) "Number of double values to write to Python"; parameter Integer nDblRea(min=1) "Number of double values to be read from the Python"; final parameter Integer nStrWri(min=0)=1 "Number of strings to write to Python"; parameter String strWri[max(1, nStrWri)]={""} "String values to write"; parameter Integer flag = 0 "Flag for double values (0: use current value, 1: use average over interval, 2: use integral over interval)"; final parameter Integer flaDblWri[nDblWri] = fill(flag, nDblWri) "Flag for double values (0: use current value, 1: use average over interval, 2: use integral over interval)"; parameter Boolean passPythonObject = false "Set to true if the Python function returns and receives an object, see User's Guide"; Modelica.Blocks.Interfaces.RealInput uR[nDblWri] "Real inputs to be sent to Python"; Modelica.Blocks.Interfaces.RealOutput yR[nDblRea] "Real outputs received from Python"; Real uRInt[nDblWri] "Value of integral"; Real uRIntPre[nDblWri] "Value of integral at previous sampling instance"; Real uRWri[nDblWri] "Value to be sent to Python"; Real uRWri_temp[nDblWri] "Internal value"; protected Buildings.Utilities.IO.Python_3_12.Functions.BaseClasses.PythonObject pytObj= Buildings.Utilities.IO.Python_3_12.Functions.BaseClasses.PythonObject() "Python object, used to avoid instantiating Python in each call, and to pass python object if passPythonObject=true"; initial equation uRWri = pre(uR); uRWri_temp = pre(uR); uRInt = zeros(nDblWri); uRIntPre = zeros(nDblWri); for i in 1:nDblWri loop assert(flaDblWri[i]>=0 and flaDblWri[i]<=2, "Parameter flaDblWri out of range for " + String(i) + "-th component."); end for; // The assignment of yR avoids the warning // "initial conditions for variables of type Real are not fully specified". // At startTime, the sampleTrigger is true and hence this value will // be overwritten. yR = zeros(nDblRea); equation for i in 1:nDblWri loop der(uRInt[i]) = if (flaDblWri[i] > 0) then uR[i] else 0; end for; when {sampleTrigger} then // Compute value that will be sent to Python for i in 1:nDblWri loop if (flaDblWri[i] == 0) then uRWri_temp[i] = pre(uR[i]); uRWri[i] = pre(uR[i]); // Send the current value. else if (time > 0) then uRWri_temp[i] = uRInt[i] - pre(uRIntPre[i]); // Integral over the sampling interval if (flaDblWri[i] == 2) then uRWri[i] = uRWri_temp[i]; else uRWri[i] = uRWri_temp[i]/samplePeriod; // Average value over the sampling interval end if; else uRWri[i] = pre(uR[i]); uRWri_temp[i] = pre(uR[i]); end if; end if; end for; // Exchange data yR = Buildings.Utilities.IO.Python_3_12.Functions.exchange( moduleName=moduleName, functionName=functionName, dblWri=uRWri, intWri={0}, nDblWri=nDblWri, nDblRea=nDblRea, nIntWri=0, nIntRea=0, nStrWri=nStrWri, strWri=strWri, pytObj=pytObj, passPythonObject = passPythonObject); // Store current value of integral uRIntPre= uRInt; end when; end Real_Real;