氢水建模

import numpy as np
from ase.build import molecule
from ase import Atoms
from ase.io import read
from ase.io.vasp import write_vasp

def rotation_matrix_from_euler(alpha, beta, gamma, degrees=True):
    """
    生成绕 x, y, z 轴依次旋转 alpha, beta, gamma 的旋转矩阵。
    Rotation applied as: first Rx(alpha), then Ry(beta), then Rz(gamma).
    返回一个 3x3 的 numpy 矩阵 R,使得 r' = R @ r (列向量)。
    """
    if degrees:
        a = np.deg2rad(alpha)
        b = np.deg2rad(beta)
        g = np.deg2rad(gamma)
    else:
        a, b, g = alpha, beta, gamma

    Rx = np.array([[1, 0, 0],
                   [0, np.cos(a), -np.sin(a)],
                   [0, np.sin(a),  np.cos(a)]])
    Ry = np.array([[ np.cos(b), 0, np.sin(b)],
                   [0, 1, 0],
                   [-np.sin(b), 0, np.cos(b)]])
    Rz = np.array([[np.cos(g), -np.sin(g), 0],
                   [np.sin(g),  np.cos(g), 0],
                   [0, 0, 1]])
    # 总旋转:先 Rx 然后 Ry 然后 Rz -> R = Rz @ Ry @ Rx
    R = Rz @ Ry @ Rx
    return R

def add_water(atoms, target_pos, orientation=(0, 0, 0),
              anchor='COM', use_fractional=True, cell=None):

    if cell is None:
        cell = atoms.get_cell()

    # 计算目标笛卡尔坐标
    if use_fractional:
        if cell is None or np.linalg.norm(cell) == 0:
            raise ValueError("没有可用的 cell 来把分数坐标转换为笛卡尔。传入 cell 或将 use_fractional=False。")
        target_cart = np.dot(np.asarray(target_pos), cell)  # frac @ cell
    else:
        target_cart = np.asarray(target_pos, dtype=float)

    # 生成水分子
    water = molecule("H2O")  # ASE 内置
    # 选择锚点并将其移动到原点
    if anchor.upper() == 'COM':
        ref = water.get_center_of_mass()
    elif anchor.upper() == 'O':
        syms = water.get_chemical_symbols()
        try:
            o_idx = syms.index('O')
            ref = water.positions[o_idx].copy()
        except ValueError:
            # 退回到 COM
            ref = water.get_center_of_mass()
    else:
        raise ValueError("anchor 必须是 'COM' 或 'O'。")

    # 把参考点移动到原点
    water.translate(-ref)

    # 应用旋转(关于原点)
    R = rotation_matrix_from_euler(*orientation, degrees=True)
    # positions 为 (N,3),对每个原子应用 R: pos' = (R @ pos.T).T
    water.positions = (R @ water.positions.T).T

    # 平移到目标笛卡尔位置
    water.translate(target_cart)

    # 合并并返回
    new_atoms = atoms + water
    return new_atoms

from ase.io import read
prim = read('../../CONTCAR')
new_atoms = add_water(prim, target_pos=(0.5, 0.5, 0.75), orientation=(10, 20, 90))
from ase.io import write
write_vasp("0Vo/ori2/POSCAR", new_atoms, direct=True,sort=True)
import numpy as np
from ase.build import molecule
from ase import Atoms
from ase.io import write, read
from ase.io.vasp import write_vasp

def rotation_matrix_from_euler(alpha, beta, gamma, degrees=True):
    """生成绕 x, y, z 轴依次旋转的旋转矩阵。"""
    if degrees:
        a = np.deg2rad(alpha)
        b = np.deg2rad(beta)
        g = np.deg2rad(gamma)
    else:
        a, b, g = alpha, beta, gamma

    Rx = np.array([[1, 0, 0],
                   [0, np.cos(a), -np.sin(a)],
                   [0, np.sin(a),  np.cos(a)]])
    Ry = np.array([[ np.cos(b), 0, np.sin(b)],
                   [0, 1, 0],
                   [-np.sin(b), 0, np.cos(b)]])
    Rz = np.array([[np.cos(g), -np.sin(g), 0],
                   [np.sin(g),  np.cos(g), 0],
                   [0, 0, 1]])
    return Rz @ Ry @ Rx


def add_h2(atoms, target_pos, orientation=(0, 0, 0),
           anchor='COM', use_fractional=True, cell=None):
    """
    在 atoms 中添加一分子 H2。

    参数:
      atoms         : ASE Atoms 对象
      target_pos    : 目标位置。如果 use_fractional=True,传入分数坐标 (fx,fy,fz),否则笛卡尔坐标 (Å)。
      orientation   : (alpha, beta, gamma),绕 x,y,z 轴的欧拉角(度)。
      anchor        : 'COM' 或 'H1'。决定氢分子哪个点对齐到 target_pos。
                      'COM' -> 质心对齐; 'H1' -> 把第一个氢原子对齐到目标。
      use_fractional: 是否把 target_pos 当作分数坐标,默认 True。
      cell          : 可选晶胞。如果 None,则用 atoms.get_cell()。

    返回:
      new_atoms : 含有氢分子的 ASE Atoms 对象
    """
    if cell is None:
        cell = atoms.get_cell()

    # 计算目标笛卡尔坐标
    if use_fractional:
        target_cart = np.dot(np.asarray(target_pos), cell)  # frac -> cart
    else:
        target_cart = np.asarray(target_pos, dtype=float)

    # 生成氢分子
    h2 = molecule("H2")

    # 锚点
    if anchor.upper() == 'COM':
        ref = h2.get_center_of_mass()
    elif anchor.upper() == 'H1':
        ref = h2.positions[0].copy()
    else:
        raise ValueError("anchor 必须是 'COM' 或 'H1'。")

    # 移动锚点到原点
    h2.translate(-ref)

    # 旋转
    R = rotation_matrix_from_euler(*orientation, degrees=True)
    h2.positions = (R @ h2.positions.T).T

    # 平移到目标
    h2.translate(target_cart)

    # 合并
    new_atoms = atoms + h2
    return new_atoms


from ase.io import read
prim = read('../../CONTCAR')
new_atoms = add_h2(prim, target_pos=(0.5, 0.5, 0.25), orientation=(45, 45, 0))
from ase.io import write
write_vasp("ori2/POSCAR", new_atoms, direct=True,sort=True)

Comment