5.4.1.1.3 : La fonction générale du programme
Maintenant, implémentons la fonctions générale :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
///Simulate the images
/** @param nbRow : number of rows of the images to be created
* @param nbCol : number of columns of the images to be created
* @param nbImage : number of images to be created
* @param nbExtraStep : number of extra steps to be computed between images
* @param killRate : rate of the process which converts V into P
* @param feedRate : rate of the process which feeds U and drains U, V and P
* @param dt : time interval between two computation
* @param outputFile : name of the file to be created
* @param blockSizeRow : number of rows of the blocks
* @param blockSizeCol : number of columns of the blocks
* @return true on succsess, false otherwise
*/
bool simulateImage(size_t nbRow, size_t nbCol, size_t nbImage, size_t nbExtraStep, float killRate, float feedRate, float dt, const std::string & outputFile,
size_t blockSizeRow, size_t blockSizeCol)
{
|
Nous allons commencer par indiquer quelques paramètres utiles pour l'utilisateur :
1
|
std::cout << "simulateImage : nbImage = "<<nbImage<<", nbRow = " << nbRow << ", nbCol = " << nbCol << std::endl;
|
Ensuite, nous devons définir l'ensemble de nos images en
HDF5 :
1
2
3
|
MatrixHdf5 fullMat;
fullMat.setAllDim(nbCol, nbRow);
fullMat.resize(nbImage);
|
On créé et on initialise les
PTensor et les pointeurs de nos images temporaires :
1
2
3
|
PTensor<float> tmpInU, tmpInV, tmpOutU, tmpOutV;
float *tmpU1 = NULL, *tmpU2 = NULL, *tmpV1 = NULL, *tmpV2 = NULL;
allocate_temporary(tmpU1, tmpU2, tmpV1, tmpV2, tmpInU, tmpInV, tmpOutU, tmpOutV, nbRow, nbCol);
|
On définit notre stencil
3x3 (avec éventuellement des coéfficients plus sérieux mais qui donnent des résultats moins rigolos) :
1
2
3
4
5
6
7
8
9
10
|
long nbStencilRow(3l), nbStencilCol(3l);
float diffudionRateU(0.1f), diffusionRateV(0.05f);
float matDeltaSquare[] = {1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f};
|
Cependant, nous devons maintenant convertir tout nos temporaires afin qu'ils soient complètement vectorisables :
1
2
3
4
5
6
|
PTensor<float> tmpVecInU(AllocMode::ALIGNED), tmpVecInV(AllocMode::ALIGNED), tmpVecOutU(AllocMode::ALIGNED), tmpVecOutV(AllocMode::ALIGNED);
tmpVecInU.fromScalToVecNeigbhour(tmpInU, PLIB_VECTOR_SIZE_FLOAT);
tmpVecOutU.fromScalToVecNeigbhour(tmpOutU, PLIB_VECTOR_SIZE_FLOAT);
tmpVecInV.fromScalToVecNeigbhour(tmpInV, PLIB_VECTOR_SIZE_FLOAT);
tmpVecOutV.fromScalToVecNeigbhour(tmpOutV, PLIB_VECTOR_SIZE_FLOAT);
|
La vectorisation de la matrice qui définit les poids du gradient :
1
2
|
PTensor<float> vecMatDeltaSquare(AllocMode::ALIGNED, nbStencilRow, nbStencilCol*PLIB_VECTOR_SIZE_FLOAT);
reshuffle_broadcastTensor(vecMatDeltaSquare.getData(), matDeltaSquare, nbStencilRow, nbStencilCol, 0lu, PLIB_VECTOR_SIZE_FLOAT);
|
On définit les pointeurs correspondants :
1
|
float * ptrVecMatStencil = vecMatDeltaSquare.getData();
|
Et le résultat de chaque image :
1
|
PTensor<float> tmpScalOutV(AllocMode::ALIGNED);
|
Nous devons également créer nos vecteur de blocs qui seront liés à leur tenseur respectif, ce qui nous économisera une copie de donnée :
1
2
3
4
5
|
std::vector<PBlock<float> > vecBlockOutU, vecBlockOutV, vecBlockInU, vecBlockInV;
tmpVecOutU.splitBlockLink(vecBlockOutU, blockSizeRow, blockSizeCol, 1lu);
tmpVecOutV.splitBlockLink(vecBlockOutV, blockSizeRow, blockSizeCol, 1lu);
tmpVecInU.splitBlockLink(vecBlockInU, blockSizeRow, blockSizeCol, 1lu);
tmpVecInV.splitBlockLink(vecBlockInV, blockSizeRow, blockSizeCol, 1lu);
|
On défint la nouvelle taille des matrices :
1
2
|
size_t nbVecRow(tmpVecInV.getFullNbRow()), nbVecCol(tmpVecInV.getNbCol());
|
On créé notre barre de chargement :
1
2
|
ProgressTime progress(nbImage);
progress.start();
|
On boucle sur toutes les images que l'on veut créer :
1
|
for(size_t i(0lu); i < nbImage; ++i){
|
Petite mise à jour de la barre de chargement :
On boucle sur les image supplémentaires à calculer entre deux images à sauvegarder :
1
|
for(size_t j(0lu); j < nbExtraStep/2lu; ++j){
|
On appelle notre calcul de
Gray Scott :
1
2
3
4
|
grayscott_propagation_link_block(
vecBlockOutU, vecBlockOutV, vecBlockInU, vecBlockInV,
ptrVecMatStencil, nbStencilRow, nbStencilCol,
diffudionRateU, diffusionRateV, feedRate, killRate, dt);
|
Il ne faut pas oublier que notre calcul se fait que sur la moitier des valeurs duppliquées, il faut donc les mettre à jour :
1
2
3
|
reshuffle_updateDupplicateVecNeighbour(tmpVecOutU.getData(), nbVecRow, nbVecCol, PLIB_VECTOR_SIZE_FLOAT);
reshuffle_updateDupplicateVecNeighbour(tmpVecOutV.getData(), nbVecRow, nbVecCol, PLIB_VECTOR_SIZE_FLOAT);
|
Plutôt que d'intervertir les pointeurs
input et
output, il est plus simple de faire deux fois moins d'étapes doubles :
1
2
3
4
5
|
///Let's swap the in and out tensors
grayscott_propagation_link_block(
vecBlockInU, vecBlockInV, vecBlockOutU, vecBlockOutV,
ptrVecMatStencil, nbStencilRow, nbStencilCol,
diffudionRateU, diffusionRateV, feedRate, killRate, dt);
|
Il ne faut pas oublier que notre calcul se fait que sur la moitier des valeurs duppliquées, il faut donc les mettre à jour :
1
2
3
4
|
reshuffle_updateDupplicateVecNeighbour(tmpVecInU.getData(), nbVecRow, nbVecCol, PLIB_VECTOR_SIZE_FLOAT);
reshuffle_updateDupplicateVecNeighbour(tmpVecInV.getData(), nbVecRow, nbVecCol, PLIB_VECTOR_SIZE_FLOAT);
}
|
On récupère l'image que l'on veut sauvegarder (en faisant attention car on interverti les pointeurs régulièrement) :
1
|
tmpScalOutV.fromVecToScalNeigbhour(tmpVecInV);
|
On sauvegarde l'image que l'on veut :
1
2
|
fullMat.setRow(i, tmpScalOutV.getData());
}
|
On termine la barre de chargement :
1
2
|
progress.finish();
std::cerr << "Done" << std::endl;
|
`
On écrit le fichier
HDF5 :
1
2
|
fullMat.write(outputFile);
|
On renvoie
true car tout s'est bien passé et on finit la fonction :