grrmpy.automate.by_neb のソースコード

from ase.neb import interpolate
from ase.optimize import FIRE, LBFGS
from ase.io import write,iread, Trajectory
from ase.vibrations import Vibrations
from ase.units import kJ,mol
from ase.geometry import find_mic
from pathlib import Path
import csv
from pprint import pprint
import numpy as np
from ase.build.rotate import minimize_rotation_and_translation

#USER
from grrmpy.calculator import pfp_calculator
from grrmpy.neb.auto_neb import SNEB
from grrmpy.io.html import write_html
from grrmpy.vibrations.functions import to_html_table_and_imode,find_ts_idx,to_html_graph
from grrmpy.functions import (to_html_energy_diagram,
                              minimize_rotation_and_translation_for_specified_indices_only,
                              connected_components)
from grrmpy.path import ReactPath
try:
    from grrmpy.optimize import FIRELBFGS
    defaultoptimizer = FIRELBFGS
except:
    defaultoptimizer = FIRE


[ドキュメント]class SinglePath(): """ Parameters: \*data: | 3種類の方法でinput構造を与えることができる. ini,fin構造と初めの中間イメージの数を与える(3つの引数を与える) >>> sp = SinglePath(ini,fin,21,indices=[1,2,3,4]) # 21個内部にイメージを作成 ini,fin構造を与える(2つの引数を与える.初期イメージの数は13になる) >>> sp = SinglePath(ini,fin,indices=[1,2,3,4]) images構造を与える(1つの引数を与える.calculatorを付ける必要はない) >>> sp = SinglePath(images,indices=[1,2,3,4]) Constraintsがある場合 >>> f = FixAtoms(indices=[0,1,2,3]) >>> sp = SinglePath(ini,fin,indices=[1,2,3,4],constraints=f) >>> f = FixAtoms(indices=[0,1,2,3]) >>> c = FixBondLength(0, 1) >>> sp = SinglePath(ini,fin,indices=[1,2,3,4],constraints=[f,c]) imageの数は奇数個を推奨する. indices: list 振動数計算を行なう時に動かす原子のindex番号のリスト parallel: bool 並列計算を行なう場合True. mic: bool | 最小イメージ規則を適用する場合True. | Noneの場合,周期境界条件で計算する場合自動でTrueにする. | デフォルトはTrue. calc_func: function object calculatorを返す関数. デフォルトは ``pfp_calculator`` debug: bool debug.logを出力する(デバック用) Note: | EQ_list.traj, TS_list.trajが既にディレクトリ中にある場合,上書きされてしまうので | 1つフォルダ内で複数の計算を行なわないようにする!!! """ def __init__(self, *data, indices = None, parallel:bool=True, mic:bool=None, calc_func=pfp_calculator, debug=False, constraints=[]): """ EQ構造,TS構造,PT構造はEQ_list.traj,TS_list.traj,PT_list.trajに保存される. CONNECTIONSの情報はTS_CONNECTIONS.csv,PT_CONNECTIONS.csvに保存される Parameters: *data: 3種類の方法でinput構造を与えることができる. ・ini,fin構造と初めの中間イメージの数を与える(3つの引数を与える) ・ini,fin構造を与える(2つの引数を与える.初期イメージの数は13になる) ・images構造を与える(1つの引数を与える.calculatorを付ける必要はない) imageの数は奇数個を推奨する. indices: list 振動数計算を行なう時に動かす原子のindex番号のリスト parallel: bool 並列計算を行なう場合True. mic: bool | 最小イメージ規則を適用する場合True. | Noneの場合,周期境界条件で計算する場合自動でTrueにする. | デフォルトはTrue. calc_func: function object calculatorを返す関数. デフォルトは ``pfp_calculator`` constraints:constraint object or list | constraintsまたはconstraintsのリスト debug: bool debug.logを出力する(デバック用) Note: EQ_list.traj, TS_list.trajが既にディレクトリ中にある場合,上書きされてしまうので 1つフォルダ内で複数の計算を行なわないようにする!!! """ self.parallel = parallel self.calc_func = calc_func self.indices = indices # vibrations用 self.debug = debug self.constraints = constraints ### 初期イメージの作成 ### if len(data)==1: data_type="images" self.images = data[0] else: data_type="ini_fin" ini = data[0] fin = data[1] nimages = 13 # Deffaultのイメージ数 if len(data) == 3: nimages = data[2] self.images = [ini.copy()]+[ini.copy() for _ in range(nimages)]+[fin.copy()] if mic is None: self.mic = True if any(self.images[0].get_pbc()) else False else: self.mic = mic if data_type == "ini_fin": interpolate(self.images,mic=self.mic,apply_constraint=False) for image in self.images: self.set_calculator(image) image.set_constraint(self.constraints) def write_connections(self,file,data_list,mode="a"): with open(file,mode) as f: writer = csv.writer(f) writer.writerow(data_list) def write_eq(self,atoms): self.eq_traj.write(atoms) self.eq_count += 1 def write_ts(self,atoms): self.ts_traj.write(atoms) self.ts_count += 1 def write_pt(self,atoms): self.pt_traj.write(atoms) self.pt_count += 1 def debug_log(self,text): if self.debug: with open("debug.log","a") as f: f.write(str(text)+"\n") @property def default_param(self): """run()のデフォルトのパラメータを返す. General: stopping_criterion: int 停止基準. SNEBの計算をstopping_criterion回行なうと停止する. struct_check_threshold: list of float | 2つの構造が同一構造であるとみなす基準. | a,b,c,d,e,fの6つの要素を指定する.基準1,基準2のどちらかを満たしている時,同一構造とする. | 基準1: | 電子エネルギーの差, 原子間距離のRMS誤差,原子間距離の最大距離がそれぞれ | a kJ/mol, b%, cÅ 以下の時, 同一構造であるとみなす | 基準2: | エネルギーの差がd kJ/mol以下であり,indicesで指定した原子を重ね合わせた時, | (最小二乗法で重ねる)原子間距離のRMS誤差,原子間距離の最大距離がそれぞれ, | e%, fÅ 以下の時, 同一構造であるとみなす. minimize_rotation_and_translation: boolean | 基準1で2つの構造の座標が近くなるように再配置して解析する場合Ture. | (最小二乗値が小さくなるよう再配置を行なう) SNEB: optimizer: Optimizer class NEB計算に使用するOptimizerの種類 nimages: list of int 各NEB計算の中間イメージの数 maxstep: list of float | 各NEB計算でのmaxstep値のリスト | Noneの場合,forceに合わせて自動的にmaxstepsを変化させる. fmax: list of float 各NEB計算でのfmax値のリスト steps: list of int 各NEB計算でのsteps値のリスト tolerance: float NEBバンド中のtolerance(kJ/mol)以下の谷間はEQとは考えないで計算する. threshold: list of float 活性化エネルギーに対してthreshold%以下の点は削り新たなNEBイメージを作成して計算する dist: float | 新たなNEBイメージを作成する際,TSの両隣に,TSからdistÅ離れた位置にイメージを作成する. | TS付近にイメージを密集させるために行なう. min_nimages: int | 新たなイメージを作成するにあたって,それまでのNEBを削ることになるが, | 少なくともmin_nimages個は確保するようにする. with_stop: bool | optimizerにstop機能を付ける場合True. | max_n回連続でEnergyとForceが同じ状況がtimes回あった時計算を終了する. | grrmpy.optimize.functions.add_stop_to()を参照 max_n: int: with_stop=Trueの場合に指定. with_stop(add_stop_to)の引数. デフォルト12 times: int with_stop=Trueの場合に指定. with_stop(add_stop_to)の引数. デフォルト2 climb_steps: integer | climb=Falseで収束しなかった場合,その後のTrueでのNEB計算のStep数をclimb_steps回にする. | Noneの場合,climb=Falseの時と同じstepsで計算する. | 一番最後のNEB計算ではclimb_stepsに関係なくclimb=Falseの時と同じstepsで計算する. climb: bool | Falseの場合,climbはFalse,False,False,False...Trueで行なう. | Trueの場合はclimbはFalse,True,Fase,True,False....Trueで行なう | Falseにした場合,climb_stepsを設定する意味はなくなる. IRC: optimizer1: Optimizer calss 振動数計算後のエネルギーダイアグラム解析の結果,TSがはっきり分かる場合に使用するOptimizer optimizer2: Optimizer calss 振動数計算後のエネルギーダイアグラム解析の結果,TSがはっきり見えない場合に使用するOptimizer maxsteps:float or None | maxsteps値. maxstepsの値を大きく設定したとしても,始めの200回はmaxstep=0.03で計算する. | Noneを指定した場合,forceに合わせて自動的にmaxstepを調整する. dif: float | 振動数計算後のエネルギーダイアグラム解析の結果, | TS(極大値)と谷間(極小値)の差が,dif(kJ/mol)以下の時,optimizer2を使って計算する. calc_notop_ts: boolean | 振動数計算後のエネルギーダイアグラム解析の結果,TSがない(極大値がない)時, | IRC計算を行なわないならFalse. """ return { "General":{ "stopping_criterion":20, "struct_check_threshold":[10.0, 0.25, 2.5, 10.0, 0.25, 2.5], "minimize_rotation_and_translation" : True, }, "SNEB":{ "optimizer":FIRE, "nimages":[17, 11, 13], "maxstep":[0.2, 0.3, None], "fmax":[0.09, 0.06, 0.03], "steps":[500, 1000, 3000], "tolerance":5.0, "threshold":[0, 0], "dist":0.1, "min_nimages":3, "with_stop":True, "max_n":12, "times":4, "climb_steps":15, "climb":False, }, "IRC":{ "optimizer1":defaultoptimizer, "optimizer2":defaultoptimizer, "maxstep":None, "fmax":0.001, "steps":40000, "dif":2.0, "calc_notop_ts":True, }, } def set_calculator(self,atoms): if not atoms.get_calculator(): atoms.calc = self.calc_func() atoms.set_constraint(self.constraints) # 多分なくても良い def run_sneb(self,*images): self.sneb = SNEB(*images, logfile=f"SNEB{self.iter_count}.log", html=f"SNEB{self.iter_count}_progress.html", mic=self.mic, parallel=self.parallel, calc_func=self.calc_func, optimizer=self.neb_optimizer, with_stop = self.with_stop, max_n = self.max_n, times = self.times, constraints=self.constraints) self.sneb.attach(lambda:write_html(f"SNEB{self.iter_count}.html", self.sneb.images)) self.sneb.attach(lambda:write(f"SNEB{self.iter_count}.traj", self.sneb.images)) sneb_converged = self.sneb.run( nimages=self.nimages, maxstep=self.neb_maxstep, fmax=self.neb_fmax, steps=self.neb_steps, tolerance=self.tolerance, threshold=self.threshold, dist=self.neb_dist, min_nimages=self.min_nimages, climb_steps = self.climb_steps, climb = self.climb ) return sneb_converged def run_vib(self): self.vib = Vibrations(self.ts,self.indices,name=f"vib{self.iter_count}") self.vib.run() tb_text,imode = to_html_table_and_imode(self.vib,full_html=False,include_plotlyjs="cdn") if type(imode) == int: self.vib.write_mode(n=imode) self.vimages = [i for i in iread(f"vib{self.iter_count}.{imode}.traj")] fig_text = to_html_energy_diagram( self.vimages, calc_func=self.calc_func, full_html=False, unit="kJ/mol", title="Vibration Energy Diagram", xaxis_title="", yaxis_title=None, include_plotlyjs="cdn") success = True else: success = False fig_text = "" with open(f"vib{self.iter_count}.html","w") as f: f.write(tb_text+fig_text) return success def run_irc(self,ts_idx:int,r_use_newton:bool, f_use_newton:bool): self.ini = self.vimages[ts_idx-1].copy() self.ini.calc = self.calc_func() self.ini.set_constraints(self.constraints) self.fin = self.vimages[ts_idx+1].copy() self.fin.calc = self.calc_func() self.fin.set_constraints(self.constraints) r_converged = self.irun_irc(self.ini, r_use_newton, "reverse") f_converged = self.irun_irc(self.fin, f_use_newton, "forward") return [r_converged, f_converged] def irun_irc(self,atoms,use_newton,name): optimizer = self.optimizer2 if use_newton else self.optimizer1 def get_args(maxsteps): try: if optimizer==FIRELBFGS: arg = {"switch":0.04,"maxstep_fire":maxsteps,"maxstep_lbfgs":maxsteps} else: arg = {"maxstep":maxsteps} except: arg = {"maxstep":maxsteps} return arg ### 始めにmaxstep=0.03で200回計算しておく self.irc_opt = optimizer(atoms, logfile = f"IRC{self.iter_count}_{name}.log", **get_args(0.03)) self.irc_opt.attach(lambda:write(f"IRC{self.iter_count}_{name}.traj", atoms)) self.irc_opt.run(fmax=self.irc_fmax, steps=200) ### 計算 self.irc_opt = optimizer(atoms, logfile = f"IRC{self.iter_count}_{name}.log", **get_args(self.irc_maxstep)) self.irc_opt.attach(lambda:write(f"IRC{self.iter_count}_{name}.traj", atoms)) converged = self.irc_opt.run(fmax=self.irc_fmax, steps=self.irc_steps) return converged def _get_dif_energy(self,atoms1, atoms2): """エネルギー差の判定""" dif_e = abs(atoms1.get_potential_energy()-atoms2.get_potential_energy()) # eV単位 dif_e *= mol/kJ # kJ/mol単位 return dif_e def _get_check_rmse_and_maxdist(self,atoms1,atoms2): """RMS誤差と最大距離の判定""" pos1 = atoms1.get_positions() pos2 = atoms2.get_positions() d_mat = pos2 - pos1 if self.mic: d_mat = find_mic(d_mat, atoms2.get_cell(), atoms2.pbc)[0] d_line = np.linalg.norm(d_mat,axis=1) rms_error = np.sqrt(np.sum(d_line**2)/len(atoms1)) return rms_error, np.max(d_line) def check_structures(self,atoms1,atoms2): """2つの構造が同一構造であるか判断する. | エネルギーの差, 原子間距離のRMS誤差,原子間距離の最大距離がそれぞれ | a kJ/mol, b%, cÅ 以下の時, 同一構造であるとみなす. | もしくはエネルギーの差がd kJ/mol以下であり,indicesの原子を重ね合わせた時, | 原子間距離のRMS誤差,原子間距離の最大距離がそれぞれ, | e%, fÅ 以下の時, 同一構造であるとみなす. """ a,b,c,d,e,f = self.struct_check_threshold dif_energy = self._get_dif_energy(atoms1, atoms2) # エネルギー差を取得 atoms1_cp = atoms1.copy() atoms2_cp = atoms2.copy() mols_idx1 = [i for i in connected_components(atoms1_cp,self.indices)] # 分子を抽出 mols_idx2 = [i for i in connected_components(atoms2_cp,self.indices)] # 分子を抽出 if mols_idx1 == mols_idx2: # 同じ分子で構成されていた場合 for idxs in mols_idx1: indices = list(idxs) minimize_rotation_and_translation_for_specified_indices_only( atoms1_cp, atoms2_cp, indices ) rmse1, max_dist1 = self._get_check_rmse_and_maxdist(atoms1_cp,atoms2_cp) same_geo = True else: rmse1, max_dist1 = False,False same_geo = False atoms1_cp = atoms1.copy() atoms2_cp = atoms2.copy() if self.mrt: minimize_rotation_and_translation(atoms1_cp,atoms2_cp) rmse2, max_dist2 = self._get_check_rmse_and_maxdist(atoms1_cp,atoms2_cp) # 同じ分子で構成されていた場合,rmse1を返す rmse = rmse1 if rmse1 else rmse2 max_dist = max_dist1 if max_dist1 else max_dist2 self.debug_log(f"{dif_energy},{rmse1},{max_dist1},{rmse2},{max_dist2}") if all([dif_energy<a, rmse2<b, max_dist2<c]) or all([dif_energy<d, b<rmse<e, max_dist<f]): return True, same_geo, rmse else: return False, same_geo, rmse def analyze_connection(self,ts_n,ini_n,fin_n,ts,ini,fin): """CSVファイルに書き込む情報を作成する ["TS/PT","ini","fin","ini_energy","fin_energy","forward_Ea","reverce_Ea"] Parameters: ts_n (int): TS番号 ini_n (int): iniのEQ番号 fin_n (int): finのEQ番号 ts (Atoms): TSのAtoms ini (Atoms): iniのEQのAtoms fin (Atoms): finのEQのAtoms """ self.set_calculator(ini) self.set_calculator(fin) self.set_calculator(ts) ini_e = ini.get_potential_energy()*mol/kJ fin_e = fin.get_potential_energy()*mol/kJ ts_e = ts.get_potential_energy()*mol/kJ forward_ea = ts_e-ini_e reverse_ea = ts_e-fin_e text = [ts_n,ini_n,fin_n,ini_e,fin_e,forward_ea,reverse_ea] return text def create_path(self): """Pathオブジェクトを作成する""" name = self.order atoms = [self.atoms_dict[i] for i in self.order] self.path = ReactPath({"name":name,"atoms":atoms}) self.path.write_html("Path.html") self.path.topkl("Path.pickle")
[ドキュメント] def run(self, param=None): """計算を実行する Parameters: param: dict | 種々のパラメータは辞書で与える | デフォルトの値はget_param()で取得できる Example: IRCのoptimizer1をBFGS変えたい時 >>> sp = SinglePath(ini,fin) >>> param = sp.default_param >>> param["IRC"]["optimizer1"] = BFGS >>> sp.run(param) """ ### 番号の初期化 ### self.eq_count = 0 self.ts_count = 0 self.pt_count = 0 # connection # self.ts_connections = [] self.pt_connections = [] ### ファイル関係 ### self.eq_list_file = "EQ_list.traj" self.ts_list_file = "TS_list.traj" self.pt_list_file = "PT_list.traj" self.ts_info_file = "TS_CONNECTIONS.csv" self.pt_info_file = "PT_CONNECTIONS.csv" if Path(self.eq_list_file).exists(): raise Exception("既にEQ_list.trajファイルがあります") if Path(self.ts_list_file).exists(): raise Exception("既にTS_list.trajファイルがあります") self.eq_traj = Trajectory(self.eq_list_file,mode="a") self.ts_traj = Trajectory(self.ts_list_file,mode="a") self.pt_traj = Trajectory(self.pt_list_file,mode="a") self.write_eq(self.images[0]) self.write_eq(self.images[-1]) ts_coulmn = ["TS","ini","fin","ini_energy","fin_energy","forward_Ea","reverce_Ea"] self.write_connections(self.ts_info_file, ts_coulmn, "w") pt_coulmn = ["PT","ini","fin","ini_energy","fin_energy","forward_Ea","reverce_Ea"] self.write_connections(self.pt_info_file, pt_coulmn, "w") ###パラメータ関係 if param is None: param = self.default_param with open("PARAM.txt","w") as f: pprint(param, stream=f) self.stopping_criterion = param["General"]["stopping_criterion"] self.struct_check_threshold = param["General"]["struct_check_threshold"] self.mrt = param["General"]["minimize_rotation_and_translation"] self.neb_optimizer = param["SNEB"]["optimizer"] self.first_nimages = param["SNEB"]["nimages"][0] self.nimages = param["SNEB"]["nimages"][1:] self.neb_maxstep = param["SNEB"]["maxstep"] self.neb_fmax = param["SNEB"]["fmax"] self.neb_steps = param["SNEB"]["steps"] self.tolerance = param["SNEB"]["tolerance"] self.threshold = param["SNEB"]["threshold"] self.neb_dist = param["SNEB"]["dist"] self.min_nimages = param["SNEB"]["min_nimages"] self.with_stop = param["SNEB"]["with_stop"] self.climb_steps = param["SNEB"]["climb_steps"] self.max_n = param["SNEB"]["max_n"] self.times = param["SNEB"]["times"] self.climb = param["SNEB"]["climb"] self.optimizer1 = param["IRC"]["optimizer1"] self.optimizer2 = param["IRC"]["optimizer2"] self.irc_maxstep = param["IRC"]["maxstep"] self.irc_fmax = param["IRC"]["fmax"] self.irc_steps = param["IRC"]["steps"] self.irc_dif = param["IRC"]["dif"] self.calc_notop_ts = param["IRC"]["calc_notop_ts"] #QUE self.que = [[0,1]] # 実際には一回目はinitで作成したself.imagesを用いる self.sneb_ini = self.images[0].copy() self.sneb_fin = self.images[-1].copy() self.order = ["EQ0","EQ1"] self.atoms_dict = {"EQ0":self.sneb_ini,"EQ1":self.sneb_fin} self.first_calculation = True self.iter_count = -1 while len(self.que)> 0 and self.stopping_criterion >= self.iter_count+1: self.iter_count += 1 self.debug_log(f"{self.order}") self.debug_log(f"QUE:{self.que}") self.create_path() sneb_ini_idx,sneb_fin_idx = self.que.pop(0) self.debug_log(f"SNEB:{sneb_ini_idx}-{sneb_fin_idx}") if self.first_calculation: """始めのSNEB計算(始めのみinitで作成したimagesで計算する)""" self.first_calculation = False if not self.run_sneb(self.images): self.debug_log(f"SNEB収束せず終了") continue else: self.sneb_ini = self.atoms_dict[f"EQ{sneb_ini_idx}"] self.sneb_fin = self.atoms_dict[f"EQ{sneb_fin_idx}"] if not self.run_sneb(self.sneb_ini,self.sneb_fin,self.first_nimages,self.constraints): self.debug_log(f"SNEB収束せず終了") continue imax = self.sneb.imax self.ts = self.sneb.images[imax].copy() self.ts.calc = self.calc_func() ### VIB計算と虚振動の確認 ### if not self.run_vib(): self.debug_log(f"虚振動がない") self.write_pt(self.ts) continue # 虚振動がない時はSellaを行なうように今後したい ### VIB エネルギーダイアグラムの分析 ### ts_idx,(r_use_newton,f_use_newton,n) = find_ts_idx(self.vimages, dif=self.irc_dif, calc_func=self.calc_func) if n == 0 and not self.calc_notop_ts: #TSが見えない(極大値がない)時, self.debug_log(f"ピークが見えない.終了") self.write_pt(self.ts) continue # TSを確認できない場合,Sellaを行なうように今後したい else: self.debug_log(f"TSがほぼ見えないが計算を続行") self.write_ts(self.ts) self.atoms_dict[f"TS{self.iter_count}"] = self.ts.copy() ### IRC計算 ### self.debug_log(f"IRC計算") r_converged,f_converged = self.run_irc(ts_idx, r_use_newton, f_use_newton) if r_converged: self.write_eq(self.ini) self.irc_ini_eq_n = self.eq_count-1 # IRC計算の結果ini構造となったEQ番号 self.atoms_dict[f"EQ{self.irc_ini_eq_n}"] = self.ini.copy() self.debug_log(f"Revers収束,EQ{self.irc_ini_eq_n}として保存") if f_converged: self.write_eq(self.fin) self.irc_fin_eq_n = self.eq_count-1 # IRC計算の結果fin構造となったEQ番号 self.atoms_dict[f"EQ{self.irc_fin_eq_n}"] = self.fin.copy() self.debug_log(f"Forward収束,EQ{self.irc_fin_eq_n}として保存") if not all([r_converged,f_converged]): self.debug_log(f"IRCの一方が収束しなかったので終了") continue ### CONNECTION情報の分析と書き込み ### text = self.analyze_connection(self.ts_count-1, self.eq_count-2, self.eq_count-1, self.ts, self.ini, self.fin) self.write_connections(self.ts_info_file,text) ### 同一構造か判定とQUEへのイメージの追加 ### # self.sneb.images[0] # SNEBのiniのAtoms # sneb_ini_idx # SNEBのini番号 # self.sneb.images[-1] # SNEBのiniのAtoms # sneb_fin_idx # SNEBのfin番号 # self.ini # IRCのiniのAtoms irc_ini_idx = self.eq_count-2 # IRCのini番号 # self.fin # IRCのiniのAtoms irc_fin_idx = self.eq_count-1 # IRCのfii番号 self.debug_log(f"SNEB:{sneb_ini_idx}-{sneb_fin_idx}"+ f"-->IRC:{irc_ini_idx}-{irc_fin_idx}") self.sneb_ini.calc = self.calc_func() self.sneb_fin.calc = self.calc_func() check1,val1_1,val1_2 = self.check_structures(self.ini, self.fin) #IRC_ini,IRC_finが同じか check2,val2_1,val2_2 = self.check_structures(self.sneb_ini, self.ini) #SNEB_ini,IRC_iniが同じか check3,val3_1,val3_2 = self.check_structures(self.sneb_fin, self.fin) #SNEB_fin,IRC_finが同じか check4,val4_1,val4_2 = self.check_structures(self.sneb_ini, self.fin) #SNEB_ini,IRC_finが同じか check5,val5_1,val5_2 = self.check_structures(self.ini, self.sneb_fin) #IRC_ini,NEB_finが同じか insert_idx = self.order.index(f"EQ{sneb_ini_idx}") if check1 and not check2 and not check3: self.que.append([sneb_ini_idx,irc_ini_idx]) self.que.append([irc_fin_idx,sneb_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_ini_idx}",f"TS{self.iter_count}",f"EQ{irc_fin_idx}"] self.debug_log(0) continue elif not check1 and check2 and not check3: self.que.append([irc_fin_idx,sneb_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_ini_idx}",f"TS{self.iter_count}",f"EQ{irc_fin_idx}"] self.debug_log(1) continue elif not check1 and not check2 and check3: self.que.append([sneb_ini_idx,irc_ini_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_ini_idx}",f"TS{self.iter_count}",f"EQ{irc_fin_idx}"] self.debug_log(2) continue elif not check1 and check4 and not check5: self.que.append([irc_ini_idx,sneb_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_fin_idx}",f"TS{self.iter_count}",f"EQ{irc_ini_idx}"] self.debug_log(3) continue elif not check1 and not check4 and check5: self.que.append([sneb_ini_idx,irc_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_fin_idx}",f"TS{self.iter_count}",f"EQ{irc_ini_idx}"] self.debug_log(4) continue elif not check1 and check2 and check3: self.debug_log(5) self.order[insert_idx+1:0] = [f"EQ{irc_ini_idx}",f"TS{self.iter_count}",f"EQ{irc_fin_idx}"] continue elif not check1 and check4 and check5: self.debug_log(6) self.order[insert_idx+1:0] = [f"EQ{irc_fin_idx}",f"TS{self.iter_count}",f"EQ{irc_ini_idx}"] continue else: if [val2_1,val3_1].count(True) > [val4_1,val5_1].count(True): self.que.append([sneb_ini_idx,irc_ini_idx]) self.que.append([irc_fin_idx,sneb_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_ini_idx}",f"TS{self.iter_count}",f"EQ{irc_fin_idx}"] self.debug_log(7) continue elif [val2_1,val3_1].count(True) < [val4_1,val5_1].count(True): self.que.append([sneb_ini_idx,irc_fin_idx]) self.que.append([irc_ini_idx,sneb_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_fin_idx}",f"TS{self.iter_count}",f"EQ{irc_ini_idx}"] self.debug_log(8) continue else: rmse_list = [val2_2,val3_2,val4_2,val5_2] idx = rmse_list.index(min(rmse_list)) if idx==0 or idx==1: self.que.append([sneb_ini_idx,irc_ini_idx]) self.que.append([irc_fin_idx,sneb_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_ini_idx}",f"TS{self.iter_count}",f"EQ{irc_fin_idx}"] self.debug_log(9) continue else: self.que.append([sneb_ini_idx,irc_fin_idx]) self.que.append([irc_ini_idx,sneb_fin_idx]) self.order[insert_idx+1:0] = [f"EQ{irc_fin_idx}",f"TS{self.iter_count}",f"EQ{irc_ini_idx}"] self.debug_log(10) continue self.create_path() return True if len(self.que) == 0 else False