%created by Dr. Mayank JHA

clear all; clc; close all;

%% Battery Prognostics using a Fully Connected Deep Neural Network: Tutorial and Exercises
% This script is divided into three parts:
%   Part A: Illustrative Plots of Key Prognostic Variables and Parameters.
%   Part B: Full Dataset Generation using a Synthetic Degradation Model.
%   Part C: Prognostics using a Fully Connected Deep Neural Network.
%
% Each section includes detailed pedagogical comments and exercises.
% 
% Author: Dr. Mayank JHA
% Date: 17 Feb, 2025

%% Part A: Illustrative Plots of Battery Prognostic Concepts
% In this section, we plot:
%   - The nominal degradation curve.
%   - The impact of varying temperature (with current fixed).
%   - The impact of varying current (with temperature fixed).
%
% These plots help you visualize how the battery capacity decreases with cycle count
% and how different operating conditions affect degradation.
%

% Define a range of battery cycles for visualization
cycles_plot = (1:1000)';

% Define fixed synthetic model parameters (used later for data generation)
a = 1e-6;  % Base degradation rate
b = 2;     % Nonlinear exponent for cycle effect
c = 0.2;   % Temperature influence factor
d = 0.3;   % Current influence factor

% Plot 1: Degradation curve under nominal conditions
nominal_temp = 30;     % Nominal temperature in °C
nominal_current = 3;   % Nominal current in A
degradation_nominal = a * cycles_plot.^b .* (1 + c*(nominal_temp-30)/10) .* (1 + d*(nominal_current-3)/2);
capacity_nominal = 1 - degradation_nominal;
figure;
plot(cycles_plot, capacity_nominal, 'b-', 'LineWidth', 2);
xlabel('Cycle Number');
ylabel('Battery Capacity');
title('Battery Capacity under Nominal Conditions (30°C, 3 A)');
grid on;

%


% Plot 2: Effect of Temperature Variation (current fixed at 3 A)
temps = [20, 30, 40]; % Temperatures in °C
figure;
hold on;
colors = {'r-', 'g-', 'b-'};
for i = 1:length(temps)
    degradation_temp = a * cycles_plot.^b .* (1 + c*(temps(i)-30)/10) .* (1 + d*(nominal_current-3)/2);
    capacity_temp = 1 - degradation_temp;
    plot(cycles_plot, capacity_temp, colors{i}, 'LineWidth', 2, 'DisplayName', sprintf('Temp = %d°C', temps(i)));
end
xlabel('Cycle Number');
ylabel('Battery Capacity');
title('Impact of Temperature on Battery Capacity (Current = 3 A)');
legend('show');
grid on;

%


% Plot 3: Effect of Current Variation (temperature fixed at 30°C)
currents = [1, 3, 5]; % Currents in A
figure;
hold on;
for i = 1:length(currents)
    degradation_current = a * cycles_plot.^b .* (1 + c*(nominal_temp-30)/10) .* (1 + d*(currents(i)-3)/2);
    capacity_current = 1 - degradation_current;
    plot(cycles_plot, capacity_current, colors{i}, 'LineWidth', 2, 'DisplayName', sprintf('Current = %d A', currents(i)));
end
xlabel('Cycle Number');
ylabel('Battery Capacity');
title('Impact of Current on Battery Capacity (Temperature = 30°C)');
legend('show');
grid on;

%


%% Part B: Full Dataset Generation for Neural Network Prognostics
% In this section, we generate a synthetic dataset for battery prognostics.
% The dataset includes:
%   - Cycle number
%   - Temperature (random values between 20°C and 40°C)
%   - Current (random values between 1 A and 5 A)
%   - Battery capacity computed from the degradation model with added noise.
%
% Here we introduce a non-stationary, non additive noise factor. Instead of simply adding
% Gaussian noise, we generate a multiplicative noise factor that changes with cycle number,
% making the degradation trend more challenging to capture for shallow neural networks.

N = 1000;                  % Total number of battery cycles
cycles = (1:N)';           % Battery cycle numbers

% Generate random operating conditions
temperature = 20 + (40-20)*rand(N,1);  % Temperatures between 20°C and 40°C
current = 1 + (5-1)*rand(N,1);           % Currents between 1 A and 5 A

% Create a non-stationary, non additive noise factor.
% The noise factor increases with cycle number and has a sinusoidal variation.
% This means that later cycles are affected by higher, fluctuating noise levels.
noiseFactor = 1 + 0.05*sin(0.02*cycles) + 0.1*(cycles/N).*rand(N,1);

% Calculate degradation using the synthetic model multiplied by the noise factor.
degradation = a * cycles.^b .* (1 + c*(temperature-30)/10) .* (1 + d*(current-3)/2) .* 10.*noiseFactor;
capacity = 1 - degradation;  % Battery capacity decreases as degradation increases

% Visualize the complete dataset with a scatter plot (color indicates temperature)
figure;
scatter(cycles, capacity, 15, temperature, 'filled');
xlabel('Cycle Number');
ylabel('Battery Capacity');
title('Generated Battery Capacity Data (Color Coded by Temperature)');
colorbar;
grid on;

%


%% Part C: Prognostics using a Deep Neural Network
% In this final section, we use the generated dataset to train a deep neural network
% for predicting battery capacity.
%
% The steps include:
%   1. Data preparation: Formatting and normalization of inputs and targets.
%   2. Neural network creation: A fully connected network with 3 hidden layers.
%   3. Training: Using the Levenberg-Marquardt algorithm.
%   4. Evaluation: Assessing performance using Mean Squared Error (MSE) and regression plots.
%

% Data Preparation
% Inputs: [cycle, temperature, current]
% Targets: Battery capacity
inputs = [cycles, temperature, current]';  % 3 x N matrix (each column is one sample)
targets = capacity';                         % 1 x N vector (target values)

% Normalize inputs and targets using mapminmax
[inputn, inputps] = mapminmax(inputs);   % Normalize inputs to [0, 1]
[targetn, targetps] = mapminmax(targets);  % Normalize targets to [0, 1]

% Neural Network Creation
% Create a fully connected neural network with a hidden layer of 10 neurons
hiddenLayerSizes = ;  
net = fitnet(, );  % Using Levenberg-Marquardt algorithm

% Set training parameters
net.trainParam.epochs = ;       % Maximum number of training epochs
net.trainParam.goal =;         % Performance goal (MSE)
net.trainParam.showWindow = ;   % Display interactive training window

% Divide data: 70% training, 15% validation, 15% test
net.divideParam.trainRatio = ;
net.divideParam.valRatio = ;
net.divideParam.testRatio = ;

% Training the Neural Network

%


% Evaluation on Test Data
% Extract test indices from the training record
testIndices = tr.testInd;
testInputs = inputn(:, testIndices);
testTargets = targetn(:, testIndices);

% Use the trained network to predict battery capacity (in normalized scale)
predictionsNorm = net () % put your inputs here 

% Reverse normalization to obtain predictions in the original scale
predictions = mapminmax('reverse', predictionsNorm, targetps);
actual = mapminmax('reverse', testTargets, targetps);

% Compute Mean Squared Error (MSE) on the test data
mseTest = %calculate your MSE here
fprintf('Test MSE: %f\n', mseTest);

% Visualization: Actual vs. Predicted Battery Capacity
figure;
plot(actual, 'b-o', 'LineWidth', 2, 'DisplayName', 'Actual Capacity');
hold on;
plot(predictions, 'r-s', 'LineWidth', 2, 'DisplayName', 'Predicted Capacity');
xlabel('Test Sample Index');
ylabel('Battery Capacity');
title('Actual vs. Predicted Battery Capacity');
legend('show');
grid on;

% Additional Regression Plot for Further Evaluation
figure;
plotregression(actual, predictions);
title('Regression Analysis for Battery Capacity Prediction');

disp('Battery prognostics using a fully connected deep neural network is complete.');

%


% Exercise 3:
%   Experiment with modifying the network architecture.
%   For instance, change hiddenLayerSizes from [10 10 10] to [20 10 5] or reduce to [10 10].

%   Observe how the MSE and regression plot change.

%   (Solution: Increasing neurons or layers can capture more complex nonlinearities,
%    potentially lowering the MSE, but may risk overfitting if too complex.
%    Reducing layers simplifies the model, which may lead to underfitting.)