|
259 | 259 | }, |
260 | 260 | { |
261 | 261 | "cell_type": "code", |
262 | | - "execution_count": null, |
| 262 | + "execution_count": 18, |
263 | 263 | "id": "ca743a8b", |
264 | 264 | "metadata": {}, |
265 | | - "outputs": [], |
| 265 | + "outputs": [ |
| 266 | + { |
| 267 | + "name": "stdout", |
| 268 | + "output_type": "stream", |
| 269 | + "text": [ |
| 270 | + "Atomic charges: [-0.28375011 -0.28374982 -0.01416517 0.18020443 0.1505785 0.15057809\n", |
| 271 | + " 0.06419838 0.0641997 -0.00754719 -0.01027312 -0.0102737 ]\n", |
| 272 | + "Equilibrated chemical potential: 0.24684627271641876\n", |
| 273 | + "Sum of charges: -5.204170427930421e-17\n" |
| 274 | + ] |
| 275 | + } |
| 276 | + ], |
266 | 277 | "source": [ |
267 | | - "# Set up the equations to solve for the charges and print out the charges. " |
| 278 | + "# Set up the equations to solve for the charges and print out the charges.\n", |
| 279 | + "\n", |
| 280 | + "def eem_charges(atnums, atcoords, mu, eta, mol_charge, alpha=0.5):\n", |
| 281 | + " \"\"\"Compute EEM atomic charges.\n", |
| 282 | + "\n", |
| 283 | + " Builds and solves the (P+1) x (P+1) EEM linear system A·δ = m, where\n", |
| 284 | + " the diagonal contains the atomic hardnesses, the off-diagonal atom-atom\n", |
| 285 | + " blocks contain the erfgau-screened Coulomb interaction J_AB, the border\n", |
| 286 | + " row and column enforce the charge constraint, and the right-hand side\n", |
| 287 | + " contains the negative chemical potentials and the total molecular charge.\n", |
| 288 | + "\n", |
| 289 | + " Parameters\n", |
| 290 | + " ----------\n", |
| 291 | + " atnums : array-like of int, shape (P,)\n", |
| 292 | + " Atomic numbers (used only to determine P; properties come from mu/eta).\n", |
| 293 | + " atcoords : numpy.ndarray, shape (P, 3)\n", |
| 294 | + " Atomic coordinates in bohr.\n", |
| 295 | + " mu : numpy.ndarray, shape (P,)\n", |
| 296 | + " Atomic chemical potentials (negative electronegativities).\n", |
| 297 | + " eta : numpy.ndarray, shape (P,)\n", |
| 298 | + " Atomic chemical hardnesses.\n", |
| 299 | + " mol_charge : float\n", |
| 300 | + " Total molecular charge Q.\n", |
| 301 | + " alpha : float\n", |
| 302 | + " Range-separation parameter for the erfgau Coulomb screening.\n", |
| 303 | + "\n", |
| 304 | + " Returns\n", |
| 305 | + " -------\n", |
| 306 | + " charges : numpy.ndarray, shape (P,)\n", |
| 307 | + " EEM atomic charges q_A = -Delta N_A.\n", |
| 308 | + " mu_total : float\n", |
| 309 | + " Equilibrated chemical potential (Lagrange multiplier).\n", |
| 310 | + " \"\"\"\n", |
| 311 | + " atcoords = np.asarray(atcoords)\n", |
| 312 | + " mu = np.asarray(mu)\n", |
| 313 | + " eta = np.asarray(eta)\n", |
| 314 | + " P = len(mu)\n", |
| 315 | + "\n", |
| 316 | + " # Build the (P+1) x (P+1) EEM matrix A\n", |
| 317 | + " A = np.zeros((P + 1, P + 1))\n", |
| 318 | + "\n", |
| 319 | + " # Diagonal: atomic hardnesses\n", |
| 320 | + " A[:P, :P] = np.diag(eta)\n", |
| 321 | + "\n", |
| 322 | + " # Off-diagonal atom-atom blocks: erfgau-screened Coulomb interaction\n", |
| 323 | + " for i in range(P):\n", |
| 324 | + " for j in range(i + 1, P):\n", |
| 325 | + " R_ij = np.linalg.norm(atcoords[i] - atcoords[j])\n", |
| 326 | + " J_ij = erfgau(R_ij, alpha)\n", |
| 327 | + " A[i, j] = J_ij\n", |
| 328 | + " A[j, i] = J_ij\n", |
| 329 | + "\n", |
| 330 | + " # Border row and column: charge constraint\n", |
| 331 | + " A[:P, P] = 1.0\n", |
| 332 | + " A[P, :P] = 1.0\n", |
| 333 | + " # A[P, P] = 0 (already zero)\n", |
| 334 | + "\n", |
| 335 | + " # Build the right-hand side m\n", |
| 336 | + " m = np.empty(P + 1)\n", |
| 337 | + " m[:P] = -mu\n", |
| 338 | + " m[P] = -mol_charge\n", |
| 339 | + "\n", |
| 340 | + " # Solve A·delta = m where delta = [Delta_N_1, ..., Delta_N_P, mu_total]\n", |
| 341 | + " delta = np.linalg.solve(A, m)\n", |
| 342 | + "\n", |
| 343 | + " charges = -delta[:P] # q_A = -Delta N_A\n", |
| 344 | + " mu_total = delta[P]\n", |
| 345 | + "\n", |
| 346 | + " return charges, mu_total\n", |
| 347 | + "\n", |
| 348 | + "\n", |
| 349 | + "charges, mu_total = eem_charges(mol0.atnums, mol0.atcoords, mu, eta, mol_charge=0)\n", |
| 350 | + "print(\"Atomic charges:\", charges)\n", |
| 351 | + "print(\"Equilibrated chemical potential:\", mu_total)\n", |
| 352 | + "print(\"Sum of charges:\", charges.sum())\n" |
268 | 353 | ] |
| 354 | + }, |
| 355 | + { |
| 356 | + "cell_type": "code", |
| 357 | + "execution_count": null, |
| 358 | + "id": "3cd69598", |
| 359 | + "metadata": {}, |
| 360 | + "outputs": [], |
| 361 | + "source": [] |
269 | 362 | } |
270 | 363 | ], |
271 | 364 | "metadata": { |
|
0 commit comments