%% CAS_F0016_VERIFY.m -- Entropy production (sigma = Jq . grad(1/T) >= 0)
%  Assertion-based CAS audit block
%  Pillar: Thermodynamics | Chain: local equil -> entropy balance -> Fourier -> non-negativity
%  CalRef: Thermodynamics Math Appendix S3B, Thermo Calibration S5Z
%
%  Structure mirrors cas_F15.txt (= F0016) sections A-E.
%  Verifies:
%    1. Chain rule: grad(1/T) = -grad(T)/T^2
%    2. Product rule: div(Jq/T) = (1/T)*div(Jq) + Jq.grad(1/T)
%    3. Energy balance cancellation: (1/T)*du/dt + (1/T)*div(Jq) = 0
%    4. Sigma isolation: sigma = Jq.grad(1/T)
%    5. Fourier substitution: sigma = kappa*|grad T|^2/T^2
%    6. Non-negativity: sigma >= 0 (kappa >= 0, T > 0)
%    7. Equilibrium limit: grad T = 0 => sigma = 0
%    8. Concrete 1D test
%    9. Self-test: wrong Fourier sign gives sigma < 0
%   10. Self-test: wrong residual quantified
%
%  HARDENING: isAlways(..., 'Unknown', 'false') throughout.

clear; clc;
fprintf('=== CAS AUDIT: F0016 -- Entropy production ===\n\n');

pass_count = 0;
fail_count = 0;
total_steps = 0;

%% ---- A. INPUTS ----
syms x y z real
syms T_field(x,y,z)         % temperature field (positive, but symfun)

% We need grad(T) and grad(1/T) componentwise
gradT_x = diff(T_field, x);
gradT_y = diff(T_field, y);
gradT_z = diff(T_field, z);

% Heat flux components (general, before Fourier)
syms Jqx(x,y,z) Jqy(x,y,z) Jqz(x,y,z)

% Thermal conductivity
syms kappa_th positive      % kappa >= 0 (positive assumption covers > 0)

fprintf('Section A: Inputs defined.\n');
fprintf('  Entropy balance: ds/dt + div(Js) = sigma\n');
fprintf('  Local equilibrium: ds = (1/T)*du\n');
fprintf('  Js = Jq/T, energy balance: du/dt + div(Jq) = 0\n');
fprintf('  Fourier: Jq = -kappa*grad(T)\n\n');

%% ---- B. ASSUMPTIONS / DOMAINS ----
fprintf('Section B: Local equilibrium, T > 0, kappa >= 0, pure conduction.\n\n');

%% ---- C. ALLOWED LEMMAS ----
fprintf('Section C: Lemmas declared.\n');
fprintf('  C.1: ds/dt = (1/T)*du/dt\n');
fprintf('  C.2: div(Jq/T) = (1/T)*div(Jq) + Jq.grad(1/T)\n');
fprintf('  C.3: grad(1/T) = -(1/T^2)*grad(T)\n\n');

%% ---- D. STEP LOG ----
fprintf('Section D: Step log\n');
fprintf('---------------------------------------------\n');

% --- Step 1: Chain rule for grad(1/T) ---
% grad(1/T) = -(1/T^2)*grad(T)
% Verify componentwise: d/dx(1/T) = -(1/T^2)*dT/dx
invT = 1/T_field;
grad_invT_x = diff(invT, x);
expected_grad_invT_x = -(1/T_field^2)*gradT_x;

step1_residual = simplify(grad_invT_x - expected_grad_invT_x);

total_steps = total_steps + 1;
if isAlways(step1_residual == 0, 'Unknown', 'false')
    fprintf('  Step 1  PASS  grad(1/T) = -(1/T^2)*grad(T) [x-component]\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 1  FAIL  residual: %s\n', char(step1_residual));
    fail_count = fail_count + 1;
end

% Verify y and z components too (structural completeness)
grad_invT_y = diff(invT, y);
grad_invT_z = diff(invT, z);
step1b_res = simplify(grad_invT_y - (-(1/T_field^2)*gradT_y));
step1c_res = simplify(grad_invT_z - (-(1/T_field^2)*gradT_z));

total_steps = total_steps + 1;
if isAlways(step1b_res == 0, 'Unknown', 'false') && isAlways(step1c_res == 0, 'Unknown', 'false')
    fprintf('  Step 1b PASS  grad(1/T) y,z components confirmed\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 1b FAIL\n');
    fail_count = fail_count + 1;
end

% --- Step 2: Product rule for div(Jq/T) ---
% div(Jq/T) = d/dx(Jqx/T) + d/dy(Jqy/T) + d/dz(Jqz/T)
% Each term: d/dx(Jqx/T) = (1/T)*dJqx/dx + Jqx*d/dx(1/T)
%                         = (1/T)*dJqx/dx - Jqx/(T^2)*dT/dx
div_JqOverT = diff(Jqx/T_field, x) + diff(Jqy/T_field, y) + diff(Jqz/T_field, z);

div_Jq = diff(Jqx, x) + diff(Jqy, y) + diff(Jqz, z);
Jq_dot_grad_invT = Jqx*grad_invT_x + Jqy*grad_invT_y + Jqz*grad_invT_z;

expected_div = (1/T_field)*div_Jq + Jq_dot_grad_invT;

step2_residual = simplify(div_JqOverT - expected_div);

total_steps = total_steps + 1;
if isAlways(step2_residual == 0, 'Unknown', 'false')
    fprintf('  Step 2  PASS  div(Jq/T) = (1/T)*div(Jq) + Jq.grad(1/T)\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 2  FAIL  residual: %s\n', char(step2_residual));
    fail_count = fail_count + 1;
end

% --- Step 3: Energy balance cancellation ---
% (1/T)*du/dt + (1/T)*div(Jq) = (1/T)*(du/dt + div(Jq)) = 0
% This is algebraic: any expression * (1/T) where the expression = 0 gives 0.
% Verify: if du/dt + div(Jq) = 0, then (1/T)*(du/dt + div(Jq)) = 0.
syms dudt_sym  % symbolic placeholder for du/dt
syms divJq_sym % symbolic placeholder for div(Jq)
syms T_pos positive

% Energy balance: dudt_sym + divJq_sym = 0 => dudt_sym = -divJq_sym
combined = (1/T_pos)*((-divJq_sym) + divJq_sym);
step3_residual = simplify(combined);

total_steps = total_steps + 1;
if isAlways(step3_residual == 0, 'Unknown', 'false')
    fprintf('  Step 3  PASS  (1/T)*(du/dt + div(Jq)) = 0 (energy balance)\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 3  FAIL  residual: %s\n', char(step3_residual));
    fail_count = fail_count + 1;
end

% --- Step 4: Sigma isolation ---
% After cancellation: sigma = Jq.grad(1/T)
% This is the algebraic result of Steps 1-3.
% Verify structurally: sigma defined as Jq_dot_grad_invT
% and Jq_dot_grad_invT uses the correct grad(1/T) from Step 1.
%
% Check: Jq.grad(1/T) = Jqx*(-1/T^2*dT/dx) + ... = -(1/T^2)*Jq.gradT
Jq_dot_gradT = Jqx*gradT_x + Jqy*gradT_y + Jqz*gradT_z;
sigma_form1 = Jq_dot_grad_invT;
sigma_form2 = -(1/T_field^2)*Jq_dot_gradT;

step4_residual = simplify(sigma_form1 - sigma_form2);

total_steps = total_steps + 1;
if isAlways(step4_residual == 0, 'Unknown', 'false')
    fprintf('  Step 4  PASS  sigma = Jq.grad(1/T) = -(1/T^2)*Jq.grad(T)\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 4  FAIL  residual: %s\n', char(step4_residual));
    fail_count = fail_count + 1;
end

% --- Step 5: Fourier substitution ---
% Jq = -kappa*grad(T)
% sigma = Jq.grad(1/T) = (-kappa*gradT).(-gradT/T^2) = kappa*|gradT|^2/T^2
%
% Work with scalar symbols for clarity
syms gradT_sq positive  % |grad T|^2
syms T_val positive     % temperature

sigma_fourier = kappa_th * gradT_sq / T_val^2;

% From Jq.grad(1/T) with Fourier:
% Jq.grad(1/T) = (-kappa*gradT).(-(1/T^2)*gradT) = kappa*|gradT|^2/T^2
Jq_dot_grad_invT_fourier = (-kappa_th)*gradT_sq * (-(1/T_val^2));
step5_residual = simplify(Jq_dot_grad_invT_fourier - sigma_fourier);

total_steps = total_steps + 1;
if isAlways(step5_residual == 0, 'Unknown', 'false')
    fprintf('  Step 5  PASS  sigma = kappa*|grad T|^2/T^2 (Fourier substitution)\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 5  FAIL  residual: %s\n', char(step5_residual));
    fail_count = fail_count + 1;
end

% --- Step 6: Non-negativity ---
% kappa > 0, |gradT|^2 >= 0, T^2 > 0 => sigma >= 0
total_steps = total_steps + 1;
if isAlways(sigma_fourier >= 0, 'Unknown', 'false')
    fprintf('  Step 6  PASS  sigma >= 0 (second law)\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 6  FAIL  Non-negativity check\n');
    fail_count = fail_count + 1;
end

% --- Step 7: Equilibrium limit ---
% grad T = 0 => |gradT|^2 = 0 => sigma = 0
sigma_equil = subs(sigma_fourier, gradT_sq, 0);
step7_residual = simplify(sigma_equil);

total_steps = total_steps + 1;
if isAlways(step7_residual == 0, 'Unknown', 'false')
    fprintf('  Step 7  PASS  Equilibrium: grad T = 0 => sigma = 0\n');
    pass_count = pass_count + 1;
else
    fprintf('  Step 7  FAIL  Equilibrium residual: %s\n', char(step7_residual));
    fail_count = fail_count + 1;
end

% --- Step 8: Concrete 1D test ---
% T(x) = 300 + 10*x (linear gradient, kappa = 200 W/(m K))
% dT/dx = 10 K/m, |gradT|^2 = 100 K^2/m^2
% At x=0: T = 300 K
% sigma = 200 * 100 / 300^2 = 20000/90000 = 2/9 ~ 0.2222 W/(m^3 K)
kappa_val = 200;
dTdx_val = 10;
T_val_num = 300;
sigma_num = kappa_val * dTdx_val^2 / T_val_num^2;
sigma_expected = 2/9;  % exact

rel_error = abs(sigma_num - sigma_expected) / sigma_expected;

total_steps = total_steps + 1;
if rel_error < 1e-12
    fprintf('  Step 8  PASS  Numerical: sigma = %.6f W/(m^3 K)\n', sigma_num);
    pass_count = pass_count + 1;
else
    fprintf('  Step 8  FAIL  Numerical rel error: %.2e\n', rel_error);
    fail_count = fail_count + 1;
end

fprintf('---------------------------------------------\n\n');

%% ---- E. CHECK OUTPUTS ----
fprintf('Section E: Output checks\n');
fprintf('---------------------------------------------\n');

% --- Unit check ---
fprintf('  Unit check:\n');
fprintf('    Jq: [W/m^2], grad(1/T): [1/(K*m)]\n');
fprintf('    sigma = Jq.grad(1/T): [W/(m^3*K)]\n');
fprintf('    kappa*|gradT|^2/T^2: [W/(m*K)]*[K^2/m^2]/[K^2] = [W/(m^3*K)]\n');
fprintf('    PASS\n\n');

% --- Self-test: wrong Fourier sign (Jq = +kappa*gradT) ---
% sigma_wrong = (+kappa*gradT).(-gradT/T^2) = -kappa*|gradT|^2/T^2 < 0
sigma_wrong = -kappa_th * gradT_sq / T_val^2;
wrong_residual = simplify(sigma_wrong - sigma_fourier);

total_steps = total_steps + 1;
if ~isAlways(wrong_residual == 0, 'Unknown', 'false')
    fprintf('  Self-test 1: Wrong Fourier sign gives sigma < 0 (detected)  PASS\n');
    pass_count = pass_count + 1;
else
    fprintf('  Self-test 1: FAIL (wrong sign not detected)\n');
    fail_count = fail_count + 1;
end

% --- Self-test: quantify wrong residual ---
% sigma_wrong - sigma_correct = -2*kappa*|gradT|^2/T^2
expected_wrong_res = -2*kappa_th*gradT_sq/T_val^2;
wrong_quant = simplify(wrong_residual - expected_wrong_res);

total_steps = total_steps + 1;
if isAlways(wrong_quant == 0, 'Unknown', 'false')
    fprintf('  Self-test 2: wrong - correct = -2*kappa*|gradT|^2/T^2 (quantified)  PASS\n');
    pass_count = pass_count + 1;
else
    fprintf('  Self-test 2: FAIL  residual = %s\n', char(wrong_quant));
    fail_count = fail_count + 1;
end

fprintf('---------------------------------------------\n\n');

%% ---- VERDICT ----
fprintf('=============================================\n');
fprintf('  F0016 AUDIT RESULT\n');
fprintf('  Steps: %d  |  Pass: %d  |  Fail: %d\n', total_steps, pass_count, fail_count);
if fail_count == 0
    fprintf('  STATUS: *** PASS ***\n');
else
    fprintf('  STATUS: *** FAIL *** (%d step(s) failed)\n', fail_count);
end
fprintf('=============================================\n');
fprintf('Audit complete for F0016.\n');
