classdef class_REVS_battery
    %UNTITLED Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
        
        chemistry;									% Battery Chemistry from enum_battery_type
        initial_soc_norm;							% Initial pack state of charge [0-1]
        
        num_series_cells;							% Number of cells connected in series in the pack
        num_parallel_cells;							% Number of cells or series collections of cells in paralel within the pack
        num_cells;									% Total number of cells in the pack
        
        capacity_Coulombs;							% Battery charge capacity [Culombs]
        use_second_order_model;						% Use second order transient response model [bool]
        
        max_voltage_V;								% not used, for now (see model)
        min_voltage_V;								% not used, for now (see model)
        
        % in original pre-REVS ALPHA (1.0) model, discharge power limit was
        % used on a per-parallel basis...
        powerlimit_discharge_map_W@class_REVS_dynamic_lookup;	% Discharging Power Limit [Watts]
        powerlimit_charge_map_W@class_REVS_dynamic_lookup;      % Charging Power Limit [Watts]
        
        discharge_derate_soc_norm;					% Derating for minimum SOC limit [0-1]
        charge_derate_soc_norm;						% Derating for maximum SOC limit [0-1]
        
        use_average_open_circuit_Volts;				% Use Average Open Circut Voltage Map instead of charging & discharging maps
        
        open_circuit_voltage_map_discharge_V@class_REVS_dynamic_lookup;     % Open Circuit Voltage Map while Discharging [V]
        open_circuit_voltage_map_charge_V@class_REVS_dynamic_lookup;        % Open Circuit Voltage Map while Charging [V]
        
        voltage_transient_cal_norm = 1.0;           % calibration factor from Soduk, shifts entire pack voltage
        
        transient_R_discharge_short_term_Ohms@class_REVS_dynamic_lookup;	% Transient response short term RC resistance when discharging [Ohms]
        transient_R_charge_short_term_Ohms@class_REVS_dynamic_lookup;		% Transient response short term RC resistance when charging [Ohms]
        transient_R_discharge_long_term_Ohms@class_REVS_dynamic_lookup;     % Transient response long term RC resistance when discharging [Ohms]
        transient_R_charge_long_term_Ohms@class_REVS_dynamic_lookup;		% Transient response long term RC resistance when charging [Ohms]
        
        resistance_map_discharge_Ohms@class_REVS_dynamic_lookup;			% Internal Resistance map while discharging [Ohms]
        resistance_map_charge_Ohms@class_REVS_dynamic_lookup;				% Internal Resistance map while charging [Ohms]
        
        transient_C_discharge_short_term_Farads@class_REVS_dynamic_lookup;  % Transient response short term RC capacitance when discharging [Farads]
        transient_C_charge_short_term_Farads@class_REVS_dynamic_lookup;     % Transient response short term RC capacitance when charging [Farads]
        transient_C_discharge_long_term_Farads@class_REVS_dynamic_lookup;	% Transient response long term RC capacitance when discharging [Farads]
        transient_C_charge_long_term_Farads@class_REVS_dynamic_lookup;      % Transient response long term RC capacitance when charging [Farads]
        
        charge_efficiency_map_norm@class_REVS_dynamic_lookup;               % Charging efficiency map [0-1]
        
        temperature_power_derate_index_temp_degC;	% Power Limit Temperature Derate axis [degC]
        temperature_power_discharge_derate_norm;	% Power Limit Temperature Derate map when discharging [0-1]
        temperature_power_charge_derate_norm;		% Power Limit Temperature Derate map when charging [0-1]
        
        cell = class_REVS_battery_cell;     % Battery Cell properties (if available) as class class_REVS_battery_cell
        
        thermal = class_REVS_battery_thermal;		% Battery thermal model as class class_REVS_battery_thermal;
    end
    
    properties ( Hidden = true, SetAccess = protected)
        private_series;
        private_parallel;
        private_cells;
    end
    
    properties (Dependent = true, Transient = true)
        % 		num_series_cells;							% Number of cells connected in series in the pack
        % 		num_parallel_cells;							% Number of cells or series collections of cells in paralel within the pack
        % 		num_cells;									% Total number of cells in the pack
        
        capacity_nominal_Ah;						% Cell Nominal Capacity [Amp*Hours]
        
%         open_circuit_voltage_map_V;					% Open Circuit Voltage Combined [V] - for future use
%         transient_R_short_term_Ohms;				% Transient response short term RC resistance [Ohms] - for future use
%         transient_R_long_term_Ohms;					% Transient response long term RC resistance [Ohms] - for future use
%         transient_C_short_term_Farads;				% Transient response short term RC capacitance [Farads] - for future use
%         transient_C_long_term_Farads;				% Transient response long term RC capacitance [Farads] - for future use
%         resistance_map_Ohms;						% Internal Resistance Map [Ohms] - for future use
    end
    
    methods
        %Constructor
        function obj = class_REVS_battery()
            obj.powerlimit_discharge_map_W              = class_REVS_dynamic_lookup;
            obj.powerlimit_charge_map_W                 = class_REVS_dynamic_lookup;
            
            obj.open_circuit_voltage_map_discharge_V    = class_REVS_dynamic_lookup;
            obj.open_circuit_voltage_map_charge_V       = class_REVS_dynamic_lookup;
            
            obj.transient_R_discharge_short_term_Ohms   = class_REVS_dynamic_lookup;
            obj.transient_R_charge_short_term_Ohms      = class_REVS_dynamic_lookup;
            obj.transient_R_discharge_long_term_Ohms    = class_REVS_dynamic_lookup;
            obj.transient_R_charge_long_term_Ohms       = class_REVS_dynamic_lookup;
            
            obj.resistance_map_discharge_Ohms           = class_REVS_dynamic_lookup;
            obj.resistance_map_charge_Ohms              = class_REVS_dynamic_lookup;
            
            obj.transient_C_discharge_short_term_Farads = class_REVS_dynamic_lookup;
            obj.transient_C_charge_short_term_Farads    = class_REVS_dynamic_lookup;
            obj.transient_C_discharge_long_term_Farads  = class_REVS_dynamic_lookup;
            obj.transient_C_charge_long_term_Farads     = class_REVS_dynamic_lookup;
            
            obj.charge_efficiency_map_norm              = class_REVS_dynamic_lookup;
        end
        
        function obj = set.capacity_nominal_Ah(obj,val)
            obj.capacity_Coulombs = val * 3600;
        end
        
        function val = get.capacity_nominal_Ah(obj)
            val = obj.capacity_Coulombs / 3600;
        end
        
        function val = get.chemistry(obj)
            val = REVS_class_default( obj.chemistry, obj.cell.chemistry);
        end
        
        function val = get.capacity_Coulombs(obj)
            val = REVS_class_default(obj.capacity_Coulombs , obj.cell.capacity_Coulombs .* obj.num_parallel_cells);
        end
        
        function val = get.max_voltage_V(obj)
            if ~isempty(obj.max_voltage_V)
                val = obj.max_voltage_V;
            elseif ~isempty(obj.cell.open_circuit_voltage_map_charge_V.table)
                val = max(obj.cell.open_circuit_voltage_map_charge_V.table(:)) * obj.num_series_cells;
            else
                val = max(obj.open_circuit_voltage_map_charge_V.table(:));
            end            
        end
        
        function  val = get.min_voltage_V(obj)
            if ~isempty(obj.min_voltage_V)
                val = obj.min_voltage_V;
            elseif ~isempty(obj.cell.open_circuit_voltage_map_discharge_V.table)
                val = min(obj.cell.open_circuit_voltage_map_discharge_V.table(:)) * obj.num_series_cells;
            else
                val = min(obj.open_circuit_voltage_map_discharge_V.table(:));
            end            
        end
        
        function obj = set.num_series_cells(obj, val)
            if isempty( obj.private_cells ) || isempty( obj.private_parallel ) || ( val == obj.private_cells / obj.private_parallel )
                obj.private_series = val;
            else
                warning('Invalid battery definition num_series_cells * num_parallel_cells must equal num_cells');
            end
        end
        
        function val = get.num_series_cells( obj)
            if ~isempty(obj.private_series)
                val = obj.private_series;
            elseif ~isempty( obj.private_cells) && ~isempty( obj.private_parallel)
                val = obj.private_cells ./ obj.private_parallel;
            else
                val = [];
            end
        end
        
        function obj = set.num_parallel_cells(obj, val)
            if isempty( obj.private_cells ) || isempty( obj.private_series ) || ( val == obj.private_cells / obj.private_series )
                obj.private_parallel = val;
            else
                warning('Invalid battery definition num_series_cells * num_parallel_cells must equal num_cells');
            end
        end
        
        function val = get.num_parallel_cells(obj)
            if ~isempty(obj.private_parallel)
                val = obj.private_parallel;
            elseif ~isempty( obj.num_cells) && ~isempty( obj.private_series)
                val = obj.num_cells ./ obj.private_series;
            else
                val = [];
            end
        end
        
        function obj = set.num_cells(obj, val)
            if isempty( obj.private_parallel ) || isempty( obj.private_series ) || ( val == obj.private_parallel * obj.private_series )
                obj.num_cells = val;
            else
                warning('Invalid battery definition num_series_cells * num_parallel_cells must equal num_cells');
            end
        end
        
        function val = get.num_cells(obj)
            if ~isempty(obj.private_cells)
                val = obj.num_cells;
            elseif ~isempty( obj.private_parallel) && ~isempty( obj.private_series)
                val = obj.private_parallel .* obj.private_series;
            else
                val = [];
            end
        end
        
        function val = get.powerlimit_discharge_map_W(obj)
            if isempty(obj.powerlimit_discharge_map_W)
                val = obj.cell.powerlimit_discharge_map_W;
                val.table = val.table * obj.num_cells;
            else
                val = obj.powerlimit_discharge_map_W;
            end
        end
        
        function val = get.powerlimit_charge_map_W(obj)
            if isempty(obj.powerlimit_charge_map_W)
                val = obj.cell.powerlimit_charge_map_W;
                val.table = val.table * obj.num_cells;
            else
                val = obj.powerlimit_charge_map_W;
            end
        end
        
        function val = get.open_circuit_voltage_map_discharge_V(obj)
            if isempty(obj.open_circuit_voltage_map_discharge_V)
                val = obj.cell.open_circuit_voltage_map_discharge_V;
                val.table = val.table * obj.num_series_cells;
                
            else
                val =  obj.open_circuit_voltage_map_discharge_V;
            end
        end
        
        function val = get.open_circuit_voltage_map_charge_V(obj)
            if isempty(obj.open_circuit_voltage_map_charge_V)
                val = obj.cell.open_circuit_voltage_map_charge_V;
                val.table = val.table * obj.num_series_cells;
            else
                val =  obj.open_circuit_voltage_map_charge_V;
            end
        end
        
        function val = get.transient_R_discharge_short_term_Ohms(obj)
            if isempty(obj.transient_R_discharge_short_term_Ohms)
                val = obj.cell.transient_R_discharge_short_term_Ohms;
                val.table = val.table * obj.num_series_cells / obj.num_parallel_cells;
            else
                val = obj.transient_R_discharge_short_term_Ohms;
            end
        end
        
        function val = get.transient_R_charge_short_term_Ohms(obj)
            if isempty(obj.transient_R_charge_short_term_Ohms)
                val = obj.cell.transient_R_charge_short_term_Ohms;
                val.table = val.table * obj.num_series_cells / obj.num_parallel_cells;
            else
                val = obj.transient_R_charge_short_term_Ohms;
            end
        end
        
        function val = get.transient_R_discharge_long_term_Ohms(obj)
            if isempty(obj.transient_R_discharge_long_term_Ohms)
                val = obj.cell.transient_R_discharge_long_term_Ohms;
                val.table = val.table * obj.num_series_cells / obj.num_parallel_cells;
            else
                val = obj.transient_R_discharge_long_term_Ohms;
            end
        end
        
        function val = get.transient_R_charge_long_term_Ohms(obj)
            if isempty(obj.transient_R_charge_long_term_Ohms)
                val = obj.cell.transient_R_charge_long_term_Ohms;
                val.table = val.table * obj.num_series_cells / obj.num_parallel_cells;
            else
                val = obj.transient_R_charge_long_term_Ohms;
            end
        end
        
        function val = get.resistance_map_discharge_Ohms(obj)
            if isempty(obj.resistance_map_discharge_Ohms)
                val = obj.cell.resistance_map_discharge_Ohms;
                val.table = val.table * obj.num_series_cells / obj.num_parallel_cells;
            else
                val = obj.resistance_map_discharge_Ohms;
            end
        end
        
        function val = get.resistance_map_charge_Ohms(obj)
            if isempty(obj.resistance_map_charge_Ohms)
                val = obj.cell.resistance_map_charge_Ohms;
                val.table = val.table * obj.num_series_cells / obj.num_parallel_cells;
            else
                val = obj.resistance_map_charge_Ohms;
            end
        end
        
        function val = get.transient_C_discharge_short_term_Farads(obj)
            if isempty(obj.transient_C_discharge_short_term_Farads)
                val = obj.cell.transient_C_discharge_short_term_Farads;
                val.table = val.table / obj.num_series_cells * obj.num_parallel_cells;
            else
                val = obj.transient_C_discharge_short_term_Farads;
            end
        end
        
        function val = get.transient_C_charge_short_term_Farads(obj)
            if isempty(obj.transient_C_charge_short_term_Farads)
                val = obj.cell.transient_C_charge_short_term_Farads;
                val.table = val.table / obj.num_series_cells * obj.num_parallel_cells;
            else
                val = obj.transient_C_charge_short_term_Farads;
            end
        end
        
        function val = get.transient_C_discharge_long_term_Farads(obj)
            if isempty(obj.transient_C_discharge_long_term_Farads)
                val = obj.cell.transient_C_discharge_long_term_Farads;
                val.table = val.table / obj.num_series_cells * obj.num_parallel_cells;
            else
                val = obj.transient_C_discharge_long_term_Farads;
            end
        end
        
        function val = get.transient_C_charge_long_term_Farads(obj)
            if isempty(obj.transient_C_charge_long_term_Farads)
                val = obj.cell.transient_C_charge_long_term_Farads;
                val.table = val.table / obj.num_series_cells * obj.num_parallel_cells;
            else
                val = obj.transient_C_charge_long_term_Farads;
            end
        end
        
        function val = get.charge_efficiency_map_norm(obj)
            if isempty(obj.charge_efficiency_map_norm)
                val = obj.cell.charge_efficiency_map_norm;
            else
                val = obj.charge_efficiency_map_norm;
            end
        end
        
        function val = get.temperature_power_discharge_derate_norm(obj)
            val =  REVS_class_default( obj.temperature_power_discharge_derate_norm,  obj.cell.temperature_power_discharge_derate_norm);
        end
        
        function val = get.temperature_power_charge_derate_norm(obj)
            val =  REVS_class_default( obj.temperature_power_charge_derate_norm,  obj.cell.temperature_power_charge_derate_norm);
        end
        
        function val = get.temperature_power_derate_index_temp_degC(obj)
            val =  REVS_class_default( obj.temperature_power_derate_index_temp_degC,  obj.cell.temperature_power_derate_index_temp_degC);
        end
        
    end
    
end

