diff --git a/src/multipole.c b/src/multipole.c new file mode 100644 index 0000000000000000000000000000000000000000..6a3a52c3e74ac74e7ffa3cfd487d82b454cc9472 --- /dev/null +++ b/src/multipole.c @@ -0,0 +1,190 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <float.h> +#include <limits.h> + +/* MPI headers. */ +#ifdef WITH_MPI + #include <mpi.h> +#endif + +/* Local headers. */ +#include "error.h" +#include "const.h" +#include "cycle.h" +#include "atomic.h" +#include "lock.h" +#include "space.h" +#include "part.h" +#include "multipole.h" +#include "cell.h" + + +/** + * @brief Merge two multipoles. + * + * @param ma The #multipole which will contain the merged result. + * @param mb The other #multipole. + */ + +void multipole_merge ( struct multipole *ma , struct multipole *mb ) { + + #if multipole_order == 1 + + /* Correct the position. */ + float mma = ma->coeffs[0], mmb = mb->coeffs[0]; + float w = 1.0f / ( mma + mmb ); + for ( int k = 0 ; k < 3 ; k++ ) + ma->x[k] = ( ma->x[k]*mma + mb->x[k]*mmb ) * w; + + /* Add the particle to the moments. */ + ma->coeffs[0] = mma + mmb; + + #else + #error( "Multipoles of order %i not yet implemented." , multipole_order ) + #endif + + } + + +/** + * @brief Add a particle to the given multipole. + * + * @param m The #multipole. + * @param p The #gpart. + */ + +void multipole_addpart ( struct multipole *m , struct gpart *p ) { + + #if multipole_order == 1 + + /* Correct the position. */ + float mm = m->coeffs[0], mp = p->mass; + float w = 1.0f / ( mm + mp ); + for ( int k = 0 ; k < 3 ; k++ ) + m->x[k] = ( m->x[k]*mm + p->x[k]*mp ) * w; + + /* Add the particle to the moments. */ + m->coeffs[0] = mm + mp; + + #else + #error( "Multipoles of order %i not yet implemented." , multipole_order ) + #endif + + } + + +/** + * @brief Add a group of particles to the given multipole. + * + * @param m The #multipole. + * @param p The #gpart array. + * @param N Number of parts to add. + */ + +void multipole_addparts ( struct multipole *m , struct gpart *p , int N ) { + + #if multipole_order == 1 + + /* Get the combined mass and positions. */ + double xp[3] = { 0.0 , 0.0 , 0.0 }; + float mp = 0.0f, w; + for ( int k = 0 ; k < N ; k++ ) { + w = p[k].mass; + mp += w; + xp[0] += p[k].x[0] * w; + xp[1] += p[k].x[1] * w; + xp[2] += p[k].x[2] * w; + } + + /* Correct the position. */ + float mm = m->coeffs[0]; + w = 1.0f / ( mm + mp ); + for ( int k = 0 ; k < 3 ; k++ ) + m->x[k] = ( m->x[k]*mm + xp[k] ) * w; + + /* Add the particle to the moments. */ + m->coeffs[0] = mm + mp; + + #else + #error( "Multipoles of order %i not yet implemented." , multipole_order ) + #endif + + } + + +/** + * @brief Init a multipole from a set of particles. + * + * @param m The #multipole. + * @param parts The #gparts. + * @param N The number of particles. + */ + +void multipole_init ( struct multipole *m , struct gpart *parts , int N ) { + + #if multipole_order == 1 + + float mass = 0.0f, w; + double x[3] = { 0.0 , 0.0 , 0.0 }; + int k; + + /* Collect the particle data. */ + for ( k = 0 ; k < N ; k++ ) { + w = parts[k].mass; + mass += w; + x[0] += parts[k].x[0] * w; + x[1] += parts[k].x[1] * w; + x[2] += parts[k].x[2] * w; + } + + /* Store the data on the multipole. */ + m->coeffs[0] = mass; + m->x[0] = x[0] / mass; + m->x[1] = x[1] / mass; + m->x[2] = x[2] / mass; + + #else + #error( "Multipoles of order %i not yet implemented." , multipole_order ) + #endif + + } + + +/** + * @brief Reset the data of a #multipole. + * + * @param m The #multipole. + */ + +void multipole_reset ( struct multipole *m ) { + + /* Just bzero the struct. */ + bzero( m , sizeof(struct multipole) ); + + } diff --git a/src/multipole.h b/src/multipole.h new file mode 100644 index 0000000000000000000000000000000000000000..e3372e245637dbaec6e078ac7294902f6a22a9fe --- /dev/null +++ b/src/multipole.h @@ -0,0 +1,133 @@ +/******************************************************************************* + * This file is part of SWIFT. + * Coypright (c) 2013 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Some constants. */ +#define multipole_order 1 + + +/* Multipole struct. */ +struct multipole { + + /* Multipole location. */ + double x[3]; + + /* Acceleration on this multipole. */ + float a[3]; + + /* Multipole coefficients. */ + float coeffs[ multipole_order*multipole_order ]; + + }; + + +/* Multipole function prototypes. */ +static void multipole_iact_mm ( struct multipole *ma , struct multipole *mb , double *shift ); +void multipole_merge ( struct multipole *ma , struct multipole *mb ); +void multipole_addpart ( struct multipole *m , struct gpart *p ); +void multipole_addparts ( struct multipole *m , struct gpart *p , int N ); +void multipole_init ( struct multipole *m , struct gpart *parts , int N ); +void multipole_reset ( struct multipole *m ); + + +#include <math.h> +#include "kernel.h" + +/** + * @brief Compute the pairwise interaction between two multipoles. + * + * @param ma The first #multipole. + * @param mb The second #multipole. + * @param shift The periodicity correction. + */ + +__attribute__ ((always_inline)) INLINE static void multipole_iact_mm ( struct multipole *ma , struct multipole *mb , double *shift ) { + + float dx[3], ir, r, r2 = 0.0f, acc; + int k; + + /* Compute the multipole distance. */ + for ( k = 0 ; k < 3 ; k++ ) { + dx[k] = ma->x[k] - mb->x[k] - shift[k]; + r2 += dx[k]*dx[k]; + } + + /* Compute the normalized distance vector. */ + ir = 1.0f / sqrtf( r2 ); + r = r2 * ir; + + /* Evaluate the gravity kernel. */ + kernel_grav_eval( r , &acc ); + + /* Scale the acceleration. */ + acc *= const_G * ir * ir * ir; + + /* Compute the forces on both multipoles. */ + #if multipole_order == 1 + float mma = ma->coeffs[0], mmb = mb->coeffs[0]; + for ( k = 0 ; k < 3 ; k++ ) { + ma->a[k] -= dx[k] * acc * mmb; + mb->a[k] += dx[k] * acc * mma; + } + #else + #error( "Multipoles of order %i not yet implemented." , multipole_order ) + #endif + + } + + +/** + * @brief Compute the interaction of a multipole on a particle. + * + * @param m The #multipole. + * @param p The #gpart. + * @param shift The periodicity correction. + */ + +__attribute__ ((always_inline)) INLINE static void multipole_iact_mp ( struct multipole *m , struct gpart *p , double *shift ) { + + float dx[3], ir, r, r2 = 0.0f, acc; + int k; + + /* Compute the multipole distance. */ + for ( k = 0 ; k < 3 ; k++ ) { + dx[k] = m->x[k] - p->x[k] - shift[k]; + r2 += dx[k]*dx[k]; + } + + /* Compute the normalized distance vector. */ + ir = 1.0f / sqrtf( r2 ); + r = r2 * ir; + + /* Evaluate the gravity kernel. */ + kernel_grav_eval( r , &acc ); + + /* Scale the acceleration. */ + acc *= const_G * ir * ir * ir * m->coeffs[0]; + + /* Compute the forces on both multipoles. */ + #if multipole_order == 1 + for ( k = 0 ; k < 3 ; k++ ) + p->a[k] += dx[k] * acc; + #else + #error( "Multipoles of order %i not yet implemented." , multipole_order ) + #endif + + } + +