grrmpy.functions のソースコード

import math
from math import sqrt
import numpy as np
import plotly.graph_objects as go
import plotly.io as pyi
import networkx as nx
from ase.units import kJ,Hartree,mol,kcal
from ase import Atoms
from ase.neb import NEB
from ase.geometry import find_mic
from ase.geometry.analysis import Analysis
from ase.build.rotate import rotation_matrix_from_points
from ase.neighborlist import build_neighbor_list,natural_cutoffs
# User
from grrmpy.calculator import pfp_calculator
# 本来デフォルトのレンダラーを使用したいが,なぜかうまく作動しないため追加
import plotly.io as pio
pio.renderers.default = "sphinx_gallery"

[ドキュメント]def get_fmax(atoms): """fmaxを返す Parameters: atoms: Atoms or NEB """ return sqrt((atoms.get_forces()**2).sum(axis=1).max())
[ドキュメント]def copy_images(images): """imagesをコピーする""" return [image.copy() for image in images]
[ドキュメント]def set_calc2images(images,calc_func=pfp_calculator): """imagesにcalculatorを設定する.""" for image in images: image.calc = calc_func()
[ドキュメント]def set_const2images(images,constraints): """imagesにconstraintsをかける.""" for image in images: image.set_constraint(constraints)
[ドキュメント]def get_energy_list(images,calc_func): try: energy_list = [image.get_potential_energy() for image in images] except: set_calc2images(images,calc_func) energy_list = [image.get_potential_energy() for image in images] return energy_list
[ドキュメント]def draw_graph(images, highlight=None, annotation=[], #[((x,y),"text",{kwargs})] unit="kJ/mol", title="Energy Diagram", xaxis_title="", yaxis_title=None, calc_func=pfp_calculator,): """エネルギーダイアグラムのグラフを作成する Parameters: images: list of Atoms | Atomsオブジェクトのリスト highlight: list of int 指定したindex番号のプロットのみ赤色になる annotation:list | アノテーションを入れたい場合に設定 | [((x座標,y座標),"テキスト",{その他}),]のタプルのリストで与える | y座標もなくても良い,[x座標,テキスト]で与える | {その他}はなくても良い.(2要素で与える) | その他には例えば | "showarrow":True 矢印で示す | "arrowsize":2 矢印の大きさ | "arrowhead":3 矢印の種類 | "ax:0" 矢印の向き | "ay":-50 矢印の向きなど unit: string | 'eV', 'kJ/mol', 'Hartree', 'kcal/mol'のいずれか xaxis_title: str | x軸のタイトル yaxis_title: string | Noneの場合,Energy({unit}) """ def find_y_value(x,y_list): if float(x).is_integer(): return y_list[int(x)] else: x_ini,x_fin = int(x),int(x)+1 y_ini,y_fin = y_list[x_ini],y_list[x_fin] y = (x-x_ini)*(y_fin-y_ini)/(x_fin-x_ini)+y_ini return y x = [i for i in range(len(images))] try: y = [atoms.get_potential_energy() for atoms in images] except: for image in images: image.calc = calc_func() y = [atoms.get_potential_energy() for atoms in images] y = [i-y[0] for i in y] # iniのエネルギーを0スタートで表記 if unit == "kJ/mol": y = [i*mol/kJ for i in y] elif unit == "Hartree": y = [i/Hartree for i in y] elif unit == "kcal/mol": y = [i*mol/kcal for i in y] fig = go.Figure() fig.add_trace( go.Scatter(x=x, y=y, line=dict(width=2,color="black")) ) if highlight: fig.add_trace( go.Scatter(x=highlight, y=[y[i] for i in highlight], mode="markers", marker=dict(size=12,color="red")) ) for position,*args in annotation: if type(position)==tuple or type(position)==tuple: x_pos,y_pos = position else: x_pos = position y_pos = find_y_value(x_pos,y) if len(args) == 1: text = args[0] kwargs = {} else: text,kwargs = args fig.add_annotation(x=x_pos, y=y_pos, text=text, **kwargs) if yaxis_title is None: yaxis_title = f"Energy({unit})" fig.update_layout( title=title, xaxis_title=xaxis_title, yaxis_title=unit, showlegend=False, ) return fig
def to_html_energy_diagram(images,calc_func=pfp_calculator, full_html=False, unit="kJ/mol", title="Energy Diagram", xaxis_title="", yaxis_title=None, highlight=None, annotation=[], **kwargs): """エネルギーダイアグラムのhtmlテキストを作成する Parameters: images: list of Atoms Atomsオブジェクトのリスト highlight: list of int 指定したindex番号のプロットのみ赤色になる annotation:list | アノテーションを入れたい場合に設定 | [((x座標,y座標),"テキスト",{その他}),]のタプルのリストで与える | y座標もなくても良い,[x座標,テキスト]で与える | {その他}はなくても良い.(2要素で与える) | その他には例えば | "showarrow":True 矢印で示す | "arrowsize":2 矢印の大きさ | "arrowhead":3 矢印の種類 | "ax:0" 矢印の向き | "ay":-50 矢印の向きなど full_html: bool | <html>タグたか始まる完全なhtmlテキストを出力する場合,True | Falseの場合<div>タグのみ unit: string 'eV', 'kJ/mol', 'Hartree', 'kcal/mol'のいずれか xaxis_title: str x軸のタイトル yaxis_title: string Noneの場合,Energy({unit}) """ fig = draw_graph( images, highlight = highlight, annotation = annotation, calc_func=calc_func, unit=unit, title=title, xaxis_title=xaxis_title, yaxis_title=yaxis_title) return pyi.to_html(fig,full_html=full_html,**kwargs) def circle_fitting(x, y): """Circle Fitting with least squared input: point x-y positions output cxe x center position cye y center position re radius of circle """ sumx = sum(x) sumy = sum(y) sumx2 = sum([ix ** 2 for ix in x]) sumy2 = sum([iy ** 2 for iy in y]) sumxy = sum([ix * iy for (ix, iy) in zip(x, y)]) F = np.array([[sumx2, sumxy, sumx], [sumxy, sumy2, sumy], [sumx, sumy, len(x)]]) G = np.array([[-sum([ix ** 3 + ix * iy ** 2 for (ix, iy) in zip(x, y)])], [-sum([ix ** 2 * iy + iy ** 3 for (ix, iy) in zip(x, y)])], [-sum([ix ** 2 + iy ** 2 for (ix, iy) in zip(x, y)])]]) try: T = np.linalg.inv(F).dot(G) except np.linalg.LinAlgError: return 0, 0, float("inf") cxe = float(T[0] / -2) cye = float(T[1] / -2) try: re = math.sqrt(cxe ** 2 + cye ** 2 - T[2]) except np.linalg.LinAlgError: return cxe, cye, float("inf") return cxe, cye, re def calc_curvature_circle_fitting(x, y, npo=1): """各点での曲率を求める Calc curvature x,y: x-y position list npo: the number of points using Calculation curvature ex) npo=1: using 3 point npo=2: using 5 point npo=3: using 7 point """ cv = [] n_data = len(x) for i in range(n_data): lind = i - npo hind = i + npo + 1 if lind < 0: lind = 0 if hind >= n_data: hind = n_data xs = x[lind:hind] ys = y[lind:hind] (cxe, cye, re) = circle_fitting(xs, ys) if len(xs) >= 3: # sign evaluation c_index = int((len(xs) - 1) / 2.0) sign = (xs[0] - xs[c_index]) * (ys[-1] - ys[c_index]) - ( ys[0] - ys[c_index]) * (xs[-1] - xs[c_index]) # check straight line a = np.array([xs[0] - xs[c_index], ys[0] - ys[c_index]]) b = np.array([xs[-1] - xs[c_index], ys[-1] - ys[c_index]]) theta = math.degrees(math.acos( np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))) if theta == 180.0: cv.append(0.0) # straight line elif sign > 0: cv.append(1.0 / -re) else: cv.append(1.0 / re) else: cv.append(0.0) return cv
[ドキュメント]def n_sampling(lst, n, end=False): """listを与えたとき,いい感じに均等になるようにn個サンプリングする Parameters: lst: list リスト n: int 抽出する数 end: bool 始めと終わりの構造を必ず含める場合True Return list: サンプリング後のリスト """ division = len(lst) / n a = [lst[round(division * i):round(division * (i + 1))][0] for i in range(n)] if end: original_idx = [i for i in range(len(lst))] idx = [original_idx[round(division * i):round(division * (i + 1))][0] for i in range(n)] if original_idx[-1] != idx[-1]: a[-1] = lst[-1] return a
[ドキュメント]def get_diff(atoms1,atoms2,mic=False): """atoms1とatoms2の距離の差分を算出する Parameters: atoms1: Atoms Atomsオブジェクト atoms2: Atoms Atomsオブジェクト mic: bool 周期境界条件で最小移動規則を適用する場合.True Returns: float: 距離 """ pos1 = atoms1.get_positions() pos2 = atoms2.get_positions() d = pos2 - pos1 if mic: d = find_mic(d, atoms2.get_cell(), atoms2.pbc)[0] d = np.linalg.norm(d) return d
[ドキュメント]def minimize_rotation_and_translation_for_specified_indices_only(target, atoms, indices=None): """atomsの原子の位置をtargetの位置と近くなるように(最小二乗法)配置する. ase.build.rotate.minimize_rotation_and_translationでindexを指定できるようにした関数 indices=Noneの時はASEのminimize_rotation_and_translationと同じで全ての原子を動かす. """ if indices is None: indices = [i for i in range(len(target))] p = atoms.get_positions()[indices] p0 = target.get_positions()[indices] # centeroids to origin c = np.mean(p, axis=0) p -= c c0 = np.mean(p0, axis=0) p0 -= c0 # Compute rotation matrix R = rotation_matrix_from_points(p.T, p0.T) atoms.positions[indices] = np.dot(p, R.T) + c0
[ドキュメント]def get_bond(atoms, unique=True, mult=1.0, **kwargs): """結合している原子のindex番号のリストを返す Parameters: atoms: Atoms 対象のAtoms unique: bool Trueの場合,A-B,B-Aのいずれかのみを返す mult: float 大きい程,離れていても結合していると判定される. kwargs: | 元素毎で解離の共有結合半径を指定できる | ex) H=0.5 で水素の共有結合半径を0.5Åに変更できる. """ cutoff = natural_cutoffs(atoms, mult=mult,**kwargs) nl = build_neighbor_list(atoms,cutoff) ana = Analysis(atoms, nl=nl) if unique: bonds = ana.unique_bonds[0] else: bonds = ana.all_bonds[0] return bonds
[ドキュメント]def connected_components(atoms,indices=None,mult=1.0,**kwargs): """atoms中に存在する分子をindex番号毎にまとめたジェネレーターを返す Exampleを参照 Parameters: atoms: Atoms 対象のAtoms indices: list of int 特定の原子のみcomponentを調べる時に指定する. Noneの場合,全ての原子を対象とする. mult: float 大きい程,離れていても結合していると判定される. kwargs: | 元素毎で解離の共有結合半径を指定できる | ex) H=0.5 で水素の共有結合半径を0.5Åに変更できる. Examples: 例えばatomsの[5,6,7,8,9]がメタン分子,[10,11]がNO分子だった場合, 分子ごとに分離することができる >>> gen = connected_components(atoms, indices=[5,6,7,8,9,10,11]) >>> for i in gen: >>> print(i) >>> #--> {5,6,7,8,9} >>> #--> {10,11} """ if indices is None: indices = [i for i in range(len(atoms))] G = nx.Graph() bonds = get_bond(atoms,mult=mult,**kwargs) for i in indices: for j in bonds[i]: if j in indices: G.add_edge(i, j) return nx.connected_components(G)
[ドキュメント]def get_unique_connections(connections): """ユニークなCONNECTIONSを返す 同じEQを繋ぐconneciotns,同じ組み合わせのconnections,'??'などを含むconnectionsを削除し 新しいconnectionsのリストを作成する. 戻り値は2要素のタプルで1要素目がユニークなconnections, 2要素目がそれが元々どのindex番号のconnectionだったのかを返す. Parameters: connections: list of lists of int | [[0,1],[1,0],[2,5],["??",2],[3,4]]のようなリスト | grrmpy.io.read_connecitonsで取得できる Return: tuple : ユニークなconnections, 元々のindex番号 """ unique = [] original_idx = [] for i,(ini,fin) in enumerate(connections): if isinstance(ini,str) or isinstance(fin,str): continue if ini == fin: continue if [ini,fin] in unique or [fin,ini] in unique: continue unique.append([ini,fin]) original_idx.append(i) return unique,original_idx
[ドキュメント]def partially_overlapping_groupings(iterable,n,i,strict=False): iterable = iter(iterable) l = [next(iterable) for _ in range(n)] yield tuple(l) while True: try: li = [] for _ in range(i): li.append(next(iterable)) l = l[i:] + li yield tuple(l) except StopIteration: if not strict and li!=[] : yield tuple(li) break