/* ewald_c.c * MPIなし・最小依存のEwald和(実空間+逆格子+自己項)を全サイト同時計算。 * 返すのは: * - mp[i] : サイト i のポテンシャル φ_i [1/m](ewaldf_BM の MP と同じ次元) * - F[i] : サイト i の力ベクトル [N] * * ビルド例: * Linux/macOS: * g++ -O3 -fPIC -shared -o libewald_c.so ewald_c.c -lm * Windows (MinGW): * g++ -O3 -shared -o ewald_c.dll ewald_c.c -static-libgcc -static-libstdc++ * * 使い方は同ディレクトリの ewald_fast_ctypes.py を参照。 */ #include #include #include #ifdef __cplusplus #include using std::exp; using std::sqrt; using std::erfc; #else #include #endif /* ---- 物理定数(ewaldf_BM と一致) ---- */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif static const double PI = M_PI; static const double E_CH = 1.602176634e-19; /* C */ static const double EPS0 = 8.854418782e-12; /* C^2 N^-1 m^-2 */ static const double KE = (E_CH*E_CH)/(4.0*M_PI*EPS0); /* J·m·C^-2 */ static const double ANG2M = 1e-10; /* 行列 × ベクトル(転置) out = A^T * v, A は 3x3(行メジャ) */ static inline void matvec_T(const double *A, const double v[3], double out[3]) { /* A[r*3+c] */ out[0] = A[0]*v[0] + A[3]*v[1] + A[6]*v[2]; out[1] = A[1]*v[0] + A[4]*v[1] + A[7]*v[2]; out[2] = A[2]*v[0] + A[5]*v[1] + A[8]*v[2]; } static inline double dot3(const double a[3], const double b[3]) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } static inline double norm3(const double v[3]) { return sqrt(dot3(v,v)); } /* 2π*(h*x + k*y + l*z) */ static inline double phase_2pi(int h, int k, int l, const double frac[3]) { return 2.0*M_PI*( (double)h*frac[0] + (double)k*frac[1] + (double)l*frac[2] ); } /* v^T * M * v (Mは3x3, 行メジャ) */ static inline double quad3(const double M[9], const double v[3]) { double t0 = M[0]*v[0] + M[1]*v[1] + M[2]*v[2]; double t1 = M[3]*v[0] + M[4]*v[1] + M[5]*v[2]; double t2 = M[6]*v[0] + M[7]*v[1] + M[8]*v[2]; return v[0]*t0 + v[1]*t1 + v[2]*t2; } /* 共有ライブラリ公開:Cリンケージ */ #ifdef __cplusplus extern "C" { #endif /* 戻り値: 0=成功, <0=エラー * 引数(Row-major 前提): * N : サイト数 * aij[9] : 実格子ベクトル行列(Å) * Raij[9] : 逆格子ベクトル行列(Å^-1) * gij[9] : 実空間metric(Å^2) * Rgij[9] : 逆空間metric(Å^-2) * volume_A3 : 体積(Å^3) * frac[3N] : 各サイトの分率座標 * q[N] : 各サイトの電荷(単位: e) * alpha_invA : Ewald α(Å^-1) * nrmax[3] : 実空間反復の最大(各軸の±範囲) * hgmax[3] : 逆格子反復の最大(各軸の±範囲) * rmin_A : 近接スキップ閾値(Å) * halfspace : 1=G空間で半空間 (l>=0、l!=0 は2倍)、0=全空間 * out_mp[N] : 出力 φ_i(1/m) * out_F_N[3N] : 出力 力(N) */ int ewald_all_sites( int N, const double *aij, const double *Raij, const double *gij, const double *Rgij, double volume_A3, const double *frac, const double *q, double alpha_invA, const int *nrmax, const int *hgmax, double rmin_A, int halfspace, double *out_mp, double *out_F_N ) { if (N<=0 || !aij || !Raij || !gij || !Rgij || !frac || !q || !nrmax || !hgmax || !out_mp || !out_F_N) { return -1; } /* 初期化 */ for (int i=0;i 0.0) { double expterm = exp(-(alpha_m*r_m)*(alpha_m*r_m)); double G = erfc(alpha_m*r_m)/(r_m*r_m*r_m) + (2.0*alpha_m/sqrt(M_PI))*expterm/(r_m*r_m); double coeff = qi * KE * q[j] * G; out_F_N[3*i+0] += coeff * rm[0]; out_F_N[3*i+1] += coeff * rm[1]; out_F_N[3*i+2] += coeff * rm[2]; } } } } } } /* ---------- 逆格子 ---------- */ /* l 半空間(l>=0): l!=0 のとき寄与を 2 倍。全空間にしたい場合は halfspace=0 */ const int l_start = halfspace ? 0 : -hgz; for (int l = l_start; l<=hgz; ++l) { for (int k=-hgy; k<=hgy; ++k) { for (int h=-hgx; h<=hgx; ++h) { if (h==0 && k==0 && l==0) continue; /* |G|^2 = [h k l]^T Rgij [h k l] */ double hkltmp[3] = { (double)h, (double)k, (double)l }; double G2_Ainv2 = quad3(Rgij, hkltmp); if (G2_Ainv2 <= 0.0) continue; /* amp = exp(-π^2 G2/α^2) / (G2 * 1e20) → [m^2] */ double amp = exp(-Kexp * G2_Ainv2) / (G2_Ainv2 * 1e20); /* 構造因子(電荷重み) */ double Csum = 0.0, Ssum = 0.0; for (int j=0; j