def tp_pars(some_str: str) -> List: """ Функция для парсинга температуры и давленя в *.out файле Parameters --------- some_str: str Returns ------ Список содержащий два объекта класса Setting, Температура и давление соответственно """ _temperature = r'(Temperature)\s*([0-9.]+)\s*([\w]+).' _pressure = r'(Pressure)\s*([0-9.]+)\s*([\w]+).' tp_string = re.search(_temperature + r'\s*' + _pressure, some_str) if tp_string: return [ Setting(name=tp_string.group(1), value=tp_string.group(2), unit=tp_string.group(3)), Setting(name=tp_string.group(4), value=tp_string.group(5), unit=tp_string.group(6)) ]
def tsrot_pars(some_string: str): """Функция необходима для парсинга строк в которые входит TSrot (вращательная энтропия умноженная на температуру), на вход принимает строку в которой параметры указаны в kcal/mol, а возвращает объект класса Setting переконвертировав в J/mol. Parameters --------- some_string: str Строка содержащая в себе значение вращательной энтропии Returns ------ Возвращает объект класса Setting, содержащий в себе значения TSrot в J/mol. """ _sn_coef_ = r'sn=\s?([\d.]+)' _some_info_ = r'[\s]+qrot.sn=[\s]+[-\d.]+\s' _tsrot_segment_ = r'T\*S\(rot\)=[\s]+' _value_ = r'([-\d.]+)[\s]+kcal\/mol' _reg = re.search(_sn_coef_ + _some_info_ + _tsrot_segment_ + _value_, some_string) if _reg: _sn_coef = _reg.group(1) _value = _reg.group(2) _unit = 'kcal/mol' return Setting(name=f'{_sn_coef} T*S(rot)', value=_value, unit=_unit).convert(koef=4184, unit='J/mol')
def freq_pars(some_str: str) -> list: """ Функция для парсинга частот Parameters --------- some_string: str Строка содержащая в себе значение частоты Return ------ Возвращает список содержащий в себе объекты класса Setting, с значениями частот в cm**-1. """ _current_freq_ = [] _name_ = r'(\w+)' _freq_value_ = r'([-\d.]+)' _reg_str = _name_ + r'\s\-\-\s*' # Когда в строке не 3 частоты # Мы можем учесть это размером регулярного выражения for i in range(len(some_str.split()) - 2): _reg_str += _freq_value_ + r'\s*' freq_str = re.search(_reg_str, some_str) if freq_str: for i in range(2, len(some_str.split())): _current_freq_.append( Setting(name='freq.', value=freq_str.group(i), unit='cm**-1')) return _current_freq_
def name_value_pars(some_str: str): _name = r'([\w\s]+)' _dots = r'\s*[\.]+\s*' _value = r'([\d]+)' _reg = re.search(_name + _dots + _value, some_str) if _reg: return Setting(name=_reg.group(1), value=_reg.group(2), unit='')
def setting_pars(settings_str: str): # TODO Документация job_indx """Функция для извлечения параметров расчета из строк, принимает строку из *.tab файла, содержащую подстроку 'Settings' Arguments --------- settings_str: str Строка *.tab файла, содержащая ключевое слово 'Settings' Return ------ job_indx: str settings_list: tuple Кортеж содержащий объекты класса Setting, описывающие условия проведения расчета Example ------- >>> setting_pars('Settings job 2 : T= 223.15 K ; x(1)= 0.1000;') (2, (T= 223.15 K, x(1)= 0.1 %)) """ settings_list = [] job_indx, new_line = settings_str.split(":") job_indx = job_indx.split()[2] settings = new_line.split(";") for setting in settings: new_setting = None if len(setting.split()) == 3: settings_list.append(Setting.from_record(setting)) elif len(setting.split()) == 2: new_setting = Setting.from_record(setting) new_setting.convert(name=compound_nr(new_setting.name), unit="%") settings_list.append(new_setting) elif len(setting.split()) > 3: # TODO: Проблемное место, пофиксить n в chunkit for element in chunkit(setting.split(), n=len(setting.split()) / 2): new_setting = Setting.from_record(element) new_setting.convert(name=compound_nr(new_setting.name), unit="%") settings_list.append(new_setting) return int(job_indx), tuple(settings_list)
def settings_df(self, detailed=None): # TODO Документация """ """ columns = [self.job_indx] index = [x.name for x in self.settings] if 'p=' in index: pass else: index.append('p=') self.settings.append(Setting(name='p=', value=1, unit='atm')) if detailed: data = self.settings else: data = [x.value for x in self.settings] return pd.DataFrame(columns=columns, index=index, data=data)
def freq_pars(some_str: str): """Функция для парсинга строк содержащих значения частот Parameters --------- some_string: str Строка содержащая в себе значение частоты Returns ------ Возвращает объект класса Setting, содержащий в себе значения параметра в cm**-1. """ _freq_number_ = r'([\d]+):\s+' _value_ = r'([-\d.]+)' _unit_ = r'\s(cm\**-1)' _reg = re.search(_freq_number_ + _value_ + _unit_, some_str) if _reg: return Setting(name='freq.', value=_reg.group(2), unit=_reg.group(3))
def molecular_mass_pars(some_str: str) -> Setting: """ Функция для извлечения молекулярной массы Parameters ---------- some_str : str Строка для парсинга Returns ------- Setting Содержит название, значение и единицы измерения параметра """ _mol_mass = r'Molecular mass:\s*([0-9.]+)\s*([\w]+).' _mol_mass_string = re.search(_mol_mass, some_str) if _mol_mass_string: return Setting(name='molecular mass', value=_mol_mass_string.group(1), unit=_mol_mass_string.group(2))
def scf_energy_pars(some_str: str): """ Функция для извлечения электронной энергии Parameters ---------- some_str : str Строка для парсинга Returns ------- Setting Содержит название, значение и единицы измерения параметра """ _scf_energy = r'SCF\sDone:\s*E[\w\d()-]*\s*=\s*([-\d.]*)' _scf_energy_string = re.search(_scf_energy, some_str) if _scf_energy_string: return Setting(name='scf energy', value=_scf_energy_string.group(1), unit='Eh').convert(koef=EH_JMOL, unit='J/mol')
def other_param_pars(some_string: str): """Функция предназначена для парсинга параметров, указанных в Eh, автоматически конвертирует их в J/mol Parameters --------- some_string: str Строка содержащая в себе значения вращательной энтропии Returns ------ Возвращает объект класса Setting, содержащий в себе значения параметра в J/mol. """ _name_ = r'([\w\s]+\w+)' _dots_ = r'\s*[\.]+\s*' _value_ = r'([-\d.]+)\s*.*' _reg = re.search(_name_ + _dots_ + _value_, some_string) if _reg: return Setting(name=_reg.group(1), value=_reg.group(2), unit='Eh').convert(koef=EH_JMOL, unit='J/mol')
def properties_pars(some_str: str) -> Setting: """ Извлекает значение и название типа энтропии из строки Parameters ---------- some_str : str Строки типа: "Vibrational 120.478 27.641 25.420" Returns ------- Setting Содержит название, значение в J/Mol*K, и единицы измерения """ _name_ = some_str.split()[0] _value_ = r'([0-9]{1,10}\.[0-9]{3})' _whitespace_ = r'\s{2,15}' entropy_str = re.search( _value_ + _whitespace_ + _value_ + _whitespace_ + _value_, some_str) if entropy_str: return Setting(name=_name_ + ' entropy', value=entropy_str.group(3), unit='Cal/mol*K').convert(koef=4.184, unit='J/mol*K')
def parameter_pars(some_str: str) -> Setting: """Функция для парсинга термодинамических параметров, должна принимать строки содержащие в себе один элемент из списка PARAMETER_LIST Parameters --------- some_str: str Строка для парсинга Return ------ Возвращает объект класса Setting, содержащий в себе термодинамические параметры в J/mol """ _name_ = some_str.split('=')[0] _value_ = r'(-?[0-9]{1,10}\.[0-9]{6})' param_str = re.search(_value_, some_str) # coef = 4.359744 * 6.022e5 # hartree_to_j_coef / N_Avogadro if param_str: return Setting(name=_name_[1:], value=param_str.group(1), unit='Eh').convert(koef=EH_JMOL, unit='J/mol')
def tp_pars(some_string: str): """Функция для парсинга строк содержащих значения температуры и давления Parameters --------- some_string: str Строка содержащая в себе значение параметра (температура или давление) Returns ------ Возвращает объект класса Setting, содержащий в себе значения параметра в J/mol. """ _name_ = r'([\w\s]+)' _dots_ = r'\s*[\.]+\s*' _value_ = r'([\d]+.[\d]+)' _unit_ = r'\s*(\w+)' _reg = re.search(_name_ + _dots_ + _value_ + _unit_, some_string) if _reg: return Setting(name=_reg.group(1).split()[0], value=_reg.group(2), unit=_reg.group(3))
def post_process(element: Setting): if element.name.lower() == 'number of atoms': element.convert(name='natoms') elif element.name.lower() == 'total': element.convert(name='molecular mass') elif element.name.lower() == 'zero point energy': element.convert(name='zero-point energy') elif element.name.lower() == 'final gibbs free enthalpy': element.convert('sum of electronic and thermal free energies') elif element.name.lower() == 'total enthalpy': element.convert(name='sum of electronic and thermal enthalpies') elif element.name.lower() == 'electronic energy': element.convert(name='scf energy') elif 'THERMOCHEMISTRY AT' in element.name: element = None elif element.name.lower() == 'number of degrees of freedom': element.convert('deg. of freedom') elif element.name.lower() == 'freq.' and element.value == 0: element = None else: pass return element
def file_pars(file_path: str) -> pd.Series: """ Принимает на вход путь к файлу, и парсит его, возвращая результаты в виде pandas.Series, если в файле не было данных или нужного файла не существует вылетит с ошибкой. Parameters ---------- file_path : str Путь к файлу *.out Returns ------- pd.Series Серия содержит термохимические и структурные параметры, необходимые для дальнейшего получения термодинамических данных """ read_data = read_data_gaussian(file_path) _all_parameters = [] if read_data: for line in read_data: if 'Frequencies' in line: _all_parameters.append(freq_pars(line)) elif any(xs in line for xs in PARAMETER_LIST): _all_parameters.append(parameter_pars(line)) elif any(xs in line for xs in PROPERTIES_LIST): _all_parameters.append(properties_pars(line)) elif 'Temperature' in line: _all_parameters.append(tp_pars(line)) elif 'Molecular mass' in line: _all_parameters.append(molecular_mass_pars(line)) elif 'SCF Done' in line: _all_parameters.append(scf_energy_pars(line)) elif 'NAtoms' in line: _all_parameters.append( Setting(name='natoms', value=line.split()[1], unit='n')) elif 'Full point group' in line: sym_line = line.split()[3] _all_parameters.append( Setting(name=line.split()[4], value=line.split()[5], unit='')) elif 'Deg. of freedom' in line: _all_parameters.append( Setting(name='deg. of freedom', value=line.split()[3], unit='')) else: pass raw_data = list_unpack(_all_parameters) data = [parameter for parameter in raw_data if parameter is not None] # TODO Comments if data: indexes = [parameter.name for parameter in data] series = pd.Series(data=[x.value for x in data], name='parameters', index=indexes) series.loc['full point group'] = sym_line series.loc['qm_program'] = 'gaussian' if series.loc['natoms'] == 1: series.loc['atom'] = True series.loc['linear'] = True else: series.loc['atom'] = False if series.loc['deg. of freedom'] == 1: series.loc['linear'] = True elif series.loc[ 'natoms'] > 1 and series.loc['natoms'] * 3 - 5 == len( series.loc['freq.']): series.loc['linear'] = True else: series.loc['linear'] = False return series