#!/usr/bin/env python # ./cube2mayavi.py -i 050Li3PS4-050LiI_PS4_I.cube -atom I -iso 1.26483e-09 import numpy as np from mayavi import mlab ang_borr = 0.5291772109217 def read_cube(filename): """ .cubeファイルを読み込み、原子座標と3次元ボクセルデータを返す関数 """ with open(filename, 'r') as f: # ヘッダーの読み飛ばし(最初の2行はコメント) f.readline() f.readline() # 3行目: 原子の数 と 原点座標 line = f.readline().split() natoms = abs(int(line[0])) # 原子の数 origin = np.array([float(x) for x in line[1:4]]) # 原点 # 4-6行目: グリッドの分割数(N)とベクトル(X, Y, Z軸) line = f.readline().split() nx, x_vec = int(line[0]), np.array([float(x) for x in line[1:4]]) line = f.readline().split() ny, y_vec = int(line[0]), np.array([float(x) for x in line[1:4]]) line = f.readline().split() nz, z_vec = int(line[0]), np.array([float(x) for x in line[1:4]]) # 原子座標の読み込み atoms = [] for _ in range(natoms): line = f.readline().split() atoms.append([float(x) for x in line[2:5]]) atoms = np.array(atoms) # ボクセルデータの読み込み data = [] for line in f: data.extend([float(x) for x in line.split()]) vol_data = np.array(data).reshape(nx, ny, nz) return vol_data, atoms, origin, (x_vec, y_vec, z_vec) def draw_radial_scale(max_dist=10.0, step=1.0, axis='x'): """ 原点から動径方向に目盛り(定規)を描画する """ if axis == 'y': vec = np.array([0, 1, 0]) tick_dir = np.array([0.2, 0, 0]) elif axis == 'z': vec = np.array([0, 0, 1]) tick_dir = np.array([0.2, 0, 0]) else: # default x vec = np.array([1, 0, 0]) tick_dir = np.array([0, 0, 0.2]) # メインの直線を引く end_point = vec * max_dist mlab.plot3d([0, end_point[0]], [0, end_point[1]], [0, end_point[2]], tube_radius=0.02, color=(0, 0, 0)) # 目盛りと数字を配置 for r in np.arange(step, max_dist + step, step): pos = vec * r t_start = pos - tick_dir t_end = pos + tick_dir mlab.plot3d([t_start[0], t_end[0]], [t_start[1], t_end[1]], [t_start[2], t_end[2]], tube_radius=0.02, color=(0, 0, 0)) text_pos = pos + tick_dir * 1.5 mlab.text3d(text_pos[0], text_pos[1], text_pos[2], f"{int(r)}", scale=0.25, color=(0, 0, 0)) def change_view(azimuth=30, elevation=60): mlab.view(azimuth=azimuth, elevation=elevation) def Li_visualize(filename, iso_val=None, radius_cutoff=5.0): print(f"Reading {filename}...") vol_data, atoms, origin, vectors = read_cube(filename) atoms, origin_ang = atoms * ang_borr, origin * ang_borr dx = np.linalg.norm(vectors[0]) * ang_borr dy = np.linalg.norm(vectors[1]) * ang_borr dz = np.linalg.norm(vectors[2]) * ang_borr nx, ny, nz = vol_data.shape i, j, k = np.mgrid[0:nx, 0:ny, 0:nz] X = origin_ang[0] + i * dx Y = origin_ang[1] + j * dy Z = origin_ang[2] + k * dz R = np.sqrt(X**2 + Y**2 + Z**2) vol_inner = np.where(R <= 5, vol_data, 0.0) vol_outer = np.where((R < 9) & (R > 5), vol_data, 0.0) mlab.figure(bgcolor=(1, 1, 1), size=(800, 600)) if args.iso_val is None: iso_val = np.std(vol_data) * 2.0 else: iso_val = args.iso_val print(f"Plotting isosurface at value: +/- {iso_val:.4f}") print("max_value: ", np.max(vol_data)) print("min_value: ", np.min(vol_data)) # --- 確率密度 内側 --- src_in = mlab.pipeline.scalar_field(vol_inner) src_in.spacing = [dx, dy, dz] src_in.origin = origin_ang obj_in = mlab.pipeline.iso_surface(src_in, contours=[iso_val], opacity=0.4, color=(0, 0.2, 0.8)) # --- 確率密度 外側 --- src_out = mlab.pipeline.scalar_field(vol_outer) src_out.spacing = [dx, dy, dz] src_out.origin = origin_ang obj_out = mlab.pipeline.iso_surface(src_out, contours=[iso_val], opacity=0.2, color=(0.7, 0.1, 0.1)) # --- 原子の描画 --- mlab.points3d(atoms[0, 0], atoms[0, 1], atoms[0, 2], scale_factor=0.8, color=(0.576, 0.439, 0.8), resolution=20) mlab.points3d(atoms[1:, 0], atoms[1:, 1], atoms[1:, 2], scale_factor=0.8, color=(1, 1, 0), resolution=20) # --- 結合の描画 --- p_coord = atoms[0] s_coords = atoms[1:] for s_coord in s_coords: mlab.plot3d([p_coord[0], s_coord[0]], [p_coord[1], s_coord[1]], [p_coord[2], s_coord[2]], tube_radius=0.1, color=(0.6, 0.6, 0.6), opacity=1.0) mlab.points3d(0, 0, 0, scale_factor=6.6, mode='sphere', color=(0, 0, 0), opacity=0.05, resolution=50) mlab.points3d(0, 0, 0, scale_factor=14, mode='sphere', color=(0, 0, 0), opacity=0.05, resolution=50) mlab.axes() return obj_in, obj_out def I_visualize(filename, iso_val=None, cutoff=8): """ 1つのIのcubeファイルを読み込んで可視化する """ print(f"Reading {filename}...") vol_data, atoms, origin, vectors = read_cube(filename) atoms, origin_ang = atoms * ang_borr, origin * ang_borr dx = np.linalg.norm(vectors[0]) * ang_borr dy = np.linalg.norm(vectors[1]) * ang_borr dz = np.linalg.norm(vectors[2]) * ang_borr mlab.figure(bgcolor=(1, 1, 1), size=(800, 600)) if iso_val is None: if 'args' in globals() and hasattr(args, 'iso_val') and args.iso_val is not None: iso_val = args.iso_val else: iso_val = np.std(vol_data) * 2.0 print(f"Plotting isosurface at value: +/- {iso_val:.4f}") # --- カットオフによるデータの球状マスク処理 --- if cutoff is not None: nx, ny, nz = vol_data.shape x = origin_ang[0] + np.arange(nx) * dx y = origin_ang[1] + np.arange(ny) * dy z = origin_ang[2] + np.arange(nz) * dz X, Y, Z = np.meshgrid(x, y, z, indexing='ij') R = np.sqrt(X**2 + Y**2 + Z**2) vol_data[R > cutoff] = 0.0 print("max_value: ", np.max(vol_data)) print("min_value: ", np.min(vol_data)) # --- 確率密度 --- src_in = mlab.pipeline.scalar_field(vol_data) src_in.spacing = [dx, dy, dz] src_in.origin = origin_ang # 赤色で表示 obj = mlab.pipeline.iso_surface(src_in, contours=[iso_val], opacity=0.3, color=(0.850, 0.058, 0.058)) # --- 原子の描画 --- mlab.points3d(atoms[0, 0], atoms[0, 1], atoms[0, 2], scale_factor=0.8, color=(0.576, 0.439, 0.8), resolution=20) mlab.points3d(atoms[1:, 0], atoms[1:, 1], atoms[1:, 2], scale_factor=0.8, color=(1, 1, 0), resolution=20) # --- 結合の描画 --- p_coord = atoms[0] s_coords = atoms[1:] for s_coord in s_coords: mlab.plot3d([p_coord[0], s_coord[0]], [p_coord[1], s_coord[1]], [p_coord[2], s_coord[2]], tube_radius=0.1, color=(0.6, 0.6, 0.6), opacity=1.0) mlab.points3d(0, 0, 0, scale_factor=11, mode='sphere', color=(0, 0, 0), opacity=0.05, resolution=50) return obj # --- 実行部分 --- if __name__ == "__main__": import argparse description = """This is a test program""" par = argparse.ArgumentParser(description=description) par.add_argument('-i', '--infiles', default="", required=True, nargs="+", help='input file') par.add_argument('-atom', '--atom', default="", required=True, choices=['Li', 'I'], help='atom') par.add_argument('-iso', '--iso_val', required=False, type=float, help='しきい値') args = par.parse_args() if args.atom == "Li": obj_in, obj_out = Li_visualize(args.infiles[0]) mlab.show() elif args.atom == "I": if len(args.infiles) > 1: print("Warning: Multiple files were passed, but only the first one will be read.") obj = I_visualize(args.infiles[0]) mlab.show() else: print("atom error")