|
|
本帖最后由 Graphite 于 2018-11-15 18:08 编辑
CAUTION: This page is written in Chinese, If you read it via Google Translate, the code below will LOSE ITS FORMAT, so DO NOT copy and paste the code in translated page.
本模块(ChOrca)主要用于解决以下问题:
1.治好用惯了gview的人要用ORCA时, 粘来粘去的毛病
2.节省反复编写输入文件和建文件夹浪费的时间
3.简单调用cclib对输出文件进行解析
环境需求:
1.cclib -- 一个用于解析量化软件输出的库
2.需要自定义settings.json(下文提到)
注意:
本模块仅在windows下测试, linux理论可用.
By the way, 笔者用win10+GView+ORCA+本模块+VisualStudio Code集成终端用的很爽...虚拟机已删...
例: 本文件夹下有数个.gjf文件, 现在想要调用ORCA提取其中的坐标进行计算, 编写如下脚本:- import ChOrca
- for gjf in ['a.gjf', 'b.gjf', 'c.gjf', ]:
- name = gjf.split('.')[0]
- calc = ChOrca.Calculation(name)
- calc.set_input_from_template('rank_1_opt', 'mem_1000mb', 0, 1, gjf)
- calc.run_orca()
复制代码
这里的'rank_1_opt', 'mem_1000mb'是我们在settings.json里的键, 实际调用其对应的值, 相当于设置了别名. settings.json最好与此脚本放在同一目录下.
- {
- "orca_path": "D:\\ProgramFiles\\orca\\orca.exe",
- "template_keyword_excls": {
- "rank_1_opt": "!pal4 blyp def2-svp def2/j d3 miniprint"
- },
- "template_keyword_prcts": {
- "mem_1000mb": "%maxcore 1000"
- }
- }
复制代码
模块包含的方法和参数, 在代码内部的docstring中都有提到. 源代码如下:- #coding: utf-8
- """
- This is a python module for calculation orca jobs conviently.
- Import it and enjoy!
- Lincense: Whatever, use it, copy it, or modify it as you like.
- Author: T. H. Graphite
- """
- import json
- import os
- import re
- import subprocess
- import sys
- import cclib
- class Calculation():
- """Calculate orca jobs.
-
- Keyword Arguments:
- name {str}-- the instance name, for creating new files and dirs.
- work_dir {str} -- work directory, default: the path of module.
- Once a instance is created, a new directory work_dir/name will be made.
- Thus, all the calcultion will be done at this directory.
- Methods:
- load_settings(path)
- set_input(keyword_excl, keyword_prct, ncharge, nmulti, coordinates)
- set_input_from_xyz(keyword_excl, keyword_prct, ncharge, nmulti, path)
- Attributes:
- parse_json
- parse_result
- """
- def __init__(self, name, work_dir=sys.path[0]):
- self.name = name
- self.work_dir = work_dir
- os.chdir(self.work_dir)
- self.dir = os.path.join(self.work_dir, self.name)
- self.load_settings()
- if not os.path.exists(self.dir):
- os.mkdir(self.dir)
- self.input_path = os.path.join(self.dir, self.name + '.inp')
- self.output_path = os.path.join(self.dir, self.name + '.out')
- def load_settings(self, path=None):
- """Load settings from path.
-
- Keyword Arguments:
- path {str} -- the path of json settings file (default: work_dir/settings.json)
- """
- if path is None:
- path = os.path.join(self.work_dir, 'settings.json')
- try:
- settings_file = open(path, 'r')
- settings_dict = json.load(settings_file)
- self.orca_path = settings_dict['orca_path']
- self.template_keyword_excls = settings_dict['template_keyword_excls']
- self.template_keyword_prcts = settings_dict['template_keyword_prcts']
- except Exception:
- print('No settings file or invalid format. Exit.')
- exit()
- def set_input(self, keyword_excl, keyword_prct, ncharge, nmulti, coordinates):
- """Set input contents for calculation
-
- Arguments:
- keyword_excl {str} -- keywords start with a !
- keyword_prct {str} -- keyword start with a %, a.k.a. the 'block' in orca manual.
- ncharge {str or int} -- charge of molecule
- nmulti {str or int} -- multiplicity of molecule
- coordinates {str} -- coordinates in format like "C 1.000 1.000 1.000\nC 2.000 2.000 2.000..." .
- """
- self.input_content = "{kw_excl}\n{kw_prct}\n*xyz {nchg} {nmtp}\n{coord}\n*".format(
- kw_excl=keyword_excl,
- kw_prct=keyword_prct,
- nchg=str(ncharge),
- nmtp=str(nmulti),
- coord=coordinates)
- def set_input_from_xyz(self, keyword_excl, keyword_prct, ncharge, nmulti, path):
- """Set input contents for calculation, coordinates from a xyz-like file
- (.xyz, .gjf, or any file can be filter by the regex below)
-
- Arguments:
- keyword_excl {str} -- keywords start with a !
- keyword_prct {str} -- keyword start with a %
- ncharge {str or int} -- charge of molecule
- nmulti {str or int} -- multiplicity of molecule
- path {str} -- the path of the xyz-like file
- """
- regex = r'\ {0,2}[A-Z][a-z]?(\ *-?[0-9]*\.[0-9]*){3,}'
- pattern = re.compile(regex)
- file_object = open(path, 'r')
- coordinates = str()
- for line in file_object:
- if pattern.match(line):
- coordinates += line
- file_object.close()
- self.set_input(keyword_excl, keyword_prct, ncharge, nmulti, coordinates)
- def set_input_from_template(self, temp_excl, temp_prct, ncharge, nmulti, path):
- """Like set_input_from_xyz, but use template names (in the settings file, see: load_settings)
-
- Arguments:
- temp_excl {str} -- a key of template_keyword_excls
- temp_prct {str} -- a key of template_keyword_prcts
- ncharge {str or int} -- charge of molecule
- nmulti {str or int} -- multiplicity of molecule
- path {str} -- the path of the xyz-like file
- """
- keyword_excl = self.template_keyword_excls[temp_excl]
- keyword_prct = self.template_keyword_prcts[temp_prct]
- self.set_input_from_xyz(keyword_excl, keyword_prct, ncharge, nmulti, path)
-
- def run_orca(self):
- """Running orca
-
- Returns:
- Boolean -- whether the calculation is terminated normally or not.
- """
- if not os.path.exists(self.orca_path):
- raise(EnvironmentError)
- input_file = open(self.input_path, 'w')
- input_file.write(self.input_content)
- input_file.close()
- run_command = self.orca_path + ' ' + self.input_path + ' > ' + self.output_path
- try:
- subprocess.check_call(run_command, shell=True)
- return True
- except subprocess.CalledProcessError:
- return False
- @property
- def parse_json(self):
- """Parse the output file using cclib.
-
- Returns:
- str -- a json string, including lots of interesting infos
- """
- parse_target = cclib.ccopen(self.output_path)
- if parse_target is not None:
- try:
- print(parse_target)
- parse_result = parse_target.parse()
- json = cclib.ccwrite(
- parse_result, outputtype='json', returnstr=True)
- return json
- except Exception:
- return None
- @property
- def parse_result(self):
- """The dictionary version of parse_json.
-
- Returns:
- dict -- a flattened dict
- """
- def walk(dictionary):
- for key, value in dictionary.items():
- if isinstance(value, dict):
- for tup in walk(value):
- yield (key, ) + tup
- else:
- yield key, value
- result = dict()
- for tup in walk(json.loads(self.parse_json)):
- result.setdefault('/'.join(tup[:-1]), tup[-1])
- return result
- if __name__ == '__main__':
- print(__doc__)
复制代码
|
评分 Rate
-
查看全部评分 View all ratings
|