0001 function [result pbeDefinition] = solvePbe(pbeDefinition, varargin)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 tic
0052
0053
0054
0055 optionStruct = mapOptionStruct(varargin, ...
0056 'odeSolver', @ode45, ...
0057 'absTolPosition', 1e-12, ...
0058 'absTolNumber', 1e10, ...
0059 'absTolBulk', 1e-12, ...
0060 'absTolDensity', 1e8, ...
0061 'relTol', 1e-5, ...
0062 'initialTimeStep', 1, ...
0063 'distanceLimit', 1e-8, ...
0064 'binLoad', 1e17, ...
0065 'gradientRelativeDelta', 1e-3, ...
0066 'gradientAbsoluteDelta', 1e-10, ...
0067 'updateUnifiedLimit', 1, ...
0068 'updateTiming', 1, ...
0069 'limitGrowthRate', 1, ...
0070 'useEmbeddingPartitions', 1, ...
0071 'verbose', 2, ...
0072 'density', 0);
0073
0074
0075 if isfield(pbeDefinition, 'optionStruct')
0076 pbeDefinition.optionStruct = mergeStruct(pbeDefinition.optionStruct, ...
0077 optionStruct);
0078 else
0079 pbeDefinition.optionStruct = optionStruct;
0080 end
0081 optionStruct = pbeDefinition.optionStruct;
0082
0083
0084 pbeDefinition = checkInputSolver(pbeDefinition);
0085
0086 [pbeDefinition.seeding seedingTimeVector] = prepareSeeding(...
0087 pbeDefinition.seeding, pbeDefinition, optionStruct);
0088
0089
0090 nextSeedIndex = 1;
0091 eventIndex = [];
0092
0093 seedingTimeVector = [seedingTimeVector pbeDefinition.timeSpan(end)];
0094
0095
0096 solverState = obtainCleanSolverState;
0097
0098 solverState.time = pbeDefinition.timeSpan(1);
0099 solverState.evalTime = toc;
0100
0101 solverState.bulkStateVector = pbeDefinition.bulkInitial(:);
0102 solverState.hcMatrix = zeros(pbeDefinition.nDecCoordinate + ...
0103 pbeDefinition.nExtraCoordinate, 0);
0104 solverState.numberVector = zeros(1, 0);
0105 if optionStruct.density
0106 solverState.probabilityVector = zeros(1, 0);
0107 end
0108
0109 solverState.indexVector = zeros(1,0);
0110 solverState.upVector = zeros(1, 0);
0111 solverState.dissolvedPivotVector = [];
0112
0113 solverState.pivotPropertyMatrix = [];
0114 solverState.bulkPropertyVector = [];
0115
0116 solverState.flagNucleation = 0;
0117 solverState.flagDensity = optionStruct.density;
0118
0119
0120
0121
0122 [initialEventVector, ~, ~] = genericEventFunction(packState(solverState), ...
0123 solverState.time, pbeDefinition, solverState);
0124 if initialEventVector(1) > 0
0125 solverState = addNucleation(solverState, pbeDefinition);
0126 elseif initialEventVector(1) == 0
0127 error('elk:populationBalance:initialNucleation', ...
0128 ['The initial nucleation rate is exactly zero. ' ...
0129 'Please alter the initial nucleation or time slightly to resolve ' ...
0130 'this issue. If you don''t have nucleation, choose a negative ' ...
0131 'nucleation rate']);
0132 end
0133
0134
0135
0136 result = solverState;
0137 result.hcMatrixDynamic = result.hcMatrix;
0138 result.derivativeStruct = struct([]);
0139
0140 result(:) = [];
0141
0142 result(length(pbeDefinition.timeSpan)).time = [];
0143
0144 [result(:).time] = deal(nan);
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158 if optionStruct.verbose,disp('Start solving pbe ..');end
0159 while solverState.time < seedingTimeVector(end)
0160
0161
0162 eventIndex = unique(eventIndex);
0163
0164
0165
0166
0167
0168
0169
0170
0171 particleDissolveIndex = eventIndex(eventIndex > 4) - 4;
0172 while ~isempty(eventIndex)
0173
0174
0175 if eventIndex(1) == 1
0176 if solverState.flagNucleation
0177 disp(' ..stopping nucleation');
0178 solverState = removeNucleation(solverState);
0179
0180 eventIndex(eventIndex == 2) = [];
0181
0182
0183 particleDissolveIndex = particleDissolveIndex - 1;
0184 else
0185 disp(' ..starting of nucleation');
0186 solverState = addNucleation(solverState, pbeDefinition, ...
0187 optionStruct);
0188
0189
0190 particleDissolveIndex = particleDissolveIndex + 2;
0191 end
0192
0193
0194 elseif eventIndex(1) == 2
0195 disp([' ..new nucleation bin, now: ' num2str(sum(...
0196 solverState.indexVector==-1)+2)]);
0197 solverState = newBinForNucleation(solverState, pbeDefinition, ...
0198 optionStruct);
0199
0200
0201 particleDissolveIndex = particleDissolveIndex + 1;
0202
0203
0204 elseif eventIndex(1) == 3
0205 disp(' ..updating unified partition data')
0206 [dump solverState.upVector] = ensureProperDecData(...
0207 solverState.hcMatrix, solverState.upVector, pbeDefinition);
0208
0209
0210 elseif eventIndex(1) == 4
0211
0212
0213
0214
0215
0216 error('elk:pbeSolver:confinedVectorGenerated', ['This ' ...
0217 'simulation generated an unconfined vector. This can ' ...
0218 'happen but you might try an implicit solver or different ' ...
0219 'initial conditions to overcome this issue. An automatic ' ...
0220 'handling of this case is not yet implemented. Sorry.']);
0221
0222
0223
0224
0225
0226
0227 elseif eventIndex > 4 && ...
0228 particleDissolveIndex >= (1+2*solverState.flagNucleation) && ...
0229 particleDissolveIndex <= length(solverState.numberVector)
0230
0231 disp([' ..removing pivot' num2str(particleDissolveIndex(1)) ...
0232 ' .. remaining ' ...
0233 num2str(length(solverState.numberVector)-1)]);
0234
0235 thisPivotClass = solverState.indexVector(particleDissolveIndex(1));
0236 if thisPivotClass > 0
0237 solverState.dissolvedPivotVector(thisPivotClass) = ...
0238 solverState.dissolvedPivotVector(thisPivotClass) + 1;
0239 end
0240 solverState.indexVector(particleDissolveIndex(1)) = [];
0241 solverState.hcMatrix(:, particleDissolveIndex(1)) = [];
0242 solverState.upVector(particleDissolveIndex(1)) = [];
0243
0244 solverState.numberVector(particleDissolveIndex(1)) = [];
0245 if solverState.flagDensity
0246 solverState.probabilityVector(particleDissolveIndex(1)) = [];
0247 end
0248
0249
0250 particleDissolveIndex = particleDissolveIndex - 1;
0251
0252 particleDissolveIndex(1) = [];
0253
0254 else
0255 error('elk:solver:unknownEvent', 'This should not happen.');
0256 end
0257
0258 eventIndex(1) = [];
0259 end
0260
0261
0262 if solverState.time >= seedingTimeVector(nextSeedIndex)
0263
0264 thisHcMatrix = pbeDefinition.seeding(nextSeedIndex).positionMatrix;
0265 thisNumberVector = pbeDefinition.seeding(nextSeedIndex).numberVector;
0266 nDecCoordinate = pbeDefinition.nDecCoordinate;
0267
0268
0269 newPivotFilter = thisNumberVector ~= 0;
0270 nNew = sum(newPivotFilter);
0271 nVoid = sum(~newPivotFilter);
0272
0273
0274 solverState.hcMatrix(:, end + (1:nNew)) = thisHcMatrix(:, ...
0275 newPivotFilter);
0276
0277 solverState.numberVector(1, end + (1:nNew)) = ...
0278 pbeDefinition.seeding(nextSeedIndex).numberVector(newPivotFilter);
0279
0280
0281 [hcMatrixDump solverState.upVector(1, end + (1:nNew)) ~] = ensureProperDecData(...
0282 thisHcMatrix(:, newPivotFilter), ones(1, nNew), pbeDefinition);
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300 solverState.indexVector(1, end + (1:nNew)) = ...
0301 ones(1, nNew)*nextSeedIndex;
0302
0303 solverState.dissolvedPivotVector(end+1) = nVoid;
0304
0305 if solverState.flagDensity
0306 solverState.probabilityVector(1, end + (1:nNew)) = ...
0307 pbeDefinition.seeding(nextSeedIndex).probabilityVector(...
0308 newPivotFilter);
0309 end
0310
0311 nextSeedIndex = nextSeedIndex + 1;
0312 end
0313
0314
0315
0316
0317
0318
0319
0320 thisStateVector = packState(solverState);
0321
0322 if isempty(thisStateVector)
0323 warning('elk:solver:emptyState', ...
0324 'Solver cancelled calculations because of empty state vector');
0325 return
0326 end
0327
0328 thisDerivativeFunction = @(time, state)(genericDerivativeStateVector(...
0329 state, time, pbeDefinition, solverState));
0330
0331 thisEventFunction = @(time, state)(genericEventFunction(state, time, ...
0332 pbeDefinition, solverState));
0333
0334 thisTimeSpan = [solverState.time seedingTimeVector(nextSeedIndex)];
0335
0336 thisTimeSpan(2) = min(thisTimeSpan(2), thisTimeSpan(1) + ...
0337 optionStruct.updateTiming*...
0338 (seedingTimeVector(end) - seedingTimeVector(1)));
0339 thisTimeSpan = [thisTimeSpan(1), ...
0340 pbeDefinition.timeSpan((pbeDefinition.timeSpan > thisTimeSpan(1)) & ...
0341 (pbeDefinition.timeSpan < thisTimeSpan(2))), ...
0342 thisTimeSpan(2)];
0343
0344 thisAbsTol = obtainAbsTolVector(solverState, ...
0345 optionStruct.absTolPosition, ...
0346 optionStruct.absTolNumber, ...
0347 optionStruct.absTolBulk, ...
0348 optionStruct.absTolDensity);
0349
0350
0351 [thisTimeVector, thisStateMatrix, eventTime, eventState, eventIndex] = ...
0352 feval(optionStruct.odeSolver,thisDerivativeFunction, thisTimeSpan, thisStateVector, ...
0353 odeset(...
0354 'absTol', thisAbsTol, ...
0355 'relTol', optionStruct.relTol, ...
0356 'events', thisEventFunction, ...
0357 'InitialStep', optionStruct.initialTimeStep ...
0358 ));
0359 if ~any([1 eventIndex] == 3)
0360 eventIndex = [eventIndex 3];
0361 end
0362
0363 solverState = unpackState(thisStateMatrix(end, :)', thisTimeVector(end), ...
0364 solverState);
0365 if isempty(eventIndex)
0366 disp(['..stopped ode45 at time ' num2str(solverState.time)])
0367 else
0368 disp(['..stopped ode45 for event at time ' num2str(solverState.time) ...
0369 ' with event index ' num2str(eventIndex(:)')])
0370 end
0371
0372
0373 for iTime = 1:length(thisTimeVector)
0374
0375 thisWriteIndex = find(thisTimeVector(iTime) == pbeDefinition.timeSpan, 1);
0376 if isempty(thisWriteIndex)
0377
0378 continue
0379 end
0380
0381
0382 thisResult = unpackState(thisStateMatrix(iTime, :)', thisTimeVector(iTime), ...
0383 solverState);
0384
0385 thisResult.hcMatrixDynamic = thisResult.hcMatrix;
0386
0387 if thisWriteIndex > 1 && ~isnan(result(thisWriteIndex - 1).time)
0388 thisResult.upVector = result(thisWriteIndex - 1).upVector;
0389 end
0390
0391 thisResult.derivativeStruct = genericDerivative(thisResult, ...
0392 thisTimeVector(iTime), pbeDefinition);
0393
0394
0395 [thisResult.hcMatrix thisResult.upVector] = ...
0396 ensureProperDecData(thisResult.hcMatrix, thisResult.upVector, ...
0397 pbeDefinition);
0398
0399
0400 thisResult.pivotPropertyMatrix = pbeDefinition.pivotProperty(...
0401 thisResult.hcMatrix, thisResult.upVector);
0402
0403
0404 thisResult.bulkPropertyVector = pbeDefinition.bulkProperty(...
0405 thisResult.pivotPropertyMatrix, ...
0406 thisResult.numberVector, thisResult.bulkStateVector, ...
0407 thisResult.time);
0408
0409
0410 thisResult.evalTime = toc;
0411 result(thisWriteIndex) = thisResult;
0412 end
0413
0414 end
0415 disp('integration complete')
0416
0417
0418 removeFilter = isnan([result(:).time]);
0419 if any(removeFilter)
0420 warning('elk:pbeSolver:missingOutput', ['Not all requested output '...
0421 'times were generated. Check [result(:).time] for ' ...
0422 num2str(sum(removeFilter)) ' missing entries.'])
0423 end
0424 result(removeFilter) = [];