Nodes of different colours represent the following:
Solid arrows point from a parent (sub)module to the submodule which is descended from it. Dashed arrows point from a module being used to the module or program unit using it. Where possible, edges connecting nodes are given different colours to make them easier to distinguish in large graphs.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
integer, | intent(in) | :: | WaterThermalTankNum |
Nodes of different colours represent the following:
Solid arrows point from a procedure to one which it calls. Dashed arrows point from an interface to procedures which implement that interface. This could include the module procedures in a generic interface or the implementation in a submodule of an interface in a parent module. Where possible, edges connecting nodes are given different colours to make them easier to distinguish in large graphs.
Nodes of different colours represent the following:
Solid arrows point from a procedure to one which it calls. Dashed arrows point from an interface to procedures which implement that interface. This could include the module procedures in a generic interface or the implementation in a submodule of an interface in a parent module. Where possible, edges connecting nodes are given different colours to make them easier to distinguish in large graphs.
SUBROUTINE SetupStratifiedNodes(WaterThermalTankNum)
! SUBROUTINE INFORMATION:
! AUTHOR Peter Graham Ellis
! DATE WRITTEN January 2007
! MODIFIED na
! RE-ENGINEERED na
! PURPOSE OF THIS SUBROUTINE:
! Sets up node properties based on the tank shape, i.e., vertical cylinder, horizontal cylinder, or other.
! Node height, skin area, vertical conduction area, and loss coefficients are calculated and assigned.
! Heating elements, parasitics, and fluid inlet and outlet flows are assigned according to node height.
! METHODOLOGY EMPLOYED:
! Tank is divided into nodes of equal mass. For horizontal cylinders, node heights are calculated using
! the Newton-Raphson iterative method. For vertical cylinders and other shapes, the node heights are calculated
! using basic geometry.
! USE STATEMENTS:
USE DataGlobals, ONLY: Pi
USE FluidProperties, ONLY: GetDensityGlycol
IMPLICIT NONE ! Enforce explicit typing of all variables in this routine
! SUBROUTINE ARGUMENT DEFINITIONS:
INTEGER, INTENT(IN) :: WaterThermalTankNum ! Water Heater being simulated
! SUBROUTINE PARAMETER DEFINITIONS:
REAL(r64) :: Tolerance = 1.0d-8 ! Tolerance for Newton-Raphson solution
REAL(r64) :: FluidCond = 0.6d0 ! Conductivity of water (W/m-K)
! SUBROUTINE LOCAL VARIABLE DECLARATIONS:
INTEGER :: NumNodes ! Number of stratified nodes
INTEGER :: NodeNum ! Node number index
REAL(r64) :: NodeMass ! Mass of one node (kg)
REAL(r64) :: EndArea ! Circular area of one end of the cylinder (m2)
REAL(r64) :: CrossArea ! Cross sectional area (for horizontal cylinders) (m2)
REAL(r64) :: NodeEndArea ! Area of the node at the end of the horizontal cylinder (m2)
REAL(r64) :: NodeHeight ! Height of one node (m)
REAL(r64) :: ApproxEndArea ! End area approximated by Newton-Raphson iteration (m2)
REAL(r64) :: CondCoeff ! Coefficient for vertical conduction between nodes (W/K)
REAL(r64) :: Radius ! Radius of the tank (m)
REAL(r64) :: Perimeter ! Perimeter of the tank (m)
REAL(r64) :: SkinArea ! Area of skin exposed to ambient environment (m2)
REAL(r64) :: ChordLength ! Chord length for horizontal tanks (m)
REAL(r64) :: TankHeight ! Dimension in the vertical direction; for horizontal tanks it is radius * 2 (m)
REAL(r64) :: TankLength ! For horizontal tanks, it is the length in the axial direction (m)
REAL(r64) :: R ! Radius (m)
REAL(r64) :: H0 ! Starting height (m)
REAL(r64) :: H ! Ending height (m)
REAL(r64) :: a, b, c ! Intermediate variables
REAL(r64) :: a0, b0, c0 ! Intermediate variables
REAL(r64) :: G ! Function that should converge to zero for the Newton-Raphson solution
REAL(r64) :: rho ! local fluid density (kg/m3)
INTEGER :: DummyWaterIndex = 1
! FLOW:
NumNodes = WaterThermalTank(WaterThermalTankNum)%Nodes
ALLOCATE(WaterThermalTank(WaterThermalTankNum)%Node(NumNodes))
IF (( WaterThermalTank(WaterThermalTankNum)%UseSidePlantLoopNum > 0) .AND. Allocated(PlantLoop)) THEN
rho = GetDensityGlycol(PlantLoop(WaterThermalTank(WaterThermalTankNum)%UseSidePlantLoopNum)%FluidName, &
InitConvTemp, &
PlantLoop(WaterThermalTank(WaterThermalTankNum)%UseSidePlantLoopNum)%FluidIndex, &
'GetWaterThermalTankInput')
ELSE
rho = GetDensityGlycol('WATER', InitConvTemp, DummyWaterIndex, 'GetWaterThermalTankInput')
ENDIF
NodeMass = WaterThermalTank(WaterThermalTankNum)%Volume * rho / NumNodes
! Mixing rate set to 50% of the max value for dt = 1.0
WaterThermalTank(WaterThermalTankNum)%InversionMixingRate = NodeMass * 0.5d0 * 1.0d0
IF ((WaterThermalTank(WaterThermalTankNum)%Shape == TankShapeVertCylinder) &
.OR. (WaterThermalTank(WaterThermalTankNum)%Shape == TankShapeOther)) THEN
TankHeight = WaterThermalTank(WaterThermalTankNum)%Height
EndArea = WaterThermalTank(WaterThermalTankNum)%Volume / TankHeight
NodeHeight = TankHeight / NumNodes
CondCoeff = (FluidCond + WaterThermalTank(WaterThermalTankNum)%AdditionalCond)* EndArea / NodeHeight
IF (WaterThermalTank(WaterThermalTankNum)%Shape == TankShapeVertCylinder) THEN
Radius = SQRT(EndArea / Pi)
Perimeter = 2.0d0 * Pi * Radius
ELSE ! TankShapeOther
Perimeter = WaterThermalTank(WaterThermalTankNum)%Perimeter
END IF
DO NodeNum = 1, NumNodes
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Mass = NodeMass
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Volume = WaterThermalTank(WaterThermalTankNum)%Volume / NumNodes
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Height = NodeHeight
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%CondCoeffUp = CondCoeff
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%CondCoeffDn = CondCoeff
IF ((NodeNum == 1) .OR. (NodeNum == NumNodes)) THEN
SkinArea = Perimeter * NodeHeight + EndArea
ELSE
SkinArea = Perimeter * NodeHeight
END IF
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OnCycLossCoeff = &
WaterThermalTank(WaterThermalTankNum)%SkinLossCoeff * SkinArea + &
WaterThermalTank(WaterThermalTankNum)%AdditionalLossCoeff(NodeNum)
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OffCycLossCoeff = &
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OnCycLossCoeff + &
WaterThermalTank(WaterThermalTankNum)%OffCycFlueLossCoeff
END DO ! NodeNum
WaterThermalTank(WaterThermalTankNum)%Node(1)%CondCoeffUp = 0.0d0
WaterThermalTank(WaterThermalTankNum)%Node(NumNodes)%CondCoeffDn = 0.0d0
ELSE ! WaterThermalTank(WaterThermalTankNum)%Shape == TankShapeHorizCylinder
TankLength = WaterThermalTank(WaterThermalTankNum)%Height ! Height is the length in the axial direction
EndArea = WaterThermalTank(WaterThermalTankNum)%Volume / TankLength
Radius = SQRT(EndArea / Pi)
TankHeight = 2.0d0 * Radius ! Actual vertical height
NodeEndArea = EndArea / NumNodes
R = Radius
H0 = 0.0d0
ChordLength = 0.0d0
DO NodeNum = 1, NumNodes
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Mass = NodeMass
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Volume = WaterThermalTank(WaterThermalTankNum)%Volume / NumNodes
IF (NodeNum == NumNodes) THEN
H = TankHeight
ELSE
! Use the Newton-Raphson method to solve the nonlinear algebraic equation for node height
H = H0 + TankHeight / NumNodes ! Initial guess
DO WHILE (.TRUE.)
a = SQRT(H)
b = SQRT(2.0d0 * R - H)
c = 2.0d0 * R * R * ATAN(a / b) - (2.0d0 * R * R - 3.0d0 * H * R + H * H) * (a / b)
IF (H0 > 0.0d0) THEN
a0 = SQRT(H0)
b0 = SQRT(2.0d0 * R - H0)
c0 = 2.0d0 * R * R * ATAN(a0 / b0) - (2.0d0 * R * R - 3.0d0 * H0 * R + H0 * H0) * (a0 / b0)
ELSE
c0 = 0.0d0
END IF
ApproxEndArea = c - c0 ! Area approximated by iteration
G = ApproxEndArea - NodeEndArea ! G is the function that should converge to zero
IF (ABS(G) < Tolerance) THEN
EXIT ! Converged !!!
ELSE
H = H - G / (2.0d0 * a * b) ! Calculate next guess: H = Hprev - G/G'
END IF
END DO ! Newton-Raphson
END IF
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Height = H - H0
IF (NodeNum > 1) THEN
CrossArea = 2.0d0 * ChordLength * TankLength ! Use old ChordLength from previous node
CondCoeff = (FluidCond + WaterThermalTank(WaterThermalTankNum)%AdditionalCond)* CrossArea &
/ (0.5d0 * (H - H0) + 0.5d0 * WaterThermalTank(WaterThermalTankNum)%Node(NodeNum - 1)%Height)
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum - 1)%CondCoeffUp = CondCoeff ! Set for previous node
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%CondCoeffDn = CondCoeff ! Set for this node
END IF
ChordLength = SQRT(2.0d0 * R * H - H * H) ! Calc new ChordLength to be used with next node
Perimeter = 2.0d0 * R * (ACOS((R - H)/ R) - ACOS((R - H0)/ R)) ! Segments of circular perimeter
SkinArea = Perimeter * TankLength + 2.0d0 * NodeEndArea
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OnCycLossCoeff = &
WaterThermalTank(WaterThermalTankNum)%SkinLossCoeff * SkinArea + &
WaterThermalTank(WaterThermalTankNum)%AdditionalLossCoeff(NodeNum)
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OffCycLossCoeff = &
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OnCycLossCoeff + &
WaterThermalTank(WaterThermalTankNum)%OffCycFlueLossCoeff
! Although it doesn't make much sense to have a flue in a horizontal tank, keep it in anyway
H0 = H
END DO ! NodeNum
WaterThermalTank(WaterThermalTankNum)%Node(1)%CondCoeffUp = 0.0d0
WaterThermalTank(WaterThermalTankNum)%Node(NumNodes)%CondCoeffDn = 0.0d0
END IF
! Loop through nodes again (from top to bottom this time) and assign heating elements, parasitics, flow inlets/outlets
! according to their vertical heights in the tank.
H0 = TankHeight
DO NodeNum = 1, NumNodes
IF (NodeNum == NumNodes) THEN
H = -1.0d0 ! Avoids rounding errors and ensures that anything at height 0.0 goes into the bottom node
ELSE
H = H0 - WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Height
END IF
! Assign heater elements to the nodes at the specified heights
IF ((WaterThermalTank(WaterThermalTankNum)%HeaterHeight1 <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%HeaterHeight1 > H)) THEN
! sensor node will not get set if user enters 0 for this heater capacity
! (WaterThermalTank(WaterThermalTankNum)%MaxCapacity > 0.0d0)) THEN
WaterThermalTank(WaterThermalTankNum)%HeaterNode1 = NodeNum
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%MaxCapacity = WaterThermalTank(WaterThermalTankNum)%MaxCapacity
END IF
IF ((WaterThermalTank(WaterThermalTankNum)%HeaterHeight2 <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%HeaterHeight2 > H)) THEN
! sensor node will not get set if user enters 0 for this heater capacity
! .AND. (WaterThermalTank(WaterThermalTankNum)%MaxCapacity2 > 0.0d0)) THEN
WaterThermalTank(WaterThermalTankNum)%HeaterNode2 = NodeNum
IF ((NodeNum == WaterThermalTank(WaterThermalTankNum)%HeaterNode1) &
.AND. (WaterThermalTank(WaterThermalTankNum)%ControlType == PrioritySimultaneous)) THEN
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%MaxCapacity = &
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%MaxCapacity &
+ WaterThermalTank(WaterThermalTankNum)%MaxCapacity2
ELSE
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%MaxCapacity = WaterThermalTank(WaterThermalTankNum)%MaxCapacity2
END IF
END IF
! Assign parasitic heat gains to the nodes at the specified heights
IF ((WaterThermalTank(WaterThermalTankNum)%OffCycParaHeight <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%OffCycParaHeight > H) ) THEN
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OffCycParaLoad = &
WaterThermalTank(WaterThermalTankNum)%OffCycParaFracToTank * WaterThermalTank(WaterThermalTankNum)%OffCycParaLoad
END IF
IF ((WaterThermalTank(WaterThermalTankNum)%OnCycParaHeight <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%OnCycParaHeight > H) ) THEN
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%OnCycParaLoad = &
WaterThermalTank(WaterThermalTankNum)%OnCycParaFracToTank * WaterThermalTank(WaterThermalTankNum)%OnCycParaLoad
END IF
! Assign inlets and outlets to the nodes at the specified heights
IF ((WaterThermalTank(WaterThermalTankNum)%UseInletHeight <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%UseInletHeight > H)) THEN
WaterThermalTank(WaterThermalTankNum)%UseInletStratNode = NodeNum
IF ((WaterThermalTank(WaterThermalTankNum)%UseInletNode > 0) .OR. &
(WaterThermalTank(WaterThermalTankNum)%MassFlowRateMax > 0.0d0)) &
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Inlets = &
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Inlets + 1
END IF
IF ((WaterThermalTank(WaterThermalTankNum)%UseOutletHeight <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%UseOutletHeight > H)) THEN
WaterThermalTank(WaterThermalTankNum)%UseOutletStratNode = NodeNum
IF ((WaterThermalTank(WaterThermalTankNum)%UseOutletNode > 0) .OR. &
(WaterThermalTank(WaterThermalTankNum)%MassFlowRateMax > 0.0d0)) &
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Outlets = &
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Outlets + 1
END IF
IF ((WaterThermalTank(WaterThermalTankNum)%SourceInletHeight <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%SourceInletHeight > H) &
.AND. (WaterThermalTank(WaterThermalTankNum)%SourceInletNode > 0)) THEN
WaterThermalTank(WaterThermalTankNum)%SourceInletStratNode = NodeNum
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Inlets = WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Inlets + 1
END IF
IF ((WaterThermalTank(WaterThermalTankNum)%SourceOutletHeight <= H0) .AND. &
(WaterThermalTank(WaterThermalTankNum)%SourceOutletHeight > H) &
.AND. (WaterThermalTank(WaterThermalTankNum)%SourceOutletNode > 0)) THEN
WaterThermalTank(WaterThermalTankNum)%SourceOutletStratNode = NodeNum
WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Outlets = WaterThermalTank(WaterThermalTankNum)%Node(NodeNum)%Outlets + 1
END IF
H0 = H
END DO ! NodeNum
RETURN
END SUBROUTINE SetupStratifiedNodes