The only requirement is julia, preferably 1.9.
julia --project=.
]
to enter Pkg mode. The REPL prompt should look like so
(Simulation) pkg>
(Simulation) pkg> instantiate
using Simulation
julia> include("test/runtests.jl")
We make the following assumptions and simplifications
@kwdef struct SWHSModel{T}
Ns::Int= 10 # stratifications count for tank
Nc::Int= 100 # number of points in the discretized mesh of collector
ρp::T = 8.0e3 # density of plate
ρf::T = 1.0e3 # working fluid density
ρe::T = 1.5e3 # effective density (tank + wokring fluid)
δ::T = 0.1 # plate thickness
W::T = 1.0 # plate width
L::T = 2.0 # plate length
H::T = 10.0 # height of tank
ΔH::T = H/Ns # height of each stratified layer
Δy::T = L/Nc
dt::T = 5.0 # tank diameter
dr::T = 0.1 # riser pipe diameter
dd::T = dr # down comer pipe diameter
kp::T = 50.0 # thermal conductivity of plate
Cpp::T = 450.0 # specific heat capacity of plate
Cpf::T = 4.2e3 # specific heat capacity of working fluid
Cpe::T = 5000.0 # effective specific heat of tank + working fluid
S::T = 800.0 # radiation flux
hpf::T = 1000.0 # heat transfer coefficient (plate and working fluid)
hpa::T = 100.0 # heat transfer coefficient (plate and atmosphere)
hra::T = 100.0 # heat transfer coefficient (riser pipe and ambient)
hda::T = hra # heat transfer coefficient (down pipe and ambient)
hta::T = 500.0 # loss coefficient (storage tank)
Ta::T = 300.0 # ambient temperature
T∞::T = 295.0 # sky temperature
α::T = 5.5e-8 # radiation coefficient
Af::T = W*L*0.2 # transverse cross section area pipes in collector
mdot::T = 0.5 * Af * ρf # mass flow rate (assuming 0.5m per s in collector pipes
A::T = π*dt*ΔH # ambient contact area per stratified layer
V::T = ΔH*π*(dt^2)/4 # tank volume per stratified layer
ρCl::T = 1.0e3
mCr::T = 1.0e3
mCd::T = mCr
...
end
We consider a flat plate collector with parallel riser tubes modelled as an absorber plate transfering heat to the fluid in the risers. This model is based on the work of [4]. Heat transfer between plate and fluid modelled as convection using Newton's law of cooling [1].
We have the following processes
$$\rhop \delta C{pp}\frac{\partial T_p}{\partial t} = S + \delta k_p \frac{\partial^2 Tp}{\partial y^2}- h{pf}(T_p - Tf) - h{pa}(T_p - T_a) - \alpha(Tp^4 - T{\text{sky}}^4)$$
where $\alpha$ is a constant that includes the Stefan-Boltzmann constant, emissivities etc. This equation equation models heat transfer rate per unit area of the collector. Since we assume no dynamics along the width of the collector, we multiply the equation by the width $W$ of the collector to get the effective dynamics along the length of the conductor ($T_p = T_p(t, y)$).
$$W\rhop \delta C{pp}\frac{\partial Tp}{\partial t} = WS - Wh{pf}(T_p - Tf) - Wh{pa}(T_p - T_a) - W\alpha(Tp^4 - T{\text{sky}}^4)$$
Note that we only need the width factor to get the source term for the 1D advection equation used for simulating the working fluid in the collector.
$$\frac{\partial T_p}{\partial t} = \frac{1}{\rhop \delta C{pp}}\left( S + \delta k_p \frac{\partial^2 Tp}{\partial y^2}- h{pf}(T_p - Tf) - h{pa}(T_p - T_a) - \alpha(Tp^4 - T{\text{sky}}^4) \right)$$
We use Neumann boundary conditions:
$$\frac{\partial T_p}{\partial T_y} = 0\quad \text{at }y = 0, y = L$$
We model heat transfer through and by the fluid using a 1D advection equation with the source term being the plate-fluid convective transfer term.
$$\rho_fAf C{pf}\frac{\partial T_f}{\partial t} + \rho_f Af C{pf} v_f\frac{\partial Tf}{\partial y} = Wh{pf}(T_p - T_f),$$
where $A_f$ is the transverse flow cross section. Note that mass conservation requires $\rho_f A_f v_f = \dot m$ to be constant.
$$\rho_f Ac C{pf}\frac{\partial Tf}{\partial t} + \dot{m} C{pf}\frac{\partial T_f}{\partial y} = Wh_pf(T_p - T_f),$$ $$\implies \frac{\partial T_f}{\partial t} = \frac{1}{\rho_f Ac C{pf}}\left(W h_{pf}(T_p - Tf) - \dot m C{pf} \frac{\partial T_p}{\partial y}\right)$$
We use a stratified, well-mixed tank model as presented in [3,7].
(\rho C)_l V \frac{dT_{l,i}}{dt} = \dot{m} C_p(T_{l,i-1} - T_{l,i}) - h_{ta}A(T_{l,i} - T_a)
\implies \frac{dT_{l,i}}{dt} = \frac{1}{(\rho C)_l V}\left(\dot{m} C_p(T_{l,i-1} - T_{l,i}) - h_{ta}A(T_{l,i} - T_a)\right)
for $i \in 1 \ldots N_s$, where $Ns$ is the number of stratification layers, (model parameter). We let $T{l,0}(t) = T_r(t)$, where $T_t$ is the temperature of the up-riser pipe node connecting the collector outlet to the tank inlet (more on the connecting pipes later). Note that $T_l$ is the temperature of the same working fluid as in the collector, but we use a different subscript to distinguish between the two systems. This model is based on lecture 29 of [3], and a more detailed model is developed in [7]. We can write the system of equations for the storage tank in vector form
\dot{\overline{T}}_l = \frac{1}{(\rho C)_l}(D_l \overline{T}_l + d_l) - h_{ta}A(\overline T_l - T_a)
where
D_l\overline{T}_l + d_l = \begin{bmatrix}
-1 \\
1 & -1\\
& 1 & -1\\
& & 1 & -1\\
& & & &\ddots\\
& & & & 1 & -1\\
& & & & & 1 & -1
\end{bmatrix}
\begin{bmatrix}
T_{l,1}\\
T_{l,2}\\
T_{l,3}\\
T_{l,4}\\
\vdots\\
T_{l,N_s-1}\\
T_{l,N_s}
\end{bmatrix} +
\begin{bmatrix}
T_r\\
0\\
0\\
0\\
\vdots\\
0\\
0
\end{bmatrix}
We assume that the temperature drop across the connecting pipes is small and model them as single nodes between the collector and the tank, similar to as done in (TODO: cite). Denote $\text{d}$ as the down comer pipe that leads flow into the collector, and $\text{r}$ as the up-riser pipe leading from the collector to the tank. $Tr = T{l,0}$, and $T_d$ will be used in a similar manner with the collector when discretizing the equations.
(mC)_r\frac{dT_r}{dt} = \dot m C_pf(T_f(L) - T_r) - \pi d_r h_{ra}(T_r - T_a)
(mC)_d\frac{dT_d}{dt} = \dot m C_pf(T_{l,N_s} - T_d) - \pi d_dh_{da}(T_d - T_a)
\frac{\partial T}{\partial y} \approx \frac{T_{i+1} - T_{i-1}}{2\Delta y} \quad \forall i \in 1,\ldots N_c
where $T_{i} = T(t, i\Delta y)$, and $N_c$ is the number of degrees of freedom in the discretized mesh. We also need to take into account the boundary conditions.
Consider first $T_p$.
\begin{bmatrix}
T'_{p,1}\\
T_{p,2}'\\
T_{p,3}'\\
\vdots\\
T_{p,N_c-1}'\\
T_{p,N_c}'\end{bmatrix} \approx \frac{1}{2\Delta y}\begin{bmatrix}
0 & 0 & 0 & & &\\
-1 & 0 & 1 & & &\\
& -1 & 0 & 1 & &\\
& & & \ddots & &\\
& & & -1 & 0 & 1\\
& & & 0 & 0 & 0
\end{bmatrix}
\begin{bmatrix}
T_{p,1}\\
T_{p,2}\\
T_{p,3}\\
\vdots\\
T_{p,N_c-1}\\
T_{p,N_c}
\end{bmatrix}
Where the prime $'$ represents the spatial derivative, and the top and bottom rows are zero because of the Neumann conditions on the plate boundaries. Call this matrix $D_{p1}$ and write the discrete derivative as
\overline{T'}_p = D_{p1} \overline{T}_p
We need the second derivative for the conductive term as well.
\begin{bmatrix}
T_{p,1}''\\
T_{p,2}''\\
T_{p,3}''\\
\vdots\\
T_{p,N_c-1}''\\
T_{p,N_c}''
\end{bmatrix} \approx \frac{1}{\Delta y ^2}
\begin{bmatrix}
-2 & 2 & 0\\
1 & -2 & 1\\
& 1 & -2 & 1\\
& & & \ddots &\\
& & & & 1 & -2 & 1\\
& & & & & 2 & -2
\end{bmatrix}
\begin{bmatrix}
T_{p,1}\\
T_{p,2}\\
T_{p,3}\\
\vdots\\
T_{p,N_c-1}\\
T_{p,N_c}
\end{bmatrix}
Where we estimated the second derivative at the boundaries using the Neumann conditions.
\frac{T_{p,N_c+1} - T_{p,N_c-1}}{2\Delta y} = 0 \implies T_{p,N_c+1} = T_{p,N_c-1}
\implies T''_{p,N_c} \approx \frac{T_{p,N_c+1} + T_{p,N_c-1} - 2T_{p,N_c}}{\Delta y ^2} = \frac{2T_{p,N_c-1} - 2T_{p,N_c}}{\Delta y^2}
and similarly for $T''_{p,1}$.
Denote this matrix as $D_{p2}$ and use this compact form for discretized second derivative.
\overline{T''}_p = D_{p2}T_p
Putting everything together, we get the following system of ODEs for the discretized plate subsystem.
\dot{\overline{T}_p} = \frac{1}{\rho_p \delta C_{pp}}\left( S + \delta k_p D_{p2}\overline{T}_p - h_{pf}(\overline T_p - \overline T_f) - h_{pa}(\overline T_p - T_a) - \alpha(\lVert \overline T_p \rVert^4 - T_{\text{sky}}^4) \right)
We can take $T_{f,0} = Td$ and $T{f,N_c+1} = T_r$ and use the central difference approximation as usual to get
\begin{bmatrix}
T'_{f,1}\\
T'_{f,2}\\
T'_{f,3}\\
\vdots\\
T'_{f,N_c-1}\\
T'_{f,N_c}
\end{bmatrix} \approx \frac{1}{2\Delta y}
\begin{bmatrix}
0 & 1\\
-1 & 0 & 1\\
& -1 & 0 & 1\\
& & & \ddots\\
& & & & -1 & 0 & 1\\
& & & & & -1 & 0\\
\end{bmatrix}
\begin{bmatrix}
T_{f,1}\\
T_{f,2}\\
T_{f,3}\\
\vdots\\
T_{f,N_c-1}\\
T_{f,N_c}
\end{bmatrix}
+
\frac{1}{2\Delta y}
\begin{bmatrix}
-T_d\\
0\\
0\\
\vdots\\
0\\
T_r
\end{bmatrix}
Again, compact notation
\overline{T}_f' = D_f\overline{T}_f + d_f
So that
\dot{\overline{T}}_f = \frac{1}{\rho_f A_c C_{pf}}\left(W h_{pf}(\overline{T}_p - \overline T_f) - \dot m C_{pf} (D_f \overline T_f + d_f)\right)
\dot{\overline{T}_p} = \frac{1}{\rho_p \delta C_{pp}}\left( S + \delta k_p D_{p2}\overline{T}_p - h_{pf}(\overline T_p - \overline T_f) - h_{pa}(\overline T_p - T_a) - \alpha(\lVert \overline T_p \rVert^4 - T_{\text{sky}}^4) \right)
\dot{\overline{T}}_f = \frac{1}{\rho_f A_c C_{pf}}\left(W h_{pf}(\overline{T}_p - \overline T_f) - \dot m C_{pf} (D_f \overline T_f + d_f)\right)
\dot{\overline{T}}_l = \frac{1}{(\rho C)_l}\left((D_l \overline{T}_l + d_l) - h_{ta}A(\overline T_l - T_a)\right)
(mC)_r\frac{dT_r}{dt} = \dot m(T_f(L) - T_r) - \pi d_r h_{ra}(T_r - T_a)
(mC)_d\frac{dT_d}{dt} = \dot m (T_{l,N_s} - T_d) - \pi d_dh_{da}(T_d - T_a)
The implementation can found here in the source.
We set ourselves up for contiguous memory accesses.
\begin{bmatrix}
\overline{T}_p\\
T_d\\
\overline{T}_f\\
T_r\\
\overline{T}_l
\end{bmatrix}
If we use an array/tensor programming language/library with GPU support (JuliaGPU, Halide, ArrayFire, TensorFlow etc), GPU acceleration should be simple to incorporate).
Once we have the final system of equations, we should be able to plug it into ODE and even steady-state solvers of our choice. We implement a simple fourth order explicit Runge-Kutta method.
function runge_kutta_4(f!, tspan, dt, prob::SWHSProblem)
@unpack u, du = prob
@unpack cache = prob
T = eltype(u)
k1 = zeros(T, length(u))
k2 = zeros(T, length(u))
k3 = zeros(T, length(u))
k4 = zeros(T, length(u))
t = first(tspan)
while t < last(tspan)
f!(k1, u, cache)
f!(k2, u .+ dt .* k1 ./ 2, cache)
f!(k3, u .+ dt .* k2 ./ 2, cache)
f!(k4, u .+ dt .* k3, cache)
@. u += dt/6 * (k1 + 2k2 + 2k3 + k4)
t += dt
end
u
end