Skip to content

Commit

Permalink
Time control work (cherry picked from Xavier)
Browse files Browse the repository at this point in the history
  • Loading branch information
augustjohansson committed Jul 3, 2024
1 parent 53a030a commit 6261bd9
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 3 deletions.
4 changes: 2 additions & 2 deletions Battery/BatteryInputParams.m
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@
inputparams.(ctrl) = CcCvControlModelInputParams(pick(ctrl));
case 'powerControl'
inputparams.(ctrl) = PowerControlModelInputParams(pick(ctrl));
case 'CC'
inputparams.(ctrl) = CcControlModelInputParams(pick(ctrl));
case 'timecontrol'
inputparams.(ctrl) = TimeControlModelInputParams(pick(ctrl));
otherwise
error('controlPolicy %s not recognized', jsonstruct.(ctrl).controlPolicy);
end
Expand Down
21 changes: 20 additions & 1 deletion Battery/GenericBattery.m
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@
fn = @GenericBattery.updateControl;
fn = {fn, @(propfunction) PropFunction.drivingForceFuncCallSetupFn(propfunction)};
switch model.(ctrl).controlPolicy
case {'CCDischarge', 'CCCharge', 'CC'}
case {'CCDischarge', 'CCCharge', 'CC', 'timecontrol'}
model = model.registerPropFunction({{ctrl, 'ctrlVal'}, fn, inputnames});
case {'CCCV', 'Impedance'}
% do nothing
Expand Down Expand Up @@ -520,6 +520,10 @@ function printJsonStruct(model)

switch inputparams.controlPolicy

case 'timecontrol'

control = TimeControlModel(inputparams);

case "Impedance"

control = ImpedanceControlModel(inputparams);
Expand Down Expand Up @@ -886,6 +890,12 @@ function printJsonStruct(model)

initstate.(ctrl).I = 0;

case {'timecontrol'}

% We initiate to some values, but they should be overriden as the simulation starts
initstate.(ctrl).I = 0;
initstate.(ctrl).ctrlType = 'constantCurrent';

case {'CCDischarge', 'CCCharge'}

initstate.(ctrl).ctrlType = 'constantCurrent';
Expand Down Expand Up @@ -1147,6 +1157,13 @@ function printJsonStruct(model)

% nothing to do here

case {'timecontrol'}

[ctrlVal, ctrlType] = model.(ctrl).computeInput(state.time);

state.(ctrl).ctrlVal = ctrlVal;
state.(ctrl).ctrlType = ctrlType;

case {'CCDischarge', 'CCCharge'}

Imax = model.(ctrl).Imax;
Expand Down Expand Up @@ -1630,6 +1647,8 @@ function printJsonStruct(model)
forces.powerControl = true;
case 'CC'
forces.CC = true;
case 'timecontrol'
forces.timecontrol = true;
case 'None'
% used only in addVariables
otherwise
Expand Down
1 change: 1 addition & 0 deletions Control/ControlModel.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
% - 'CCCharge'
% - 'CC'
% - 'CCCV'
% - 'timecontrol'
%
controlPolicy

Expand Down
215 changes: 215 additions & 0 deletions Control/TimeControlModel.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
classdef TimeControlModel < ControlModel


properties

usetable % true if table is used

times % Array with time value (should include at the end the end time, so that length(times) = length(durations) + 1)
durations % Array with time value
values % Array with control value
controltypes % Array with control type. The convention is
% - 1 for current
% - 2 for voltage

usefunction % true if we use a matlab function

functionname % function name, should be in matlab path

% Advanced parameters

tolerance = 1e-4 % tolerance to skip timesteps (in second)

% Helpers

computeInput % function called to give update

use_durations % Setup when usetable is true
functionhandler % Setup when usefunction is true

end

methods

function model = TimeControlModel(inputparams)

model = model@ControlModel(inputparams);

fdnames = {'usetable' , ...
'times' , ...
'durations' , ...
'values' , ...
'controltypes', ...
'usefunction' , ...
'functionname' };

model = dispatchParams(model, inputparams, fdnames);

if model.usetable

model.computeInput = @(t) model.computeInputFromTable(t);

end

if model.usefunction

model.functionhandler = str2func(model.functionname);
model.computeInput = @(t) model.computeInputFromFunction(t);

end

end

function model = registerVarAndPropfuncNames(model)

model = registerVarAndPropfuncNames@ControlModel(model);

varnames = {};
% Control type (string)
% - 'constantCurrent'
% - 'constantVoltage'
varnames{end + 1} = 'ctrlType';
% control value that can be either a voltage or a current
varnames{end + 1} = 'ctrlVal';

model = model.registerVarNames(varnames);

fn = @CTimeControlModel.updateControlEquation;
model = model.registerPropFunction({'controlEquation', fn, {'ctrlType', 'ctrlVal', 'E', 'I'}});

end

function [ctrlVal, ctrlType] = computeInputFromTable(model, t)

% We could be more efficient here and keep track of previous index to avoid full search (the time spent for
% that is probabely negligeable compared to the rest.)
ind = find(t >= model.times, 1, 'last');

if t > model.times(end)
error('outside of time table')
elseif t == model.times(end)
ind = numel(model.values);
end

ctrlVal = model.values(ind);
ctrlType = model.controltypes(ind);

switch ctrlType
case 1
ctrlType = 'constantCurrent';
case 2
ctrlType = 'constantVoltage';
otherwise
error('ctrlType not recognized. It should be equal to 1 or 2')
end

end

function [ctrlVal, ctrlType] = computeInputFromFunction(model, t)

[ctrlVal, ctrlType] = model.functionhandler(t);

end

function state = updateControlEquation(model, state)

E = state.E;
I = state.I;
ctrlVal = state.ctrlVal;
ctrlType = state.ctrlType;

switch ctrlType

case 'constantCurrent'

ctrleq = I - ctrlVal;

case 'constantVoltage'

%% TODO : fix hard-coded scaling
ctrleq = (E - ctrlVal)*1e5;

otherwise

error('ctrlType not recognized');

end

state.controlEquation = ctrleq;

end

function cleanState = addStaticVariables(model, cleanState, state)

cleanState.ctrlType = state.ctrlType;

end

function func = setupControlFunction(model)

func = [];

end

function step = setupScheduleStep(model, timeSteppingParams)

if (nargin > 1)
params = timeSteppingParams;
else
params = [];
end

if model.usetable

end
% Call parser for TimeStepping structure with some default values
params = model.parseTimeSteppingStruct(params);

totalTime = model.times(end);

givendt = false;
if ~isempty(params.timeStepDuration)
dt = params.timeStepDuration;
givendt = true;
elseif ~isempty(params.timeStepDuration)
n = params.numberOfTimeSteps;
dt = totalTime/n;
givendt = true;
end

if givendt
if params.useRampup
n = params.numberOfRampupSteps;
else
n = 0;
end

dts = rampupTimesteps(totalTime, dt, n);

end

if model.usetable

time = [0; cumsum(model.durations)];

if givendt
time1 = [0; cumsum(dts)];

time = [time; time1];
time = sort(time);
time = uniquetol(time, model.tolerance);
end

dts = diff(time);

end

step = struct('val', dts, 'control', ones(numel(dts), 1));

end

end


end

51 changes: 51 additions & 0 deletions Control/TimeControlModelInputParams.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
classdef TimeControlModelInputParams < ControlModelInputParams

properties

usetable % true if table is used

times % Array with time value (should include at the end the end time, so that length(times) = length(durations) + 1)
durations % Array with duration value
values % Array with control value
controltypes % Array with control type. The convention is
% - 1 for current
% - 2 for voltage

usefunction % true if we use a matlab function

functionname % function name, should be in matlab path

end

methods
function inputparams = TimeControlModelInputParams(jsonstruct);

jsonstruct = setDefaultJsonStructField(jsonstruct, 'usetable', true);
jsonstruct = setDefaultJsonStructField(jsonstruct, 'usefunction', false);

usetable = getJsonStructField(jsonstruct, 'usetable');
usefunction = getJsonStructField(jsonstruct, 'usefunction');

assert(xor(usetable, usefunction), 'options usetable and usefunction are not compatible');

if usetable
if isAssigned(jsonstruct, 'durations')
durations = getJsonStructField(jsonstruct, 'durations');
if isAssigned(jsonstruct, 'times')
error('cannot assign both durations and times')
else
jsonstruct.times = [0; cumsum(durations)];
end
else
times = getJsonStructField(jsontruct, 'times');
jsonstruct.durations = diff(times);
end
end

inputparams = inputparams@ControlModelInputParams(jsonstruct);

end
end

end

41 changes: 41 additions & 0 deletions Examples/Basic/time_control.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"controlPolicy" : "timecontrol",

"durations" : [100,
100,
200,
100,
100,
300,
100,
100,
300,
100,
100,
100],

"values" : [3.9,
3.9,
3e-3,
3e-3,
3e-3,
3e-3,
-1e-3,
-1e-3,
-3e-3,
-1e-3,
-1e-3,
-1e-3],

"controltypes" : [2,
2,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1]}

0 comments on commit 6261bd9

Please sign in to comment.