Example #1
0
    def execute(self):
        """
        Execute, in order, the jobs found in input file specified by the
        `input_file` attribute.
        """

        # Initialize the logging system (both to the console and to a file in the
        # output directory)
        initialize_log(self.verbose,
                       os.path.join(self.output_directory, 'arkane.log'))

        # Print some information to the beginning of the log
        log_header()

        # Load the input file for the job
        self.job_list = self.load_input_file(self.input_file)
        logging.info('')

        # Initialize (and clear!) the output files for the job
        if self.output_directory is None:
            self.output_directory = os.path.dirname(
                os.path.abspath(self.input_file))
        output_file = os.path.join(self.output_directory, 'output.py')
        with open(output_file, 'w'):
            pass
        chemkin_file = os.path.join(self.output_directory, 'chem.inp')

        # write the chemkin files and run the thermo and then kinetics jobs
        with open(chemkin_file, 'w') as f:
            write_elements_section(f)

            f.write('SPECIES\n\n')

            # write each species in species block
            for job in self.job_list:
                if isinstance(job, ThermoJob):
                    f.write(job.species.to_chemkin())
                    f.write('\n')

            f.write('\nEND\n\n\n\n')
            f.write('THERM ALL\n')
            f.write('    300.000  1000.000  5000.000\n\n')

        # run thermo and statmech jobs (also writes thermo blocks to Chemkin file)
        supporting_info = []
        hindered_rotor_info = []
        bacjob_num = 1
        for job in self.job_list:
            if isinstance(job, ThermoJob):
                job.execute(output_directory=self.output_directory,
                            plot=self.plot)
            if isinstance(job, StatMechJob):
                job.execute(output_directory=self.output_directory,
                            plot=self.plot,
                            pdep=is_pdep(self.job_list))
                if hasattr(job, 'supporting_info'):
                    supporting_info.append(job.supporting_info)
                if hasattr(job, 'raw_hindered_rotor_data'):
                    for hr_info in job.raw_hindered_rotor_data:
                        hindered_rotor_info.append(hr_info)
            if isinstance(job, BACJob):
                job.execute(output_directory=self.output_directory,
                            plot=self.plot,
                            jobnum=bacjob_num)
                bacjob_num += 1

        with open(chemkin_file, 'a') as f:
            f.write('\n')
            f.write('END\n\n\n\n')
            f.write('REACTIONS    KCAL/MOLE   MOLES\n\n')

        if supporting_info:
            # write supporting_info.csv for statmech jobs
            supporting_info_file = os.path.join(self.output_directory,
                                                'supporting_information.csv')
            with open(supporting_info_file, 'w') as csvfile:
                writer = csv.writer(csvfile,
                                    delimiter=',',
                                    quotechar='"',
                                    quoting=csv.QUOTE_MINIMAL)
                writer.writerow([
                    'Label', 'Symmetry Number', 'Number of optical isomers',
                    'Symmetry Group', 'Rotational constant (cm-1)',
                    'Calculated Frequencies (unscaled and prior to projection, cm^-1)',
                    'Electronic energy (J/mol)',
                    'E0 (electronic energy + ZPE, J/mol)',
                    'E0 with atom and bond corrections (J/mol)',
                    'Atom XYZ coordinates (angstrom)', 'T1 diagnostic',
                    'D1 diagnostic'
                ])
                for row in supporting_info:
                    label = row[0]
                    rot = '-'
                    freq = '-'
                    if row[4] is not None and isinstance(
                            row[4].rotationalConstant.value, float):
                        # diatomic species have a single rotational constant
                        rot = '{0:.2f}'.format(row[4].rotationalConstant.value)
                    elif row[4] is not None:
                        rot = ', '.join([
                            '{0:.2f}'.format(s)
                            for s in row[4].rotationalConstant.value
                        ])
                    if row[5] is not None:
                        freq = ''
                        if row[6] is not None:  # there is a negative frequency
                            freq = '{0:.1f}'.format(abs(row[6])) + 'i, '
                        freq += ', '.join(
                            ['{0:.1f}'.format(s) for s in row[5]])
                    atoms = ', '.join([
                        "{0}    {1}".format(
                            atom, "    ".join([str(c) for c in coords]))
                        for atom, coords in zip(row[10], row[11])
                    ])
                    writer.writerow([
                        label, row[1], row[2], row[3], rot, freq, row[7],
                        row[8], row[9], atoms, row[12], row[13]
                    ])
        if hindered_rotor_info:
            hr_file = os.path.join(self.output_directory,
                                   'hindered_rotor_scan_data.csv')
            # find longest length to set column number for energies
            max_energy_length = max([len(hr[4]) for hr in hindered_rotor_info])
            with open(hr_file, 'w') as csvfile:
                writer = csv.writer(csvfile,
                                    delimiter=',',
                                    quotechar='"',
                                    quoting=csv.QUOTE_MINIMAL)
                writer.writerow([
                    'species', 'rotor_number', 'symmetry',
                    'resolution (degrees)', 'pivot_atoms', 'frozen_atoms'
                ] + [
                    'energy (J/mol) {}'.format(i)
                    for i in range(max_energy_length)
                ])
                for row in hindered_rotor_info:
                    writer.writerow([
                        row[0], row[1], row[2], row[3][1] * 180 /
                        np.pi, row[5], row[6]
                    ] + [a for a in row[4]])
        # run kinetics and pdep jobs (also writes reaction blocks to Chemkin file)
        for job in self.job_list:
            if isinstance(job, KineticsJob):
                job.execute(output_directory=self.output_directory,
                            plot=self.plot)
            elif isinstance(job, PressureDependenceJob) and not any(
                [isinstance(job, ExplorerJob) for job in self.job_list]):
                # if there is an explorer job the pdep job will be run in the explorer job
                if job.network is None:
                    raise InputError(
                        'No network matched the label of the pressureDependence block and there is no explorer block '
                        'to generate a network')
                job.execute(output_file=output_file, plot=self.plot)
            elif isinstance(job, ExplorerJob):
                thermo_library, kinetics_library, species_list = self.get_libraries(
                )
                job.execute(output_file=output_file,
                            plot=self.plot,
                            species_list=species_list,
                            thermo_library=thermo_library,
                            kinetics_library=kinetics_library)

        with open(chemkin_file, 'a') as f:
            f.write('END\n\n')

        # Print some information to the end of the log
        log_footer()

        if self.save_rmg_libraries:
            # save RMG thermo and kinetics libraries
            species, reactions = list(), list()
            for job in self.job_list:
                if isinstance(job, ThermoJob) and len(job.species.molecule):
                    species.append(job.species)
                elif isinstance(job, KineticsJob) \
                        and all([len(species.molecule) for species in job.reaction.reactants + job.reaction.products]):
                    reactions.append(job.reaction)
                elif isinstance(job, PressureDependenceJob):
                    for reaction in job.network.path_reactions:
                        if all([
                                len(species.molecule)
                                for species in reaction.reactants +
                                reaction.products
                        ]):
                            reactions.append(reaction)
            lib_path = os.path.join(self.output_directory, 'RMG_libraries')
            model_chemistry = f' at the {self.model_chemistry} level of theory' if self.model_chemistry else ''
            lib_long_desc = f'Calculated using Arkane v{__version__}{model_chemistry}.'
            save_thermo_lib(species_list=species,
                            path=lib_path,
                            name='thermo',
                            lib_long_desc=lib_long_desc)
            save_kinetics_lib(rxn_list=reactions,
                              path=lib_path,
                              name='kinetics',
                              lib_long_desc=lib_long_desc)
Example #2
0
File: input.py Project: qize/RMG-Py
def species(label, *args, **kwargs):
    """Load a species from an input file"""
    global species_dict, job_list
    if label in species_dict:
        raise ValueError(
            'Multiple occurrences of species with label {0!r}.'.format(label))
    logging.info('Loading species {0}...'.format(label))

    spec = Species(label=label)
    species_dict[label] = spec

    path = None
    if len(args) == 1:
        # The argument is a path to a conformer input file
        path = args[0]
        job = StatMechJob(species=spec, path=path)
        logging.debug('Added species {0} to a stat mech job.'.format(label))
        job_list.append(job)
    elif len(args) > 1:
        raise InputError('species {0} can only have two non-keyword argument '
                         'which should be the species label and the '
                         'path to a quantum file.'.format(spec.label))

    if len(kwargs) > 0:
        # The species parameters are given explicitly
        structure = None
        E0 = None
        modes = []
        spin_multiplicity = 0
        optical_isomers = 1
        molecular_weight = None
        collision_model = None
        energy_transfer_model = None
        thermo = None
        reactive = True
        for key, value in kwargs.items():
            if key == 'structure':
                structure = value
            elif key == 'E0':
                E0 = value
            elif key == 'modes':
                modes = value
            elif key == 'spinMultiplicity':
                spin_multiplicity = value
            elif key == 'opticalIsomers':
                optical_isomers = value
            elif key == 'molecularWeight':
                molecular_weight = value
            elif key == 'collisionModel':
                collision_model = value
            elif key == 'energyTransferModel':
                energy_transfer_model = value
            elif key == 'thermo':
                thermo = value
            elif key == 'reactive':
                reactive = value
            else:
                raise TypeError(
                    'species() got an unexpected keyword argument {0!r}.'.
                    format(key))

        if structure:
            spec.molecule = [structure]
        spec.conformer = Conformer(E0=E0,
                                   modes=modes,
                                   spin_multiplicity=spin_multiplicity,
                                   optical_isomers=optical_isomers)
        if molecular_weight is not None:
            spec.molecular_weight = molecular_weight
        elif spec.molecular_weight is None and is_pdep(job_list):
            # If a structure was given, simply calling spec.molecular_weight will calculate the molecular weight
            # If one of the jobs is pdep and no molecular weight is given or calculated, raise an error
            raise ValueError(
                "No molecularWeight was entered for species {0}. Since a structure wasn't given"
                " as well, the molecularWeight, which is important for pressure dependent jobs,"
                " cannot be reconstructed.".format(spec.label))
        spec.transport_data = collision_model
        spec.energy_transfer_model = energy_transfer_model
        spec.thermo = thermo
        spec.reactive = reactive

        if spec.reactive and path is None and spec.thermo is None and spec.conformer.E0 is None:
            if not spec.molecule:
                raise InputError(
                    'Neither thermo, E0, species file path, nor structure specified, cannot estimate'
                    ' thermo properties of species {0}'.format(spec.label))
            try:
                db = get_db('thermo')
                if db is None:
                    raise DatabaseError('Thermo database is None.')
            except DatabaseError:
                logging.warning(
                    "The database isn't loaded, cannot estimate thermo for {0}. "
                    "If it is a bath gas, set reactive = False to avoid generating "
                    "thermo.".format(spec.label))
            else:
                logging.info(
                    'No E0 or thermo found, estimating thermo and E0 of species {0} using'
                    ' RMG-Database...'.format(spec.label))
                spec.thermo = db.get_thermo_data(spec)
                if spec.thermo.E0 is None:
                    th = spec.thermo.to_wilhoit()
                    spec.conformer.E0 = th.E0
                    spec.thermo.E0 = th.E0
                else:
                    spec.conformer.E0 = spec.thermo.E0

        if spec.reactive and spec.thermo and not spec.has_statmech(
        ) and structure is not None:
            # generate stat mech info if it wasn't provided before
            spec.generate_statmech()

        if not energy_transfer_model:
            # default to RMG's method of generating energy_transfer_model
            spec.generate_energy_transfer_model()

    return spec
Example #3
0
    def execute(self):
        """
        Execute, in order, the jobs found in input file specified by the
        `inputFile` attribute.
        """

        # Initialize the logging system (both to the console and to a file in the
        # output directory)
        self.initializeLog(self.verbose,
                           os.path.join(self.outputDirectory, 'arkane.log'))

        # Print some information to the beginning of the log
        self.logHeader()

        # Load the input file for the job
        self.jobList = self.loadInputFile(self.inputFile)
        logging.info('')

        # Initialize (and clear!) the output files for the job
        if self.outputDirectory is None:
            self.outputDirectory = os.path.dirname(
                os.path.abspath(self.inputFile))
        outputFile = os.path.join(self.outputDirectory, 'output.py')
        with open(outputFile, 'w') as f:
            pass
        chemkinFile = os.path.join(self.outputDirectory, 'chem.inp')

        # write the chemkin files and run the thermo and then kinetics jobs
        with open(chemkinFile, 'w') as f:
            writeElementsSection(f)

            f.write('SPECIES\n\n')

            # write each species in species block
            for job in self.jobList:
                if isinstance(job, ThermoJob):
                    f.write(job.species.toChemkin())
                    f.write('\n')

            f.write('\nEND\n\n\n\n')
            f.write('THERM ALL\n')
            f.write('    300.000  1000.000  5000.000\n\n')

        # run thermo and statmech jobs (also writes thermo blocks to Chemkin file)
        supporting_info = []
        for job in self.jobList:
            if isinstance(job, ThermoJob):
                job.execute(outputFile=outputFile, plot=self.plot)
            if isinstance(job, StatMechJob):
                job.execute(outputFile=outputFile,
                            plot=self.plot,
                            pdep=is_pdep(self.jobList))
                supporting_info.append(job.supporting_info)

        with open(chemkinFile, 'a') as f:
            f.write('\n')
            f.write('END\n\n\n\n')
            f.write('REACTIONS    KCAL/MOLE   MOLES\n\n')

        supporting_info_file = os.path.join(self.outputDirectory,
                                            'supporting_information.csv')
        with open(supporting_info_file, 'wb') as csvfile:
            writer = csv.writer(csvfile,
                                delimiter=',',
                                quotechar='"',
                                quoting=csv.QUOTE_MINIMAL)
            writer.writerow([
                'Label', 'Rotational constant (cm-1)',
                'Unscaled frequencies (cm-1)'
            ])
            for row in supporting_info:
                label = row[0]
                rot = '-'
                freq = '-'
                if len(
                        row
                ) > 1:  # monoatomic species have no frequencies nor rotational constants
                    if isinstance(row[1].rotationalConstant.value, float):
                        # diatomic species have a single rotational constant
                        rot = '{0:.2f}'.format(row[1].rotationalConstant.value)
                    else:
                        rot = ', '.join([
                            '{0:.2f}'.format(s)
                            for s in row[1].rotationalConstant.value
                        ])
                    freq = ''
                    if len(row) == 4:
                        freq = '{0:.1f}'.format(abs(row[3])) + 'i, '
                    freq += ', '.join(['{0:.1f}'.format(s) for s in row[2]])
                writer.writerow([label, rot, freq])

        # run kinetics and pdep jobs (also writes reaction blocks to Chemkin file)
        for job in self.jobList:
            if isinstance(job, KineticsJob):
                job.execute(outputFile=outputFile, plot=self.plot)
            elif isinstance(job, PressureDependenceJob) and not any(
                [isinstance(job, ExplorerJob) for job in self.jobList]):
                # if there is an explorer job the pdep job will be run in the explorer job
                if job.network is None:
                    raise InputError(
                        'No network matched the label of the pressureDependence block and there is no explorer block '
                        'to generate a network')
                job.execute(outputFile=outputFile, plot=self.plot)
            elif isinstance(job, ExplorerJob):
                thermoLibrary, kineticsLibrary, speciesList = self.getLibraries(
                )
                job.execute(outputFile=outputFile,
                            plot=self.plot,
                            speciesList=speciesList,
                            thermoLibrary=thermoLibrary,
                            kineticsLibrary=kineticsLibrary)

        with open(chemkinFile, 'a') as f:
            f.write('END\n\n')

        # Print some information to the end of the log
        self.logFooter()
    def execute(self):
        """
        Execute, in order, the jobs found in input file specified by the
        `inputFile` attribute.
        """
        
        # Initialize the logging system (both to the console and to a file in the
        # output directory)
        self.initializeLog(self.verbose, os.path.join(self.outputDirectory, 'arkane.log'))
        
        # Print some information to the beginning of the log
        self.logHeader()
        
        # Load the input file for the job
        self.jobList = self.loadInputFile(self.inputFile)
        logging.info('')
        
        # Initialize (and clear!) the output files for the job
        if self.outputDirectory is None:
            self.outputDirectory = os.path.dirname(os.path.abspath(self.inputFile))
        outputFile = os.path.join(self.outputDirectory, 'output.py')
        with open(outputFile, 'w') as f:
            pass
        chemkinFile = os.path.join(self.outputDirectory, 'chem.inp')

        # write the chemkin files and run the thermo and then kinetics jobs
        with open(chemkinFile, 'w') as f:
            writeElementsSection(f)
            
            f.write('SPECIES\n\n')

            # write each species in species block
            for job in self.jobList:
                if isinstance(job,ThermoJob):
                    f.write(job.species.toChemkin())
                    f.write('\n')

            f.write('\nEND\n\n\n\n')
            f.write('THERM ALL\n')
            f.write('    300.000  1000.000  5000.000\n\n')

        # run thermo and statmech jobs (also writes thermo blocks to Chemkin file)
        supporting_info = []
        for job in self.jobList:
            if isinstance(job, ThermoJob):
                job.execute(outputFile=outputFile, plot=self.plot)
            if isinstance(job, StatMechJob):
                job.execute(outputFile=outputFile, plot=self.plot, pdep=is_pdep(self.jobList))
                supporting_info.append(job.supporting_info)

        with open(chemkinFile, 'a') as f:
            f.write('\n')
            f.write('END\n\n\n\n')
            f.write('REACTIONS    KCAL/MOLE   MOLES\n\n')

        supporting_info_file = os.path.join(self.outputDirectory, 'supporting_information.csv')
        with open(supporting_info_file, 'wb') as csvfile:
            writer = csv.writer(csvfile, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
            writer.writerow(['Label','Rotational constant (cm-1)','Unscaled frequencies (cm-1)'])
            for row in supporting_info:
                label = row[0]
                rot = '-'
                freq = '-'
                if len(row) > 1:  # monoatomic species have no frequencies nor rotational constants
                    if isinstance(row[1].rotationalConstant.value, float):
                        # diatomic species have a single rotational constant
                        rot = '{0:.2f}'.format(row[1].rotationalConstant.value)
                    else:
                        rot = ', '.join(['{0:.2f}'.format(s) for s in row[1].rotationalConstant.value])
                    freq = ''
                    if len(row) == 4:
                        freq = '{0:.1f}'.format(abs(row[3])) + 'i, '
                    freq += ', '.join(['{0:.1f}'.format(s) for s in row[2]])
                writer.writerow([label, rot, freq])

        # run kinetics and pdep jobs (also writes reaction blocks to Chemkin file)
        for job in self.jobList:
            if isinstance(job,KineticsJob):
                job.execute(outputFile=outputFile, plot=self.plot)
            elif isinstance(job, PressureDependenceJob) and not any([isinstance(job,ExplorerJob) for job in self.jobList]): #if there is an explorer job the pdep job will be run in the explorer job
                if job.network is None:
                    raise InputError('No network matched the label of the pressureDependence block and there is no explorer block to generate a network')
                job.execute(outputFile=outputFile, plot=self.plot)
            elif isinstance(job, ExplorerJob):
                thermoLibrary,kineticsLibrary,speciesList = self.getLibraries()
                job.execute(outputFile=outputFile, plot=self.plot, speciesList=speciesList, thermoLibrary=thermoLibrary, kineticsLibrary=kineticsLibrary)

        with open(chemkinFile, 'a') as f:
            f.write('END\n\n')

        # Print some information to the end of the log
        self.logFooter()
def species(label, *args, **kwargs):
    global speciesDict, jobList
    if label in speciesDict:
        raise ValueError('Multiple occurrences of species with label {0!r}.'.format(label))
    logging.info('Loading species {0}...'.format(label))
    
    spec = Species(label=label)
    speciesDict[label] = spec

    path = None
    if len(args) == 1:
        # The argument is a path to a conformer input file
        path = args[0]
        job = StatMechJob(species=spec, path=path)
        logging.debug('Added species {0} to a stat mech job.'.format(label))
        jobList.append(job)
    elif len(args) > 1:
        raise InputError('species {0} can only have two non-keyword argument '
                         'which should be the species label and the '
                         'path to a quantum file.'.format(spec.label))
    
    if len(kwargs) > 0:
        # The species parameters are given explicitly
        structure = None
        E0 = None
        modes = []
        spinMultiplicity = 0
        opticalIsomers = 1
        molecularWeight = None
        collisionModel = None
        energyTransferModel = None
        thermo = None
        reactive = True
        for key, value in kwargs.items():
            if key == 'structure':
                structure = value
            elif key == 'E0':
                E0 = value
            elif key == 'modes':
                modes = value
            elif key == 'spinMultiplicity':
                spinMultiplicity = value
            elif key == 'opticalIsomers':
                opticalIsomers = value
            elif key == 'molecularWeight':
                molecularWeight = value
            elif key == 'collisionModel':
                collisionModel = value
            elif key == 'energyTransferModel':
                energyTransferModel = value
            elif key == 'thermo':
                thermo = value
            elif key == 'reactive':
                reactive = value
            else:
                raise TypeError('species() got an unexpected keyword argument {0!r}.'.format(key))
            
        if structure:
            spec.molecule = [structure]
        spec.conformer = Conformer(E0=E0, modes=modes, spinMultiplicity=spinMultiplicity, opticalIsomers=opticalIsomers)
        if molecularWeight is not None:
            spec.molecularWeight = molecularWeight
        elif spec.molecularWeight is None and is_pdep(jobList):
            # If a structure was given, simply calling spec.molecularWeight will calculate the molecular weight
            # If one of the jobs is pdep and no molecular weight is given or calculated, raise an error
            raise ValueError("No molecularWeight was entered for species {0}. Since a structure wasn't given"
                             " as well, the molecularWeight, which is important for pressure dependent jobs,"
                             " cannot be reconstructed.".format(spec.label))
        spec.transportData = collisionModel
        spec.energyTransferModel = energyTransferModel
        spec.thermo = thermo
        spec.reactive = reactive
        
        if spec.reactive and path is None and spec.thermo is None and spec.conformer.E0 is None:
            if not spec.molecule:
                raise InputError('Neither thermo, E0, species file path, nor structure specified, cannot estimate'
                                 ' thermo properties of species {0}'.format(spec.label))
            try:
                db = getDB('thermo')
                if db is None:
                    raise DatabaseError('Thermo database is None.')
            except DatabaseError:
                logging.warn("The database isn't loaded, cannot estimate thermo for {0}. "
                             "If it is a bath gas, set reactive = False to avoid generating thermo.".format(spec.label))
            else:
                logging.info('No E0 or thermo found, estimating thermo and E0 of species {0} using'
                             ' RMG-Database...'.format(spec.label))
                spec.thermo = db.getThermoData(spec)
                if spec.thermo.E0 is None:
                    th = spec.thermo.toWilhoit()
                    spec.conformer.E0 = th.E0
                    spec.thermo.E0 = th.E0
                else:
                    spec.conformer.E0 = spec.thermo.E0

        if spec.reactive and spec.thermo and not spec.hasStatMech() and structure is not None:
            # generate stat mech info if it wasn't provided before
            spec.generateStatMech()

        if not energyTransferModel:
            # default to RMG's method of generating energyTransferModel
            spec.generateEnergyTransferModel()

    return spec