Example #1
0
    def test_rotor_symmetry(self):
        """Test that ARC automatically determines a correct rotor symmetry"""
        path1 = os.path.join(arc_path, 'arc', 'testing', 'rotor_scans',
                             'OOC1CCOCO1.out')  # symmetry = 1; min at -10 o
        path2 = os.path.join(arc_path, 'arc', 'testing', 'rotor_scans',
                             'H2O2.out')  # symmetry = 1
        path3 = os.path.join(arc_path, 'arc', 'testing', 'rotor_scans',
                             'N2O3.out')  # symmetry = 2
        path4 = os.path.join(arc_path, 'arc', 'testing', 'rotor_scans',
                             'sBuOH.out')  # symmetry = 3
        path5 = os.path.join(arc_path, 'arc', 'testing', 'rotor_scans',
                             'CH3C(O)O_FreeRotor.out')  # symmetry = 6

        symmetry1, _ = determine_rotor_symmetry(rotor_path=path1,
                                                label='label',
                                                pivots=[3, 4])
        symmetry2, _ = determine_rotor_symmetry(rotor_path=path2,
                                                label='label',
                                                pivots=[3, 4])
        symmetry3, _ = determine_rotor_symmetry(rotor_path=path3,
                                                label='label',
                                                pivots=[3, 4])
        symmetry4, _ = determine_rotor_symmetry(rotor_path=path4,
                                                label='label',
                                                pivots=[3, 4])
        symmetry5, _ = determine_rotor_symmetry(rotor_path=path5,
                                                label='label',
                                                pivots=[3, 4])

        self.assertEqual(symmetry1, 1)
        self.assertEqual(symmetry2, 1)
        self.assertEqual(symmetry3, 2)
        self.assertEqual(symmetry4, 3)
        self.assertEqual(symmetry5, 6)
Example #2
0
def check_scan_quality(spc):
    """
    A helper function used to get the status of rotor scans
    """

    for rotor in spc['rotors_dict'].values():

        path = rotor['scan_path']
        if path:
            scan_args = parse_scan_args(path)
            energies, _ = parse_1d_scan_energies(path)
            invalid, reason, _, actions = scan_quality_check(
                spc['label'],
                pivots=rotor['scan'][1:-1],
                energies=energies,
                scan_res=scan_args['step_size'],
                log_file=path)
        else:
            rotor['success'] = False
            rotor['invalidation_reason'] = 'Unknown'
            continue

        if not invalid:
            rotor['success'] = True
            rotor['symmetry'] = determine_rotor_symmetry(
                label=spc['label'],
                pivots=rotor['scan'][1:3],
                energies=energies)[0]
            continue

        if 'change conformer' in actions:
            print(spc['label'] +
                  ': has a bad conformer orientation according to ' +
                  str(rotor['scan']))
            xyz = xyz_to_xyz_file_format(actions['change conformer'])
            return {'label': spc['label'], 'change conformer': xyz}

        if 'barrier' in reason:
            rotor['success'] = False
            rotor['invalidation_reason'] = reason
            continue

        # Otherwise need to come up with troubleshooting methods
        species_scan_lists = [rotor['scan']]
        scan_trsh, scan_res = trsh_scan_job(spc['label'],
                                            scan_args['step_size'],
                                            rotor['scan'], species_scan_lists,
                                            actions, path)
        rotor['trsh_methods'] = [{
            'scan_trsh': scan_trsh,
            'scan_res': scan_res
        }]
        rotor['archived'].append(rotor['scan_path'])
        spc['scan'].remove(rotor['scan_path'])

    return spc
Example #3
0
    def _generate_arkane_species_file(self, species):
        """
        A helper function for generating the Arkane species file.
        Assigns the input file path to species.arkane_file.

        Args:
            species (ARCSpecies): The species to process.

        Returns:
            str: The Arkane output path.
        """
        folder_name = 'rxns' if species.is_ts else 'Species'
        output_path = os.path.join(self.project_directory, 'output', folder_name, species.label, 'arkane')
        if not os.path.isdir(output_path):
            os.makedirs(output_path)

        if species.yml_path is not None:
            species.arkane_file = species.yml_path
            return output_path

        species.determine_symmetry()
        try:
            sp_path = self.output[species.label]['composite']
        except KeyError:
            try:
                sp_path = self.output[species.label]['sp']
            except KeyError:
                raise SchedulerError('Could not find path to sp calculation for species {0}'.format(
                    species.label))
        if species.number_of_atoms == 1:
            freq_path = sp_path
            opt_path = sp_path
        else:
            freq_path = self.output[species.label]['freq']
            opt_path = self.output[species.label]['freq']
        rotors, rotors_description = '', ''
        if any([i_r_dict['success'] for i_r_dict in species.rotors_dict.values()]):
            rotors = '\n\nrotors = ['
            rotors_description = '1D rotors:\n'
            for i in range(species.number_of_rotors):
                pivots = str(species.rotors_dict[i]['pivots'])
                scan = str(species.rotors_dict[i]['scan'])
                if species.rotors_dict[i]['success']:
                    rotor_path = species.rotors_dict[i]['scan_path']
                    rotor_type = determine_rotor_type(rotor_path)
                    top = str(species.rotors_dict[i]['top'])
                    try:
                        rotor_symmetry, max_e = determine_rotor_symmetry(rotor_path, species.label, pivots)
                    except RotorError:
                        logger.error('Could not determine rotor symmetry for species {0} between pivots {1}.'
                                     ' Setting the rotor symmetry to 1, which is probably WRONG.'.format(
                                      species.label, pivots))
                        rotor_symmetry = 1
                        max_e = None
                    max_e = ', max scan energy: {0:.2f} kJ/mol'.format(max_e) if max_e is not None else ''
                    free = ' (set as a FreeRotor)' if rotor_type == 'FreeRotor' else ''
                    rotors_description += 'pivots: ' + str(pivots) + ', dihedral: ' + str(scan) +\
                        ', rotor symmetry: ' + str(rotor_symmetry) + max_e + free + '\n'
                    if rotor_type == 'HinderedRotor':
                        rotors += input_files['arkane_hindered_rotor'].format(rotor_path=rotor_path, pivots=pivots,
                                                                              top=top, symmetry=rotor_symmetry)
                    elif rotor_type == 'FreeRotor':
                        rotors += input_files['arkane_free_rotor'].format(rotor_path=rotor_path, pivots=pivots,
                                                                          top=top, symmetry=rotor_symmetry)
                    if i < species.number_of_rotors - 1:
                        rotors += ',\n          '
                else:
                    rotors_description += '* Invalidated! pivots: ' + str(pivots) + ', dihedral: ' + str(scan) +\
                                          ', invalidation reason: ' + species.rotors_dict[i]['invalidation_reason'] +\
                                          '\n'

            rotors += ']'
            species.long_thermo_description += rotors_description + '\n'
        # write the Arkane species input file
        input_file_path = os.path.join(self.project_directory, 'output', folder_name, species.label,
                                       '{0}_arkane_input.py'.format(species.label))
        input_file = input_files['arkane_input_species']
        if self.use_bac and not species.is_ts:
            logger.info('Using the following BAC for {0}: {1}'.format(species.label, species.bond_corrections))
            bonds = 'bonds = {0}\n\n'.format(species.bond_corrections)
        else:
            logger.debug('NOT using BAC for {0}'.format(species.label))
            bonds = ''
        input_file = input_file.format(bonds=bonds, symmetry=species.external_symmetry,
                                       multiplicity=species.multiplicity, optical=species.optical_isomers,
                                       sp_level=self.sp_level, sp_path=sp_path, opt_path=opt_path,
                                       freq_path=freq_path, rotors=rotors)
        with open(input_file_path, 'wb') as f:
            f.write(input_file)
        species.arkane_file = input_file_path
        return output_path
Example #4
0
    def generate_arkane_species_file(self,
                                     species: Type[ARCSpecies],
                                     bac_type: Optional[str],
                                     ) -> Optional[str]:
        """
        A helper function for generating an Arkane Python species file.
        Assigns the path of the generated file to the species.arkane_file attribute.

        Args:
            species (ARCSpecies): The species to process.
            bac_type (str): The bond additivity correction type. 'p' for Petersson- or 'm' for Melius-type BAC.
                            ``None`` to not use BAC.

        Returns:
            str: The path to the species arkane folder (Arkane's default output folder).
        """
        folder_name = 'rxns' if species.is_ts else 'Species'
        species_folder_path = os.path.join(self.output_directory, folder_name, species.label)
        arkane_output_path = os.path.join(species_folder_path, 'arkane')
        if not os.path.isdir(arkane_output_path):
            os.makedirs(arkane_output_path)

        if species.yml_path is not None:
            species.arkane_file = species.yml_path
            return arkane_output_path

        species.determine_symmetry()

        sp_path = self.output_dict[species.label]['paths']['composite'] \
            or self.output_dict[species.label]['paths']['sp']
        if species.number_of_atoms == 1:
            freq_path = sp_path
            opt_path = sp_path
        else:
            freq_path = self.output_dict[species.label]['paths']['freq']
            opt_path = self.output_dict[species.label]['paths']['freq']

        return_none_text = None
        if not sp_path:
            return_none_text = 'path to the sp calculation'
        if not freq_path:
            return_none_text = 'path to the freq calculation'
        if not os.path.isfile(freq_path):
            return_none_text = f'the freq file in path {freq_path}'
        if not os.path.isfile(sp_path):
            return_none_text = f'the freq file in path {sp_path}'
        if return_none_text is not None:
            logger.error(f'Could not find {return_none_text} for species {species.label}. Not calculating properties.')
            return None

        rotors, rotors_description = '', ''
        if species.rotors_dict is not None and any([i_r_dict['pivots'] for i_r_dict in species.rotors_dict.values()]):
            rotors = '\n\nrotors = ['
            rotors_description = '1D rotors:\n'
            for i in range(species.number_of_rotors):
                pivots = str(species.rotors_dict[i]['pivots'])
                scan = str(species.rotors_dict[i]['scan'])
                if species.rotors_dict[i]['success']:
                    rotor_path = species.rotors_dict[i]['scan_path']
                    rotor_type = determine_rotor_type(rotor_path)
                    top = str(species.rotors_dict[i]['top'])
                    try:
                        rotor_symmetry, max_e, _ = determine_rotor_symmetry(species.label, pivots, rotor_path)
                    except RotorError:
                        logger.error(f'Could not determine rotor symmetry for species {species.label} between '
                                     f'pivots {pivots}. Setting the rotor symmetry to 1, '
                                     f'this could very well be WRONG.')
                        rotor_symmetry = 1
                        max_e = None
                    scan_trsh = ''
                    if 'trsh_methods' in species.rotors_dict[i]:
                        scan_res = 360
                        for scan_trsh_method in species.rotors_dict[i]['trsh_methods']:
                            if 'scan_trsh' in scan_trsh_method and len(scan_trsh) < len(scan_trsh_method['scan_trsh']):
                                scan_trsh = scan_trsh_method['scan_trsh']
                            if 'scan_res' in scan_trsh_method and scan_res > scan_trsh_method['scan_res']:
                                scan_res = scan_trsh_method['scan_res']
                        scan_trsh = f'Troubleshot with the following constraints and {scan_res} degrees ' \
                                    f'resolution:\n{scan_trsh}' if scan_trsh else ''
                    max_e = f', max scan energy: {max_e:.2f} kJ/mol' if max_e is not None else ''
                    free = ' (set as a FreeRotor)' if rotor_type == 'FreeRotor' else ''
                    rotors_description += f'pivots: {pivots}, dihedral: {scan}, ' \
                                          f'rotor symmetry: {rotor_symmetry}{max_e}{free}\n{scan_trsh}'
                    if rotor_type == 'HinderedRotor':
                        rotors += input_files['arkane_hindered_rotor'].format(rotor_path=rotor_path,
                                                                              pivots=pivots,
                                                                              top=top,
                                                                              symmetry=rotor_symmetry)
                    elif rotor_type == 'FreeRotor':
                        rotors += input_files['arkane_free_rotor'].format(rotor_path=rotor_path,
                                                                          pivots=pivots,
                                                                          top=top,
                                                                          symmetry=rotor_symmetry)
                    if i < species.number_of_rotors - 1:
                        rotors += ',\n          '
                else:
                    rotors_description += f'* Invalidated! pivots: {pivots}, dihedral: {scan}, ' \
                                          f'invalidation reason: {species.rotors_dict[i]["invalidation_reason"]}\n'

            rotors += ']'
            if 'rotors' not in species.long_thermo_description:
                species.long_thermo_description += rotors_description + '\n'

        # write the Arkane species input file
        bac_txt = '' if bac_type is not None else '_no_BAC'
        input_file_path = os.path.join(species_folder_path, f'{species.label}_arkane_input{bac_txt}.py')
        input_file = input_files['arkane_input_species'] if 'sp_sol' not in self.output_dict[species.label]['paths'] \
            else input_files['arkane_input_species_explicit_e']
        if bac_type is not None and not species.is_ts:
            logger.info(f'Using the following BAC (type {bac_type}) for {species.label}: {species.bond_corrections}')
            bonds = f'bonds = {species.bond_corrections}\n\n'
        else:
            logger.debug(f'NOT using BAC for {species.label}')
            bonds = ''

        if 'sp_sol' not in self.output_dict[species.label]['paths']:
            input_file = input_file.format(bonds=bonds,
                                           symmetry=species.external_symmetry,
                                           multiplicity=species.multiplicity,
                                           optical=species.optical_isomers,
                                           sp_path=sp_path,
                                           opt_path=opt_path,
                                           freq_path=freq_path,
                                           rotors=rotors)
        else:
            # e_elect = e_original + sp_e_sol_corrected - sp_e_uncorrected
            original_log = ess_factory(self.output_dict[species.label]['paths']['sp'])
            e_original = original_log.load_energy()
            e_sol_log = ess_factory(self.output_dict[species.label]['paths']['sp_sol'])
            e_sol = e_sol_log.load_energy()
            e_no_sol_log = ess_factory(self.output_dict[species.label]['paths']['sp_no_sol'])
            e_no_sol = e_no_sol_log.load_energy()
            e_elect = (e_original + e_sol - e_no_sol) / (constants.E_h * constants.Na)  # convert J/mol to Hartree
            logger.info(f'\nSolvation correction scheme for {species.label}:\n'
                        f'Original electronic energy: {e_original * 0.001} kJ/mol\n'
                        f'Solvation correction: {(e_sol - e_no_sol) * 0.001} kJ/mol\n'
                        f'New electronic energy: {(e_original + e_sol - e_no_sol) * 0.001} kJ/mol\n\n')
            print(f'e_elect final: {(e_original + e_sol - e_no_sol) * 0.001} kJ/mol\n\n')
            input_file = input_files['arkane_input_species_explicit_e']
            input_file = input_file.format(bonds=bonds,
                                           symmetry=species.external_symmetry,
                                           multiplicity=species.multiplicity,
                                           optical=species.optical_isomers,
                                           sp_level=self.sp_level,
                                           e_elect=e_elect,
                                           opt_path=opt_path,
                                           freq_path=freq_path,
                                           rotors=rotors)

        if freq_path:
            with open(input_file_path, 'w') as f:
                f.write(input_file)
            species.arkane_file = input_file_path
        else:
            species.arkane_file = None

        return arkane_output_path