Skip to main content Link Search Menu Expand Document (external link)

Phase Change

Table of contents
  1. Code
    1. Main program
    2. Aux. programs
  2. Output
    1. Text

Code

Main program

sp_05_03.m | View on GitHub
% Supplemental program 5.3

% -------------------------------------------------------------------------
% Use implicit formulation with "excess heat" or "apparent heat capacity"
% to solve for soil temperatures with phase change in comparison with
% Neumann's analytical solution.
% -------------------------------------------------------------------------

% --- Physical constants in physcon structure

physcon.tfrz = 273.15;             % Freezing point of water (K)
physcon.rhowat = 1000;             % Density of water (kg/m3)
physcon.rhoice = 917;              % Density of ice (kg/m3)
physcon.hfus = 0.3337e6;           % Heat of fusion for water at 0 C (J/kg)

% --- Model run control parameters

dt = 3600;                         % Time step (seconds)
nday = 60;                         % Number of days
%soilvar.method = 'excess-heat';            % Use excess heat for phase change
soilvar.method = 'apparent-heat-capacity'; % Use apparent heat capacity for phase change

% --- Initialize soil layer variables

% Number of layers in soil profile

soilvar.nsoi = 60;

% Soil layer thickness (m)

for i = 1:soilvar.nsoi
   soilvar.dz(i) = 0.10;
end

% Soil depth (m) at i+1/2 interface between layers i and i+1 (negative distance from surface)

soilvar.z_plus_onehalf(1) = -soilvar.dz(1);
for i = 2:soilvar.nsoi
   soilvar.z_plus_onehalf(i) = soilvar.z_plus_onehalf(i-1) - soilvar.dz(i);
end

% Soil depth (m) at center of layer i (negative distance from surface)

soilvar.z(1) = 0.5 * soilvar.z_plus_onehalf(1);
for i = 2:soilvar.nsoi
   soilvar.z(i) = 0.5 * (soilvar.z_plus_onehalf(i-1) + soilvar.z_plus_onehalf(i));
end

% Thickness between between z(i) and z(i+1)

for i = 1:soilvar.nsoi-1
   soilvar.dz_plus_onehalf(i) = soilvar.z(i) - soilvar.z(i+1);
end
soilvar.dz_plus_onehalf(soilvar.nsoi) = 0.5 * soilvar.dz(soilvar.nsoi);

% Initial soil temperature (K)

for i = 1:soilvar.nsoi
   soilvar.tsoi(i) = physcon.tfrz + 2;
end

% Initial unfrozen and frozen water (kg H2O/m2)

for i = 1:soilvar.nsoi
   if (soilvar.tsoi(i) > physcon.tfrz)
      soilvar.h2osoi_ice(i) = 0;
      soilvar.h2osoi_liq(i) = 0.187 * 1770 * soilvar.dz(i);
   else
      soilvar.h2osoi_liq(i) = 0;
      soilvar.h2osoi_ice(i) = 0.187 * 1770 * soilvar.dz(i);
   end
end

% Soil layers for output

k1 = 3;
k2 = 6;
k3 = 9;
k4 = 12;

% --- Time stepping loop to increment soil temperature

% Counter for output file

m = 1;

% Save initial data for output files

iday_out(m) = 0;
d0c_out(m) = 0;
z1_out(m) = -soilvar.z(k1) * 100;
tsoi1_out(m) = soilvar.tsoi(k1) - physcon.tfrz;
z2_out(m) = -soilvar.z(k2) * 100;
tsoi2_out(m) = soilvar.tsoi(k2) - physcon.tfrz;
z3_out(m) = -soilvar.z(k3) * 100;
tsoi3_out(m) = soilvar.tsoi(k3) - physcon.tfrz;
z4_out(m) = -soilvar.z(k4) * 100;
tsoi4_out(m) = soilvar.tsoi(k4) - physcon.tfrz;

% Main loop is NTIM iterations per day with a time step of DT seconds.
% This is repeated NDAY times.

ntim = round(86400/dt);
for iday = 1:nday
   for itim = 1:ntim

      tsurf = -10 + physcon.tfrz;

      % Thermal conductivity and heat capacity

      [soilvar] = soil_thermal_properties (physcon, soilvar);

      % Soil temperatures

      [soilvar] = soil_temperature (physcon, soilvar, tsurf, dt);

      % Calculate depth to freezing isotherm (0 C)

      d0c = 0; % Depth of 0 C isotherm (m)

      switch soilvar.method
         case 'excess-heat'
         num_z = 0;
         sum_z = 0;

         % Average depth of soil layers with TSOI = TFRZ

         for i = 1:soilvar.nsoi
            if (abs(soilvar.tsoi(i)-physcon.tfrz) < 1.e-03)
               % Number of soil layers for averaging
               num_z = num_z + 1;
               % Sum of soil layer depths for averaging
               sum_z = sum_z + soilvar.z(i);
            end
         end

         if (num_z > 0)
            d0c = sum_z / num_z;
         end

         case 'apparent-heat-capacity'
         % Linear interpolation between soil layers

         for i = 2:soilvar.nsoi
            if (soilvar.tsoi(i-1) <= physcon.tfrz & soilvar.tsoi(i) > physcon.tfrz)
               % slope for linear interpolation
               b = (soilvar.tsoi(i) - soilvar.tsoi(i-1)) / (soilvar.z(i) - soilvar.z(i-1));
               % intercept
               a = soilvar.tsoi(i) - b * soilvar.z(i);
               % depth of 0C isotherm
               d0c = (physcon.tfrz - a) / b;
            end
         end
      end

      % Save data for output files

      if (itim == ntim)
         m = m + 1;
         iday_out(m) = iday;
         d0c_out(m) = -d0c * 100;
         z1_out(m) = -soilvar.z(k1) * 100;
         tsoi1_out(m) = soilvar.tsoi(k1) - physcon.tfrz;
         z2_out(m) = -soilvar.z(k2) * 100;
         tsoi2_out(m) = soilvar.tsoi(k2) - physcon.tfrz;
         z3_out(m) = -soilvar.z(k3) * 100;
         tsoi3_out(m) = soilvar.tsoi(k3) - physcon.tfrz;
         z4_out(m) = -soilvar.z(k4) * 100;
         tsoi4_out(m) = soilvar.tsoi(k4) - physcon.tfrz;
      end

   end
end

A = [iday_out; d0c_out; z1_out; tsoi1_out; z2_out; tsoi2_out; z3_out; tsoi3_out; z4_out; tsoi4_out];
fileID = fopen('data_numerical.txt','w');
fprintf(fileID,'%10s %10s %10s %10s %10s %10s %10s %10s %10s %10s\n','day','z0C','z1','t1','z2','t2','z3','t3','z4','t4');
fprintf(fileID,'%10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f\n', A);
fclose(fileID);

% Analytical solution for Neumann problem (Lunardini 1981)

[dummy] = neumann;

Aux. programs

neumann.m | View on GitHub
function [dummy] = neumann

% --------------------------------------------------------
% Analytical solution for Neumann problem (Lunardini 1981)
% --------------------------------------------------------

% soil depths (meters)

nsoi = 4;
depth(1) = 0.25;
depth(2) = 0.55;
depth(3) = 0.85;
depth(4) = 1.15;

% number of days to simulate

ndays = 60;

% time-invariant surface temperature (deg C)

ts = -10;

% initial soil temperature (deg C)

for i = 1:nsoi
   t0(i) = 2;
end

% freezing temperature (deg C)

tf = 0;

% soil dependent constants
% a1 - frozen diffusivity (cm**2/hr -> m**2/s)
% a2 - unfrozen diffusivity (cm**2/hr -> m**2/s)
% rm - "m" from Jumikis (1966) and adjust for above unit conversion
% rg - "gamma" from Lunardini (1981)

a1 = 42.55 / 3600 / (100*100);
a2 = 23.39 / 3600 / (100*100);
rm = 3.6 / (60 * 100);
rg = rm / (2 * sqrt(a1));

% save output for day zero (i.e., initial conditions)

m = 1;
iday_out(m) = 0;
xfr_out(m) = 0;
t1_out(m) = t0(1);
t2_out(m) = t0(2);
t3_out(m) = t0(3);
t4_out(m) = t0(4);
z1_out(m) = depth(1) * 100;
z2_out(m) = depth(2) * 100;
z3_out(m) = depth(3) * 100;
z4_out(m) = depth(4) * 100;

% begin time loop

for iday = 1:ndays

   % time (seconds)

   time = iday * 24 * 3600;

   % depth of frost penetration XF (meters) based on time (seconds)

   xf = 2 * rg * sqrt(a1*time);

   % calculate soil temperatures given XF and time

   for i = 1:nsoi
      if (depth(i) <= xf)
         x = depth(i) / (2*sqrt(a1*time));
         y = rg;
         t(i) = ts + (tf - ts) * erf(x) / erf(y);
      else
         x = depth(i) / (2*sqrt(a2*time));
         y = rg * sqrt(a1/a2);
         t(i) = t0(i) - (t0(i) - tf) * (1-erf(x)) / (1-erf(y));
      end
   end

   % save for output

   m = m + 1;
   iday_out(m) = iday;
   xf_out(m) = xf * 100;
   t1_out(m) = t(1);
   t2_out(m) = t(2);
   t3_out(m) = t(3);
   t4_out(m) = t(4);
   z1_out(m) = depth(1) * 100;
   z2_out(m) = depth(2) * 100;
   z3_out(m) = depth(3) * 100;
   z4_out(m) = depth(4) * 100;

end

A = [iday_out; xf_out; z1_out; t1_out; z2_out; t2_out; z3_out; t3_out; z4_out; t4_out];
fileID = fopen('data_analytical.txt','w');
fprintf(fileID,'%10s %10s %10s %10s %10s %10s %10s %10s %10s %10s\n','day','z0C','z1','t1','z2','t2','z3','t3','z4','t4');
fprintf(fileID,'%10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f\n', A);
fclose(fileID);

dummy = 0;
phase_change.m | View on GitHub
function [soilvar] = phase_change (physcon, soilvar, dt)

% Adjust temperatures for phase change. Freeze or melt ice using
% energy excess or deficit needed to change temperature to the
% freezing point.

% ------------------------------------------------------
% Input
%   dt                      ! Time step (s)
%   physcon.hfus            ! Heat of fusion for water at 0 C (J/kg)
%   physcon.tfrz            ! Freezing point of water (K)
%   soilvar.nsoi            ! Number of soil layers
%   soilvar.dz              ! Soil layer thickness (m)
%   soilvar.cv              ! Volumetric heat capacity (J/m3/K)
%
% Input/output
%   soilvar.tsoi            ! Soil temperature (K)
%   soilvar.h2osoi_liq      ! Unfrozen water, liquid (kg H2O/m2)
%   soilvar.h2osoi_ice      ! Frozen water, ice (kg H2O/m2)
%
% Output
%   soilvar.hfsoi           ! Soil phase change energy flux (W/m2)
% ------------------------------------------------------

% --- Initialize total soil heat of fusion to zero

soilvar.hfsoi = 0;

% --- Now loop over all soil layers to calculate phase change

for i = 1:soilvar.nsoi

   % --- Save variables prior to phase change

   wliq0 = soilvar.h2osoi_liq(i);     % Amount of liquid water before phase change
   wice0 = soilvar.h2osoi_ice(i);     % Amount of ice before phase change
   wmass0 = wliq0 + wice0;            % Amount of total water before phase change
   tsoi0 = soilvar.tsoi(i);           % Soil temperature before phase change

   % --- Identify melting or freezing layers and set temperature to freezing

   % Default condition is no phase change (imelt = 0)

   imelt = 0;

   % Melting: if ice exists above melt point, melt some to liquid.
   % Identify melting by imelt = 1

   if (soilvar.h2osoi_ice(i) > 0 & soilvar.tsoi(i) > physcon.tfrz)
      imelt = 1;
      soilvar.tsoi(i) = physcon.tfrz;
   end

   % Freezing: if liquid exists below melt point, freeze some to ice.
   % Identify freezing by imelt = 2

   if (soilvar.h2osoi_liq(i) > 0 & soilvar.tsoi(i) < physcon.tfrz)
      imelt = 2;
      soilvar.tsoi(i) = physcon.tfrz;
   end

   % --- Calculate energy for freezing or melting

   % The energy for freezing or melting (W/m2) is assessed from the energy
   % excess or deficit needed to change temperature to the freezing point.
   % This is a potential energy flux, because cannot melt more ice than is
   % present or freeze more liquid water than is present.
   %
   % heat_flux_pot > 0: freezing; heat_flux_pot < 0: melting

   if (imelt > 0)
      heat_flux_pot = (soilvar.tsoi(i) - tsoi0) * soilvar.cv(i) * soilvar.dz(i) / dt;
   else
      heat_flux_pot = 0;
   end

   % Maximum energy for melting or freezing (W/m2)

   if (imelt == 1)
      heat_flux_max = -soilvar.h2osoi_ice(i) * physcon.hfus / dt;
   end

   if (imelt == 2)
      heat_flux_max = soilvar.h2osoi_liq(i) * physcon.hfus / dt;
   end

   % --- Now freeze or melt ice

   if (imelt > 0)

      % Change in ice (kg H2O/m2/s): freeze (+) or melt (-)

      ice_flux = heat_flux_pot / physcon.hfus;

      % Update ice (kg H2O/m2)

      soilvar.h2osoi_ice(i) = wice0 + ice_flux * dt;

      % Cannot melt more ice than is present

      soilvar.h2osoi_ice(i) = max(0, soilvar.h2osoi_ice(i));

      % Ice cannot exceed total water that is present

      soilvar.h2osoi_ice(i) = min(wmass0, soilvar.h2osoi_ice(i));

      % Update liquid water (kg H2O/m2) for change in ice

      soilvar.h2osoi_liq(i) = max(0, (wmass0-soilvar.h2osoi_ice(i)));

      % Actual energy flux from phase change (W/m2). This is equal to
      % heat_flux_pot except if tried to melt too much ice.

      heat_flux = physcon.hfus * (soilvar.h2osoi_ice(i) - wice0) / dt;

      % Sum energy flux from phase change (W/m2)

      soilvar.hfsoi = soilvar.hfsoi + heat_flux;

      % Residual energy not used in phase change is added to soil temperature

      residual = heat_flux_pot - heat_flux;
      soilvar.tsoi(i) = soilvar.tsoi(i) - residual * dt / (soilvar.cv(i) * soilvar.dz(i));

      % Error check: make sure actual phase change does not exceed permissible phase change

      if (abs(heat_flux) > abs(heat_flux_max))
         error ('Soil temperature energy conservation error: phase change')
      end

      % Freezing: make sure actual phase change does not exceed permissible phase change
      % and that the change in ice does not exceed permissible change

      if (imelt == 2)

         % Energy flux (W/m2)

         constraint = min(heat_flux_pot, heat_flux_max);
         err = heat_flux - constraint;
         if (abs(err) > 1e-03)
            error ('Soil temperature energy conservation error: freezing energy flux')
         end

         % Change in ice (kg H2O/m2)

         err = (soilvar.h2osoi_ice(i) - wice0) - constraint / physcon.hfus * dt;
         if (abs(err) > 1e-03)
            error ('Soil temperature energy conservation error: freezing ice flux')
         end
      end

      % Thawing: make sure actual phase change does not exceed permissible phase change
      % and that the change in ice does not exceed permissible change

      if (imelt == 1)

         % Energy flux (W/m2)

         constraint = max(heat_flux_pot, heat_flux_max);
         err = heat_flux - constraint;
         if (abs(err) > 1e-03)
            error ('Soil temperature energy conservation error: thawing energy flux')
         end

         % Change in ice (kg H2O/m2)

         err = (soilvar.h2osoi_ice(i) - wice0) - constraint / physcon.hfus * dt;
         if (abs(err) > 1e-03)
            error ('Soil temperature energy conservation error: thawing ice flux')
         end
      end

   end

end
soil_temperature.m | View on GitHub
function [soilvar] = soil_temperature (physcon, soilvar, tsurf, dt)

% Use an implicit formulation with the surface boundary condition specified
% as the surface temperature to solve for soil temperatures at time n+1.
%
% Calculate soil temperatures as:
%
%      dT   d     dT 
%   cv -- = -- (k --)
%      dt   dz    dz 
%
% where: T = temperature (K)
%        t = time (s)
%        z = depth (m)
%        cv = volumetric heat capacity (J/m3/K)
%        k = thermal conductivity (W/m/K)
%
% Set up a tridiagonal system of equations to solve for T at time n+1, 
% where the temperature equation for layer i is
%
%   d_i = a_i [T_i-1] n+1 + b_i [T_i] n+1 + c_i [T_i+1] n+1
%
% For soil layers undergoing phase change, set T_i = Tf (freezing) and use
% excess energy to freeze or melt ice:
%
%   Hf_i = (Tf - [T_i] n+1) * cv_i * dz_i / dt
%
% During the phase change, the unfrozen and frozen soil water
% (h2osoi_liq, h2osoi_ice) are adjusted.
%
% Or alternatively, use the apparent heat capacity method to
% account for phase change. In this approach, h2osoi_liq
% and h2osoi_ice are not calculated.
%
% ------------------------------------------------------
% Input
%   tsurf                   ! Surface temperature (K)
%   dt                      ! Time step (s)
%   soilvar.method          ! Use excess heat or apparent heat capacity for phase change
%   soilvar.nsoi            ! Number of soil layers
%   soilvar.z               ! Soil depth (m)
%   soilvar.z_plus_onehalf  ! Soil depth (m) at i+1/2 interface between layers i and i+1
%   soilvar.dz              ! Soil layer thickness (m)
%   soilvar.dz_plus_onehalf ! Thickness (m) between between i and i+1
%   soilvar.tk              ! Thermal conductivity (W/m/K)
%   soilvar.cv              ! Heat capacity (J/m3/K)
%
% Input/output
%   soilvar.tsoi            ! Soil temperature (K)
%   soilvar.h2osoi_liq      ! Unfrozen water, liquid (kg H2O/m2)
%   soilvar.h2osoi_ice      ! Frozen water, ice (kg H2O/m2)
%
% Output
%   soilvar.gsoi            ! Energy flux into soil (W/m2)
%   soilvar.hfsoi           ! Soil phase change energy flux (W/m2)
% ------------------------------------------------------

% solution = 'Crank-Nicolson'; % Use Crank-Nicolson solution
solution = 'implicit';       % Use implicit solution

% --- Save current soil temperature for energy conservation check

for i = 1:soilvar.nsoi
   tsoi0(i) = soilvar.tsoi(i);
end

% --- Thermal conductivity at interface (W/m/K)

for i = 1:soilvar.nsoi-1
   tk_plus_onehalf(i) = soilvar.tk(i) * soilvar.tk(i+1) * (soilvar.z(i)-soilvar.z(i+1)) / ...
   (soilvar.tk(i)*(soilvar.z_plus_onehalf(i)-soilvar.z(i+1)) + soilvar.tk(i+1)*(soilvar.z(i)-soilvar.z_plus_onehalf(i)));
end

% --- Heat flux at time n (W/m2)

switch solution
   case 'Crank-Nicolson'
   for i = 1:soilvar.nsoi-1
      f(i) = -tk_plus_onehalf(i) * (soilvar.tsoi(i) - soilvar.tsoi(i+1)) / soilvar.dz_plus_onehalf(i);
   end
   f(soilvar.nsoi) = 0;
end

% --- Set up tridiagonal matrix

% Top soil layer with tsurf as boundary condition

i = 1;
m = soilvar.cv(i) * soilvar.dz(i) / dt;
a(i) = 0;
c(i) = -tk_plus_onehalf(i) / soilvar.dz_plus_onehalf(i);
b(i) = m - c(i) + soilvar.tk(i) / (0 - soilvar.z(i));
d(i) = m * soilvar.tsoi(i) + soilvar.tk(i) / (0 - soilvar.z(i)) * tsurf;

switch solution
   case 'Crank-Nicolson'
   a(i) = 0;
   c(i) = -0.5 * tk_plus_onehalf(i) / soilvar.dz_plus_onehalf(i);
   b(i) = m - c(i) + soilvar.tk(i) / (0 - soilvar.z(i));
   d(i) = m * soilvar.tsoi(i) + 0.5 * f(i) + soilvar.tk(i) / (0 - soilvar.z(i)) * tsurf;
end

% Layers 2 to nsoi-1

for i = 2:soilvar.nsoi-1
   m = soilvar.cv(i) * soilvar.dz(i) / dt;
   a(i) = -tk_plus_onehalf(i-1) / soilvar.dz_plus_onehalf(i-1);
   c(i) = -tk_plus_onehalf(i) / soilvar.dz_plus_onehalf(i);
   b(i) = m - a(i) - c(i);
   d(i) = m * soilvar.tsoi(i);
end

switch solution
   case 'Crank-Nicolson'
   for i = 2:soilvar.nsoi-1
      m = soilvar.cv(i) * soilvar.dz(i) / dt;
      a(i) = -0.5 * tk_plus_onehalf(i-1) / soilvar.dz_plus_onehalf(i-1);
      c(i) = -0.5 * tk_plus_onehalf(i) / soilvar.dz_plus_onehalf(i);
      b(i) = m - a(i) - c(i);
      d(i) = m * soilvar.tsoi(i) + 0.5 * (f(i) - f(i-1));
   end
end

% Bottom soil layer with zero heat flux

i = soilvar.nsoi;
m = soilvar.cv(i) * soilvar.dz(i) / dt;
a(i) = -tk_plus_onehalf(i-1) / soilvar.dz_plus_onehalf(i-1);
c(i) = 0;
b(i) = m - a(i);
d(i) = m * soilvar.tsoi(i);

switch solution
   case 'Crank-Nicolson'
   a(i) = -0.5 * tk_plus_onehalf(i-1) / soilvar.dz_plus_onehalf(i-1);
   c(i) = 0;
   b(i) = m - a(i);
   d(i) = m * soilvar.tsoi(i) - 0.5 * f(i-1);
end

% --- Solve for soil temperature

[soilvar.tsoi] = tridiagonal_solver (a, b, c, d, soilvar.nsoi);

% --- Derive energy flux into soil (W/m2)

soilvar.gsoi = soilvar.tk(1) * (tsurf - soilvar.tsoi(1)) / (0 - soilvar.z(1));

% --- Phase change for soil layers undergoing freezing of thawing

switch soilvar.method

   case 'apparent-heat-capacity'

   % No explicit phase change energy flux. This is included in the heat capacity.

   soilvar.hfsoi = 0;

   case 'excess-heat'

   % Adjust temperatures for phase change. Freeze or melt ice using energy
   % excess or deficit needed to change temperature to the freezing point.
   % The variable hfsoi is returned as the energy flux from phase change (W/m2).

   [soilvar] = phase_change (physcon, soilvar, dt);

end

% --- Check for energy conservation

% Sum change in energy (W/m2)

edif = 0;
for i = 1:soilvar.nsoi
   edif = edif + soilvar.cv(i) * soilvar.dz(i) * (soilvar.tsoi(i) - tsoi0(i)) / dt;
end

% Error check

err = edif - soilvar.gsoi - soilvar.hfsoi;
if (abs(err) > 1e-03)
   error ('Soil temperature energy conservation error')
end
soil_thermal_properties.m | View on GitHub
function [soilvar] = soil_thermal_properties (physcon, soilvar)

% Calculate soil thermal conductivity and heat capacity

% ------------------------------------------------------
% Input
%   physcon.hfus             ! Heat of fusion for water at 0 C (J/kg)
%   physcon.tfrz             ! Freezing point of water (K)
%   physcon.rhowat           ! Density of water (kg/m3)
%   physcon.rhoice           ! Density of ice (kg/m3)
%   soilvar.method           ! Use excess heat or apparent heat capacity for phase change
%   soilvar.nsoi             ! Number of soil layers
%   soilvar.dz               ! Soil layer thickness (m)
%   soilvar.tsoi             ! Soil temperature (K)
%   soilvar.h2osoi_liq       ! Unfrozen water, liquid (kg H2O/m2)
%   soilvar.h2osoi_ice       ! Frozen water, ice (kg H2O/m2)
%
% Input/output
%   soilvar.tk               ! Thermal conducitivty (W/m/K)
%   soilvar.cv               ! Volumetric heat capacity (J/m3/K)
% ------------------------------------------------------

% Temperature range for freezing and thawing (K)

tinc = 0.5;

% Unfrozen and frozen thermal conductivity (W/m/K)

tku = 1.860;
tkf = 2.324;

% Unfrozen and frozen heat capacity (J/m3/K)

cvu = 2.862e06;
cvf = 1.966e06;

for i = 1:soilvar.nsoi

   % --- Volumetric soil water and ice

   watliq = soilvar.h2osoi_liq(i) / (physcon.rhowat * soilvar.dz(i));
   watice = soilvar.h2osoi_ice(i) / (physcon.rhoice * soilvar.dz(i));

   % Heat of fusion (J/m3) - This is equivalent to ql = hfus * (h2osoi_liq + h2osoi_ice) / dz

   ql = physcon.hfus * (physcon.rhowat * watliq + physcon.rhoice * watice);

   % Heat capacity and thermal conductivity

   if (soilvar.tsoi(i) > physcon.tfrz+tinc)
      soilvar.cv(i) = cvu;
      soilvar.tk(i) = tku;
   end

   if (soilvar.tsoi(i) >= physcon.tfrz-tinc & soilvar.tsoi(i) <= physcon.tfrz+tinc)
      switch soilvar.method
         case 'apparent-heat-capacity'
         soilvar.cv(i) = (cvf + cvu) / 2 + ql / (2 * tinc);
         case 'excess heat'
         soilvar.cv(i) = (cvf + cvu) / 2;
      end
      soilvar.tk(i) = tkf + (tku - tkf) * (soilvar.tsoi(i) - physcon.tfrz + tinc) / (2 * tinc);
   end

   if (soilvar.tsoi(i) < physcon.tfrz-tinc)
      soilvar.cv(i) = cvf;
      soilvar.tk(i) = tkf;
   end

end
tridiagonal_solver.m | View on GitHub
function [u] = tridiagonal_solver (a, b, c, d, n)

% Solve for U given the set of equations R * U = D, where U is a vector
% of length N, D is a vector of length N, and R is an N x N tridiagonal
% matrix defined by the vectors A, B, C each of length N. A(1) and
% C(N) are undefined and are not referenced.
%
%     |B(1) C(1) ...  ...  ...                     |
%     |A(2) B(2) C(2) ...  ...                     |
% R = |     A(3) B(3) C(3) ...                     |
%     |                    ... A(N-1) B(N-1) C(N-1)|
%     |                    ... ...    A(N)   B(N)  |
%
% The system of equations is written as:
%
%    A_i * U_i-1 + B_i * U_i + C_i * U_i+1 = D_i
%
% for i = 1 to N. The solution is found by rewriting the
% equations so that:
%
%    U_i = F_i - E_i * U_i+1

% --- Forward sweep (1 -> N) to get E and F

e(1) = c(1) / b(1);

for i = 2: 1: n-1
   e(i) = c(i) / (b(i) - a(i) * e(i-1));
end

f(1) = d(1) / b(1);

for i = 2: 1: n
   f(i) = (d(i) - a(i) * f(i-1)) / (b(i) - a(i) * e(i-1));
end

% --- Backward substitution (N -> 1) to solve for U

u(n) = f(n);

for i = n-1: -1: 1
   u(i) = f(i) - e(i) * u(i+1);
end

Output

Text

data_analytical.txt | View on GitHub | View raw
day        z0C         z1         t1         z2         t2         z3         t3         z4         t4
     0.000      0.000     25.000      2.000     55.000      2.000     85.000      2.000    115.000      2.000
     1.000     17.636     25.000      0.478     55.000      1.664     85.000      1.963    115.000      1.998
     2.000     24.942     25.000      0.003     55.000      1.179     85.000      1.757    115.000      1.949
     3.000     30.547     25.000     -1.748     55.000      0.853     85.000      1.522    115.000      1.841
     4.000     35.273     25.000     -2.824     55.000      0.624     85.000      1.316    115.000      1.712
     5.000     39.436     25.000     -3.565     55.000      0.454     85.000      1.143    115.000      1.583
     6.000     43.200     25.000     -4.116     55.000      0.320     85.000      0.996    115.000      1.462
     7.000     46.661     25.000     -4.546     55.000      0.213     85.000      0.872    115.000      1.350
     8.000     49.883     25.000     -4.893     55.000      0.123     85.000      0.765    115.000      1.248
     9.000     52.909     25.000     -5.182     55.000      0.048     85.000      0.671    115.000      1.156
    10.000     55.771     25.000     -5.427     55.000     -0.131     85.000      0.589    115.000      1.072
    11.000     58.493     25.000     -5.637     55.000     -0.570     85.000      0.515    115.000      0.995
    12.000     61.094     25.000     -5.822     55.000     -0.955     85.000      0.450    115.000      0.925
    13.000     63.589     25.000     -5.984     55.000     -1.296     85.000      0.391    115.000      0.860
    14.000     65.989     25.000     -6.129     55.000     -1.602     85.000      0.337    115.000      0.801
    15.000     68.305     25.000     -6.260     55.000     -1.877     85.000      0.288    115.000      0.745
    16.000     70.545     25.000     -6.378     55.000     -2.127     85.000      0.243    115.000      0.694
    17.000     72.716     25.000     -6.485     55.000     -2.355     85.000      0.201    115.000      0.646
    18.000     74.825     25.000     -6.584     55.000     -2.565     85.000      0.163    115.000      0.602
    19.000     76.875     25.000     -6.674     55.000     -2.758     85.000      0.127    115.000      0.560
    20.000     78.872     25.000     -6.758     55.000     -2.937     85.000      0.094    115.000      0.521
    21.000     80.820     25.000     -6.836     55.000     -3.103     85.000      0.063    115.000      0.484
    22.000     82.722     25.000     -6.908     55.000     -3.258     85.000      0.034    115.000      0.449
    23.000     84.581     25.000     -6.976     55.000     -3.403     85.000      0.006    115.000      0.416
    24.000     86.400     25.000     -7.039     55.000     -3.539     85.000     -0.154    115.000      0.384
    25.000     88.182     25.000     -7.099     55.000     -3.667     85.000     -0.344    115.000      0.355
    26.000     89.928     25.000     -7.155     55.000     -3.787     85.000     -0.523    115.000      0.327
    27.000     91.641     25.000     -7.208     55.000     -3.901     85.000     -0.692    115.000      0.300
    28.000     93.323     25.000     -7.258     55.000     -4.009     85.000     -0.853    115.000      0.274
    29.000     94.975     25.000     -7.305     55.000     -4.112     85.000     -1.006    115.000      0.250
    30.000     96.598     25.000     -7.351     55.000     -4.209     85.000     -1.151    115.000      0.226
    31.000     98.195     25.000     -7.394     55.000     -4.302     85.000     -1.289    115.000      0.204
    32.000     99.766     25.000     -7.434     55.000     -4.390     85.000     -1.422    115.000      0.182
    33.000    101.313     25.000     -7.474     55.000     -4.475     85.000     -1.548    115.000      0.162
    34.000    102.837     25.000     -7.511     55.000     -4.555     85.000     -1.669    115.000      0.142
    35.000    104.338     25.000     -7.547     55.000     -4.632     85.000     -1.785    115.000      0.123
    36.000    105.818     25.000     -7.581     55.000     -4.706     85.000     -1.896    115.000      0.105
    37.000    107.278     25.000     -7.614     55.000     -4.778     85.000     -2.003    115.000      0.087
    38.000    108.718     25.000     -7.645     55.000     -4.846     85.000     -2.105    115.000      0.070
    39.000    110.139     25.000     -7.675     55.000     -4.911     85.000     -2.204    115.000      0.054
    40.000    111.542     25.000     -7.705     55.000     -4.975     85.000     -2.299    115.000      0.038
    41.000    112.928     25.000     -7.733     55.000     -5.036     85.000     -2.391    115.000      0.022
    42.000    114.296     25.000     -7.760     55.000     -5.094     85.000     -2.480    115.000      0.008
    43.000    115.649     25.000     -7.786     55.000     -5.151     85.000     -2.565    115.000     -0.053
    44.000    116.986     25.000     -7.811     55.000     -5.206     85.000     -2.648    115.000     -0.162
    45.000    118.308     25.000     -7.836     55.000     -5.259     85.000     -2.728    115.000     -0.266
    46.000    119.615     25.000     -7.859     55.000     -5.310     85.000     -2.805    115.000     -0.368
    47.000    120.909     25.000     -7.882     55.000     -5.360     85.000     -2.880    115.000     -0.466
    48.000    122.188     25.000     -7.904     55.000     -5.408     85.000     -2.953    115.000     -0.561
    49.000    123.454     25.000     -7.926     55.000     -5.454     85.000     -3.024    115.000     -0.654
    50.000    124.708     25.000     -7.946     55.000     -5.500     85.000     -3.092    115.000     -0.744
    51.000    125.949     25.000     -7.967     55.000     -5.544     85.000     -3.159    115.000     -0.831
    52.000    127.177     25.000     -7.986     55.000     -5.586     85.000     -3.223    115.000     -0.916
    53.000    128.394     25.000     -8.005     55.000     -5.628     85.000     -3.286    115.000     -0.999
    54.000    129.600     25.000     -8.024     55.000     -5.668     85.000     -3.347    115.000     -1.079
    55.000    130.794     25.000     -8.042     55.000     -5.707     85.000     -3.407    115.000     -1.158
    56.000    131.978     25.000     -8.059     55.000     -5.745     85.000     -3.465    115.000     -1.234
    57.000    133.151     25.000     -8.076     55.000     -5.783     85.000     -3.521    115.000     -1.308
    58.000    134.314     25.000     -8.093     55.000     -5.819     85.000     -3.576    115.000     -1.381
    59.000    135.467     25.000     -8.109     55.000     -5.854     85.000     -3.629    115.000     -1.452
    60.000    136.610     25.000     -8.125     55.000     -5.888     85.000     -3.682    115.000     -1.520
data_numerical.txt | View on GitHub | View raw
day        z0C         z1         t1         z2         t2         z3         t3         z4         t4
     0.000      0.000     25.000      2.000     55.000      2.000     85.000      2.000    115.000      2.000
     1.000     19.935     25.000      0.456     55.000      1.573     85.000      1.933    115.000      1.993
     2.000     26.555     25.000     -0.090     55.000      1.150     85.000      1.724    115.000      1.932
     3.000     33.784     25.000     -2.487     55.000      0.929     85.000      1.518    115.000      1.825
     4.000     36.593     25.000     -2.994     55.000      0.720     85.000      1.341    115.000      1.710
     5.000     40.430     25.000     -3.180     55.000      0.620     85.000      1.203    115.000      1.596
     6.000     44.549     25.000     -4.412     55.000      0.489     85.000      1.080    115.000      1.494
     7.000     48.133     25.000     -4.567     55.000      0.438     85.000      0.985    115.000      1.400
     8.000     50.682     25.000     -4.681     55.000      0.350     85.000      0.901    115.000      1.318
     9.000     54.315     25.000     -5.347     55.000      0.129     85.000      0.821    115.000      1.240
    10.000     57.060     25.000     -5.504     55.000     -0.114     85.000      0.764    115.000      1.173
    11.000     59.710     25.000     -5.588     55.000     -0.326     85.000      0.700    115.000      1.111
    12.000     62.344     25.000     -5.664     55.000     -0.753     85.000      0.643    115.000      1.050
    13.000     64.542     25.000     -6.064     55.000     -1.501     85.000      0.612    115.000      1.001
    14.000     67.231     25.000     -6.182     55.000     -1.664     85.000      0.565    115.000      0.955
    15.000     69.552     25.000     -6.245     55.000     -1.788     85.000      0.502    115.000      0.906
    16.000     71.233     25.000     -6.298     55.000     -1.898     85.000      0.490    115.000      0.863
    17.000     73.902     25.000     -6.446     55.000     -2.425     85.000      0.469    115.000      0.831
    18.000     75.475     25.000     -6.627     55.000     -2.662     85.000      0.428    115.000      0.797
    19.000     78.171     25.000     -6.698     55.000     -2.781     85.000      0.370    115.000      0.756
    20.000     80.035     25.000     -6.745     55.000     -2.874     85.000      0.302    115.000      0.723
    21.000     81.485     25.000     -6.783     55.000     -2.955     85.000      0.232    115.000      0.701
    22.000     83.711     25.000     -6.836     55.000     -3.179     85.000      0.143    115.000      0.676
    23.000     84.970     25.000     -6.985     55.000     -3.454     85.000      0.003    115.000      0.645
    24.000     87.584     25.000     -7.059     55.000     -3.575     85.000     -0.126    115.000      0.610
    25.000     89.449     25.000     -7.103     55.000     -3.657     85.000     -0.244    115.000      0.595
    26.000     90.900     25.000     -7.136     55.000     -3.724     85.000     -0.351    115.000      0.578
    27.000     92.119     25.000     -7.164     55.000     -3.784     85.000     -0.448    115.000      0.556
    28.000     94.010     25.000     -7.201     55.000     -3.924     85.000     -0.905    115.000      0.529
    29.000     95.472     25.000     -7.299     55.000     -4.126     85.000     -1.067    115.000      0.499
    30.000     97.780     25.000     -7.360     55.000     -4.230     85.000     -1.171    115.000      0.493
    31.000     99.470     25.000     -7.397     55.000     -4.301     85.000     -1.258    115.000      0.481
    32.000    100.825     25.000     -7.425     55.000     -4.357     85.000     -1.334    115.000      0.463
    33.000    101.983     25.000     -7.449     55.000     -4.406     85.000     -1.404    115.000      0.440
    34.000    103.018     25.000     -7.470     55.000     -4.450     85.000     -1.468    115.000      0.412
    35.000    104.653     25.000     -7.507     55.000     -4.576     85.000     -1.789    115.000      0.379
    36.000    106.666     25.000     -7.572     55.000     -4.703     85.000     -1.918    115.000      0.340
    37.000    108.511     25.000     -7.615     55.000     -4.782     85.000     -2.006    115.000      0.297
    38.000    109.952     25.000     -7.644     55.000     -4.839     85.000     -2.078    115.000      0.250
    39.000    111.160     25.000     -7.667     55.000     -4.886     85.000     -2.141    115.000      0.201
    40.000    112.224     25.000     -7.686     55.000     -4.926     85.000     -2.198    115.000      0.152
    41.000    113.196     25.000     -7.703     55.000     -4.962     85.000     -2.250    115.000      0.102
    42.000    114.507     25.000     -7.720     55.000     -5.008     85.000     -2.394    115.000      0.040
    43.000    116.285     25.000     -7.763     55.000     -5.119     85.000     -2.557    115.000     -0.048
    44.000    118.119     25.000     -7.804     55.000     -5.197     85.000     -2.647    115.000     -0.131
    45.000    119.554     25.000     -7.833     55.000     -5.253     85.000     -2.716    115.000     -0.208
    46.000    120.756     25.000     -7.855     55.000     -5.297     85.000     -2.774    115.000     -0.278
    47.000    121.814     25.000     -7.873     55.000     -5.334     85.000     -2.824    115.000     -0.344
    48.000    122.777     25.000     -7.888     55.000     -5.366     85.000     -2.870    115.000     -0.405
    49.000    123.671     25.000     -7.902     55.000     -5.395     85.000     -2.912    115.000     -0.462
    50.000    124.739     25.000     -7.914     55.000     -5.424     85.000     -2.983    115.000     -0.719
    51.000    126.609     25.000     -7.941     55.000     -5.500     85.000     -3.127    115.000     -0.839
    52.000    128.280     25.000     -7.974     55.000     -5.567     85.000     -3.210    115.000     -0.914
    53.000    129.620     25.000     -7.999     55.000     -5.617     85.000     -3.272    115.000     -0.977
    54.000    130.763     25.000     -8.019     55.000     -5.656     85.000     -3.324    115.000     -1.034
    55.000    131.779     25.000     -8.035     55.000     -5.688     85.000     -3.369    115.000     -1.086
    56.000    132.708     25.000     -8.048     55.000     -5.717     85.000     -3.409    115.000     -1.135
    57.000    133.571     25.000     -8.060     55.000     -5.742     85.000     -3.446    115.000     -1.180
    58.000    134.385     25.000     -8.071     55.000     -5.766     85.000     -3.480    115.000     -1.223
    59.000    135.601     25.000     -8.082     55.000     -5.791     85.000     -3.539    115.000     -1.397
    60.000    137.428     25.000     -8.102     55.000     -5.846     85.000     -3.641    115.000     -1.504