Uno de los inconvenientes asociado a la implementaci´on de un algoritmo de estas caracter´ısticas es la gran latencia que supone la integraci´on de las ecuaciones de movimiento de cada una de las part´ıculas. Es por ello que uno de los aspectos novedosos del algoritmo DEM aqu´ı presentado es haber sido dise˜nado para ser ejecutado en paralelo, sobre GPU. Esto permite simular sistemas peque˜nos, en n´umero de part´ıculas, en tiempos de ejecuci´on muy cortos o, por el contrario, simular sistemas grandes, del orden de hasta un mill´on de part´ıculas, en tiempos razonables (impensables empleando un m´etodo secuencial). Una de las partes m´as importantes de este trabajo es la implementaci´on de nuevas funcionalidades y la optimizaci´on del algoritmo DEM desarrollado por Hidalgo et al. [25]. En esta secci´on se detallan rasgos caracter´ısticos de la implementaci´on del algoritmo DEM construido con CUDA.
Tal y como ocurre en la mayor´ıa de las aplicaciones GPGPU ciertas partes del algoritmo se llevan a cabo sobre la CPU mientras que otras se ejecutan en la GPU. En Fig. 3.1 se muestra el diagrama de flujo del m´etodo implementado, donde todos los procesos que se ejecutan en la CPU han sido marcados en cuadros de trazo continuo, mientras que las rutinas que se realizan en paralelo, sobre la GPU, se marcan con l´ıneas de trazos.
Este c´odigo ha sido implementado empleando el lenguaje C/C++ con la extensi´on CUDA de NVIDIA. El programa comienza con la inicializaci´on del driver de CUDA, necesario para tener acceso a las utilidades de GPU. Debido a la existencia de esa duplicidad de memoria, diferentes regiones para GPU y CPU, el diagrama de flujo de Fig. 3.1 contin´ua con la declaraci´on y reserva de memoria de todas las variables para ambos, host y device. La inicializaci´on de los par´ametros y rasgos de cada part´ıcula se hace a trav´es de la asignaci´on de valores a las variables de CPU para, m´as tarde, ser transferidas a las correspondientes variables de GPU a trav´es de la funci´on cudaMemcpy de CUDA.
Finalizada la puesta a punto de la configuraci´on inicial del sistema, da comienzo el bucle temporal del algoritmo DEM. Ya se ha indicado que la integraci´on de las ecuaciones de movimiento traslacional se realiza mediante un integrador tipo Velocity Verlet [24]. Este m´etodo realiza la integraci´on en dos etapas. Al comienzo del bucle temporal, toma las aceleraciones obtenidas para cada part´ıcula en la iteraci´on anterior, y calcula la velocidad en la mitad de la presente iteraci´on, adem´as de la posici´on actual de las part´ıculas. La segunda etapa es ejecutada al final de la iteraci´on, calculando s´olo la velocidad de las part´ıculas al final del intervalo.
En ambos casos la forma de proceder para su implementaci´on sobre GPU es semejante. Para aprovechar la potencia de las funciones implementadas en la librer´ıa Thrust [26], se construye des- de la CPU una estructura de datos relacional propia, en la que para cada part´ıcula se genera una nueva tupla o tanda de datos que almacena su aceleraci´on, velocidad y posici´on. Posteriormente, se traspasa el control a la GPU, y esta, a trav´es de un iterador de Thrust, integra la ecuaci´on Ec. 3.1 en paralelo, asignando las part´ıculas a diferentes threads.
La gran ventaja de usar los iteradores de la librer´ıa Thrust es que el n´umero de hilos y bloques est´a completamente optimizado: en tiempo de c´alculo, de acuerdo al n´umero de tuplas y la complejidad de la funci´on, se decide la cantidad de bloques y threads por bloque que se ejecutar´an. Sin embargo, la gran limitaci´on de esta metodolog´ıa es que el tama˜no total de las tuplas es muy restrictivo, limitando el tipo de funciones que pueden paralelizarse de este modo.
Figura 3.1: Diagrama de flujo del algoritmo DEM implementado. Por defecto, debe entenderse que todas las funciones se ejecutan en la CPU. S´olo se ejecutan en paralelo sobre la GPU, las tareas que aparecen en cajas sobrepuestas con trazo discontinuo. En cada caso se muestra el n´umero de threads, el cual var´ıa de unos kernels a otros.
en eficiencia, se debe detectar los pares de part´ıculas susceptibles de producir una colisi´on. Esto se implementa haciendo uso de una lista de vecinos, dise˜nado a partir de un m´etodo tipo link cell [11] ofrecido por por el Toolkit de NVIDIA. Puesto que en el m´etodo DEM todas las part´ıculas cambian su posici´on en cada iteraci´on temporal, la lista deber´ıa ser actualizada continuamente. Sin embargo, nuestro m´etodo est´a optimizado de modo que, en funci´on de la densidad del medio granular, y de la discretizaci´on temporal, dt, que se est´e empleando, se fija un n´umero de ite- raciones para actualizar la lista. Por ejemplo, en sistemas muy comprimidos, la lista de vecinos de una part´ıcula puede no variar en absoluto durante toda la simulaci´on. Por el contrario, en sistemas muy diluidos, puede darse el caso extremo en que sea necesaria la actualizaci´on de la lista constantemente.
Finalizado el proceso de detecci´on de colisiones, Fig. 3.1, el algoritmo contin´ua con la ejecuci´on de colisiones. Cada part´ıcula recorre su lista de vecinos tratando de calcular la fuerza y el torque que cada uno de esos posibles contactos ejerce sobre ella. Al terminar de recorrer su lista, se actualizan los nuevos valores de aceleraci´on lineal y angular.
A la hora de caracterizar un sistema de part´ıculas con DEM es necesario fijar, por una parte, el n´umero de celdas en que ser´a dividido el espacio de c´alculo y, por otro lado, la relaci´on que hay entre los vol´umenes de cada una de estas casillas y el volumen de una part´ıcula. De hecho, la elecci´on de estos dos par´ametros es un factor que influye en la eficiencia del m´etodo DEM sobre GPU en varios puntos del m´etodo. Aunque en este trabajo no se muestra un estudio comparativo sobre el impacto que ocasiona la elecci´on de diferentes tama˜nos de celda y part´ıcula, el criterio utilizado parece ser el m´as eficiente. De cara a optimizar la tarea asignada a cada warp, el n´umero de celdas siempre se escoge como una potencia de 2. Adem´as, para minimizar el coste computacional en la detecci´on de vecinos y ejecuci´on de colisiones el volumen de las part´ıculas es siempre inferior al de la celda, lo cual suele revertir en un n´umero de part´ıculas que tambi´en es un m´ultiplo del tama˜no del warp. Tanto la detecci´on como la ejecuci´on de colisiones se han implementado en CUDA con kernel est´andar. En el caso de detecci´on de colisiones el n´umero de bloques e hilos se han ajustado en base al n´umero de celdas en que se ha dividido todo el volumen de c´alculo. Sin embargo, en el proceso de ejecuci´on de colisiones estos par´ametros se han obtenido a partir del n´umero de part´ıculas que se est´an simulando.