Exemplo n.º 1
0
    def _verify_inputs(self):
        user_qin = QCInput.from_file(os.path.join(os.getcwd(), "mol.qin"))

        # Check mol.qin
        ref_qin = QCInput.from_file(os.path.join(self["ref_dir"], "mol.qin"))
        np.testing.assert_equal(ref_qin.molecule.species,
                                user_qin.molecule.species)
        np.testing.assert_allclose(
            ref_qin.molecule.cart_coords,
            user_qin.molecule.cart_coords,
            atol=0.0001)
        for key in ref_qin.rem:
            if user_qin.rem.get(key) != ref_qin.rem.get(key):
                raise ValueError("Rem key {} is inconsistent!".format(key))
        if ref_qin.opt is not None:
            for key in ref_qin.opt:
                if user_qin.opt.get(key) != ref_qin.opt.get(key):
                    raise ValueError("Opt key {} is inconsistent!".format(key))
        if ref_qin.pcm is not None:
            for key in ref_qin.pcm:
                if user_qin.pcm.get(key) != ref_qin.pcm.get(key):
                    raise ValueError("PCM key {} is inconsistent!".format(key))
        if ref_qin.solvent is not None:
            for key in ref_qin.solvent:
                if user_qin.solvent.get(key) != ref_qin.solvent.get(key):
                    raise ValueError(
                        "Solvent key {} is inconsistent!".format(key))

        logger.info("RunQChemFake: verified input successfully")
Exemplo n.º 2
0
 def test_write_file_from_OptSet(self):
     from pymatgen.io.qchem.sets import OptSet
     odd_dict = loadfn(os.path.join(os.path.dirname(__file__), "odd.json"))
     odd_mol = odd_dict["spec"]["_tasks"][0]["molecule"]
     qcinp = OptSet(odd_mol)
     qcinp.write_file(os.path.join(os.path.dirname(__file__), "test.qin"))
     test_dict = QCInput.from_file(os.path.join(os.path.dirname(__file__), "test.qin")).as_dict()
     test_ref_dict = QCInput.from_file(os.path.join(os.path.dirname(__file__), "test_ref.qin")).as_dict()
     for key in test_dict:
         self.assertEqual(test_dict[key], test_ref_dict[key])
     os.remove(os.path.join(os.path.dirname(__file__), "test.qin"))
Exemplo n.º 3
0
 def test_full_init(self):
     test_molecule = QCInput.from_file(
         os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule
     test_DictSet = QChemDictSet(
         molecule=test_molecule,
         job_type='opt',
         basis_set='6-31g*',
         scf_algorithm='diis',
         dft_rung=1,
         pcm_dielectric=10.0,
         max_scf_cycles=35)
     self.assertEqual(
         test_DictSet.rem, {
             'job_type': 'opt',
             'gen_scfman': 'true',
             'basis': '6-31g*',
             'max_scf_cycles': 35,
             'exchange': 'b3lyp',
             'geom_opt_max_cycles': 200,
             'scf_algorithm': 'diis',
             'solvent_method': 'pcm'
         })
     self.assertEqual(
         test_DictSet.pcm, {
             'heavypoints': '194',
             'hpoints': '194',
             'radii': 'uff',
             'theory': 'cpcm',
             'vdwscale': '1.1'
         })
     self.assertEqual(test_DictSet.solvent, {'dielectric': 10.0})
     self.assertEqual(test_DictSet.molecule, test_molecule)
Exemplo n.º 4
0
    def test_read_opt(self):
        str_opt = """$opt
CONSTRAINT
  tors 2 3 4 5 25.0
   bend 2 1 4 110.0
ENDCONSTRAINT

FIXED
x y 2 4 5
ENDFIXED

DUMMY
   M 2 3 4 5
ENDDUMMY

CONNECT
4 3 2 3 5 6
ENDCONNECT
$end"""
        opt_test = QCInput.read_opt(str_opt)
        opt_actual = {
            "CONSTRAINT": ["tors 2 3 4 5 25.0", "bend 2 1 4 110.0"],
            "FIXED": ["x y 2 4 5"],
            "DUMMY": ["M 2 3 4 5"],
            "CONNECT": ["4 3 2 3 5 6"]
        }
        self.assertDictEqual(opt_actual, opt_test)
Exemplo n.º 5
0
    def test_read_negative(self):
        str_molecule = """$molecule
 -1 1
 S     -1.1516880000      0.8568110000     -0.0787470000
 S      1.1527500000     -0.8580450000     -0.0786430000
 O     -1.6523520000      1.8607750000     -1.0252100000
 O     -0.9052880000      1.2448490000      1.3156410000
 O      0.9072410000     -1.2461780000      1.3158760000
 O      1.6543670000     -1.8616640000     -1.0249090000
 C     -2.5841130000     -0.3746500000      0.0297340000
 C      2.5833220000      0.3755850000      0.0296900000
 F     -3.6480730000      0.2204040000      0.6112110000
 F     -2.2609850000     -1.4531020000      0.7616580000
 F     -2.9656640000     -0.7966010000     -1.1900330000
 F      3.6467050000     -0.2152590000      0.6163310000
 F      2.2560700000      1.4560310000      0.7568190000
 F      2.9672080000      0.7933560000     -1.1908790000
 N     -0.0001900000     -0.0016540000     -0.8250640000
$end

$rem
   job_type = opt
   basis = 6-311++g*
   max_scf_cycles = 200
   gen_scfman = true
   scf_algorithm = diis
   method = wb97xd
   geom_opt_max_cycles = 200
$end
"""
        qcinp = QCInput.from_string(str_molecule)
        self.assertEqual(str_molecule,str(qcinp))
Exemplo n.º 6
0
    def test_read_only_rem(self):
        str_rem = """Trying to break you!

$rem
   job_type  opt
  method  wb97m-v
   basis  def2-qzvppd
   max_scf_cycles  300
  gen_scfman = true
$end

$pcm
heavypoints   194
hpoints   194
radii   uff
theory   cpcm
vdwscale   1.1
$end


$solvent
dielectric   10.0
$end


"""
        rem_test = QCInput.read_rem(str_rem)
        rem_actual = {
            "job_type": "opt",
            "method": "wb97m-v",
            "basis": "def2-qzvppd",
            "max_scf_cycles": "300",
            "gen_scfman": "true"
        }
        self.assertDictEqual(rem_actual, rem_test)
Exemplo n.º 7
0
    def test_solvent_template(self):
        solvent_params = {"dielectric": "5.0"}
        solvent_test = QCInput.solvent_template(solvent_params)
        solvent_actual = """$solvent
   dielectric 5.0
$end"""
        self.assertEqual(solvent_actual, solvent_test)
Exemplo n.º 8
0
    def test_pcm_template(self):
        pcm_params = {"theory": "cpcm"}
        pcm_test = QCInput.pcm_template(pcm_params)
        pcm_actual = """$pcm
   theory cpcm
$end"""
        self.assertEqual(pcm_actual, pcm_test)
Exemplo n.º 9
0
    def test_double_FF_opt(self):
        # location of test files
        test_double_FF_files = os.path.join(module_dir, "..", "..",
                                            "test_files", "double_FF_wf")
        # define starting molecule and workflow object
        initial_qcin = QCInput.from_file(
            os.path.join(test_double_FF_files, "block", "launcher_first",
                         "mol.qin.opt_0"))
        initial_mol = initial_qcin.molecule

        real_wf = get_wf_double_FF_opt(
            molecule=initial_mol,
            pcm_dielectric=10.0,
            qchem_input_params={
                "basis_set": "6-311++g**",
                "scf_algorithm": "diis",
                "overwrite_inputs": {
                    "rem": {
                        "sym_ignore": "true"
                    }
                }
            })
        # use powerup to replace run with fake run
        ref_dirs = {
            "first_FF_no_pcm":
            os.path.join(test_double_FF_files, "block", "launcher_first"),
            "second_FF_with_pcm":
            os.path.join(test_double_FF_files, "block", "launcher_second")
        }
        fake_wf = use_fake_qchem(real_wf, ref_dirs)
        self.lp.add_wf(fake_wf)
        rapidfire(
            self.lp,
            fworker=FWorker(env={"max_cores": 32, "db_file": os.path.join(db_dir, "db.json")}))

        wf_test = self.lp.get_wf_by_fw_id(1)
        self.assertTrue(
            all([s == "COMPLETED" for s in wf_test.fw_states.values()]))

        first_FF = self.get_task_collection().find_one({
            "task_label":
            "first_FF_no_pcm"
        })
        self.assertEqual(first_FF["calcs_reversed"][0]["input"]["solvent"],
                         None)
        self.assertEqual(first_FF["num_frequencies_flattened"], 1)
        first_FF_final_mol = Molecule.from_dict(
            first_FF["output"]["optimized_molecule"])

        second_FF = self.get_task_collection().find_one({
            "task_label":
            "second_FF_with_pcm"
        })
        self.assertEqual(second_FF["calcs_reversed"][0]["input"]["solvent"],
                         {"dielectric": "10.0"})
        self.assertEqual(second_FF["num_frequencies_flattened"], 1)
        second_FF_initial_mol = Molecule.from_dict(
            second_FF["input"]["initial_molecule"])

        self.assertEqual(first_FF_final_mol, second_FF_initial_mol)
Exemplo n.º 10
0
    def test_smx_template(self):
        smx_params = {"solvent": "water"}
        smx_test = QCInput.smx_template(smx_params)
        smx_actual = """$smx
   solvent water
$end"""
        self.assertEqual(smx_actual, smx_test)
Exemplo n.º 11
0
 def test_pcm_init(self):
     test_molecule = QCInput.from_file(
         os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule
     test_SPSet = SinglePointSet(
         molecule=test_molecule, pcm_dielectric=10.0)
     self.assertEqual(
         test_SPSet.rem, {
             'job_type': 'sp',
             'gen_scfman': 'true',
             'basis': '6-311++g*',
             'max_scf_cycles': 200,
             'method': 'wb97xd',
             'scf_algorithm': 'gdm',
             'solvent_method': 'pcm'
         })
     self.assertEqual(
         test_SPSet.pcm, {
             'heavypoints': '194',
             'hpoints': '194',
             'radii': 'uff',
             'theory': 'cpcm',
             'vdwscale': '1.1'
         })
     self.assertEqual(test_SPSet.solvent, {'dielectric': 10.0})
     self.assertEqual(test_SPSet.molecule, test_molecule)
Exemplo n.º 12
0
    def test_read_bad_smx(self):
        str_smx = """Once again, I'm trying to break you!

$solvent
   solvent = water
$end"""
        smx_test = QCInput.read_smx(str_smx)
        smx_actual = {}
        self.assertDictEqual(smx_actual, smx_test)
Exemplo n.º 13
0
    def test_read_bad_solvent(self):
        str_solvent = """Once again, I'm trying to break you!

$solvent
   dielectric = 5.0
$end"""
        solvent_test = QCInput.read_solvent(str_solvent)
        solvent_actual = {}
        self.assertDictEqual(solvent_actual, solvent_test)
Exemplo n.º 14
0
    def test_from_multi_jobs_file(self):
        job_list_test = QCInput.from_multi_jobs_file(
            os.path.join(test_dir, "pt_n2_wb97mv_0.0.in"))
        species = ["S", "C", "H", "C", "H", "C", "H",
                   "C", "C", "C", "H", "C", "H", "C", "H", "S"]
        coords = [[-0.00250959, -0.05817469, -0.02921636],
                  [1.70755408, -0.03033788, -0.01382912],
                  [2.24317221, -0.05215019, 0.92026728],
                  [2.21976393, 0.01718014, -1.27293235],
                  [3.27786220, 0.04082146, -1.48539646],
                  [1.20867399, 0.04478540, -2.27007793],
                  [1.40292257, 0.10591684, -3.33110912],
                  [-0.05341046, 0.01577217, -1.74839343],
                  [-1.32843436, 0.03545064, -2.45531187],
                  [-1.55195156, 0.08743920, -3.80184635],
                  [-0.75245172, 0.10267657, -4.52817967],
                  [-2.93293778, 0.08408786, -4.13352169],
                  [-3.31125108, 0.11340328, -5.14405819],
                  [-3.73173288, 0.02741365, -3.03412864],
                  [-4.80776535, 0.00535688, -2.99564645],
                  [-2.81590978, -0.00516172, -1.58990580]]
        molecule_1_actual = Molecule(species, coords)
        rem_1_actual = {
            "job_type": "opt",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": "75",
            "max_scf_cycles": "300",
            "scf_algorithm": "diis",
            "scf_guess": "sad",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": "14"
        }
        opt_1_actual = {"CONSTRAINT": ["tors 6 8 9 10 0.0"]}
        self.assertEqual(molecule_1_actual, job_list_test[0].molecule)
        self.assertEqual(rem_1_actual, job_list_test[0].rem)
        self.assertEqual(opt_1_actual, job_list_test[0].opt)

        molecule_2_actual = "read"
        rem_2_actual = {
            "job_type": "sp",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": "75",
            "max_scf_cycles": "300",
            "scf_algorithm": "diis",
            "scf_guess": "read",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": "14"
        }
        self.assertEqual(molecule_2_actual, job_list_test[1].molecule)
        self.assertEqual(rem_2_actual, job_list_test[1].rem)
Exemplo n.º 15
0
 def test_OptFF(self):
     myjob = QCJob.opt_with_frequency_flattener(qchem_command="qchem", max_cores=32, input_file="test.qin", output_file="test.qout")
     expected_next = QCJob(
                     qchem_command="qchem",
                     max_cores=32,
                     multimode="openmp",
                     input_file="test.qin",
                     output_file="test.qout",
                     suffix=".opt_0",
                     backup=True).as_dict()
     self.assertEqual(next(myjob).as_dict(),expected_next)
     expected_next = QCJob(
                     qchem_command="qchem",
                     max_cores=32,
                     multimode="openmp",
                     input_file="test.qin",
                     output_file="test.qout",
                     suffix=".freq_0",
                     backup=False).as_dict()
     self.assertEqual(next(myjob).as_dict(),expected_next)
     self.assertEqual(QCInput.from_file(os.path.join(test_dir,"FF_working/test.qin.freq_0")).as_dict(),QCInput.from_file(os.path.join(scr_dir,"test.qin")).as_dict())
     expected_next = QCJob(
                     qchem_command="qchem",
                     max_cores=32,
                     multimode="openmp",
                     input_file="test.qin",
                     output_file="test.qout",
                     suffix=".opt_1",
                     backup=False).as_dict()
     self.assertEqual(next(myjob).as_dict(),expected_next)
     self.assertEqual(QCInput.from_file(os.path.join(test_dir,"FF_working/test.qin.opt_1")).as_dict(),QCInput.from_file(os.path.join(scr_dir,"test.qin")).as_dict())
     expected_next = QCJob(
                     qchem_command="qchem",
                     max_cores=32,
                     multimode="openmp",
                     input_file="test.qin",
                     output_file="test.qout",
                     suffix=".freq_1",
                     backup=False).as_dict()
     self.assertEqual(next(myjob).as_dict(),expected_next)
     self.assertEqual(QCInput.from_file(os.path.join(test_dir,"FF_working/test.qin.freq_1")).as_dict(),QCInput.from_file(os.path.join(scr_dir,"test.qin")).as_dict())
     self.assertRaises(StopIteration,myjob.__next__)
Exemplo n.º 16
0
    def test_read_bad_pcm(self):
        str_pcm = """I'm once again trying to break you!

$pcm
   theory = cpcm
   radii = uff
   vdwscale = 1.1
$end"""
        pcm_test = QCInput.read_pcm(str_pcm)
        pcm_actual = {}
        self.assertDictEqual(pcm_actual, pcm_test)
Exemplo n.º 17
0
    def test_read_molecule(self):
        str_molecule = """$molecule
 0 1
 C     -9.5782000000      0.6241500000      0.0000000000
 O     -7.5827400000      0.5127000000     -0.0000000000
$end"""
        molecule_test = QCInput.read_molecule(str_molecule)
        species = ["C", "O"]
        coords = [[-9.5782000000, 0.6241500000, 0.0000000000],
                  [-7.5827400000, 0.5127000000, -0.0000000000]]
        molecule_actual = Molecule(species, coords)
        self.assertEqual(molecule_actual, molecule_test)
Exemplo n.º 18
0
    def test_molecule_template(self):
        species = ["C", "O"]
        coords = [[-9.5782000000, 0.6241500000, 0.0000000000],
                  [-7.5827400000, 0.5127000000, -0.0000000000]]
        mol = Molecule(species=species, coords=coords)
        molecule_test = QCInput.molecule_template(mol)
        molecule_actual = """$molecule
 0 1
 C     -9.5782000000      0.6241500000      0.0000000000
 O     -7.5827400000      0.5127000000     -0.0000000000
$end"""

        self.assertEqual(molecule_actual, molecule_test)
Exemplo n.º 19
0
    def test_rem_template(self):
        rem_params = {
            "job_type": "opt",
            "method": "wb97m-v",
            "basis": "def2-qzvppd",
            "max_scf_cycles": 300,
            "gen_scfman": "true"
        }
        rem_test = QCInput.rem_template(rem_params).split("\n")
        rem_actual_list = ["$rem", "   job_type = opt", "   method = wb97m-v", "   basis = def2-qzvppd",
                           "   max_scf_cycles = 300", "   gen_scfman = true", "$end"]

        for i_rem in rem_actual_list:
            self.assertIn(i_rem, rem_test)
Exemplo n.º 20
0
    def test_read_pcm(self):
        str_pcm = """I'm once again trying to break you!

$pcm
   theory cpcm
   radii uff
   vdwscale 1.1
$end"""
        pcm_test = QCInput.read_pcm(str_pcm)
        pcm_actual = {
            "theory": "cpcm",
            "radii": "uff",
            "vdwscale": "1.1"
        }
        self.assertDictEqual(pcm_actual, pcm_test)
Exemplo n.º 21
0
    def test_opt_template(self):
        opt_params = {
            "CONSTRAINT": ["tors 2 3 4 5 25.0", "bend 2 1 4 110.0"],
            "FIXED": ["x y 2 4 5"],
            "DUMMY": ["M 2 3 4 5"],
            "CONNECT": ["4 3 2 3 5 6"]
        }
        opt_test = QCInput.opt_template(opt_params).split("\n")
        opt_actual_list = ["$opt",
                           "CONSTRAINT", "   tors 2 3 4 5 25.0", "   bend 2 1 4 110.0", "ENDCONSTRAINT",
                           "FIXED", "   x y 2 4 5", "ENDFIXED",
                           "DUMMY", "   M 2 3 4 5", "ENDDUMMY",
                           "CONNECT", "   4 3 2 3 5 6", "ENDCONNECT", "$end"]

        for i_opt in opt_actual_list:
            self.assertIn(i_opt, opt_test)
Exemplo n.º 22
0
 def test_smd_init(self):
     test_molecule = QCInput.from_file(
         os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule
     test_SPSet = SinglePointSet(molecule=test_molecule, smd_solvent='water')
     self.assertEqual(
         test_SPSet.rem, {
             'job_type': 'sp',
             'gen_scfman': 'true',
             'basis': '6-311++g*',
             'max_scf_cycles': 200,
             'method': 'wb97xd',
             'scf_algorithm': 'gdm',
             'solvent_method': 'smd'
         })
     self.assertEqual(test_SPSet.smx, {'solvent': 'water'})
     self.assertEqual(test_SPSet.molecule, test_molecule)
Exemplo n.º 23
0
 def test_init(self):
     test_molecule = QCInput.from_file(
         os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule
     test_FreqSet = FreqSet(molecule=test_molecule)
     self.assertEqual(
         test_FreqSet.rem, {
             'job_type': 'freq',
             'gen_scfman': 'true',
             'basis': '6-311++g*',
             'max_scf_cycles': 200,
             'method': 'wb97xd',
             'scf_algorithm': 'gdm'
         })
     self.assertEqual(test_FreqSet.pcm, {})
     self.assertEqual(test_FreqSet.solvent, {})
     self.assertEqual(test_FreqSet.molecule, test_molecule)
Exemplo n.º 24
0
    def test_find_sections(self):
        str_single_job_input = """$molecule
 0  1
 S          -0.00250959       -0.05817469       -0.02921636
 C           1.70755408       -0.03033788       -0.01382912
 H           2.24317221       -0.05215019        0.92026728
 C           2.21976393        0.01718014       -1.27293235
 H           3.27786220        0.04082146       -1.48539646
 C           1.20867399        0.04478540       -2.27007793
 H           1.40292257        0.10591684       -3.33110912
 C          -0.05341046        0.01577217       -1.74839343
 C          -1.32843436        0.03545064       -2.45531187
 C          -1.55195156        0.08743920       -3.80184635
 H          -0.75245172        0.10267657       -4.52817967
 C          -2.93293778        0.08408786       -4.13352169
 H          -3.31125108        0.11340328       -5.14405819
 C          -3.73173288        0.02741365       -3.03412864
 H          -4.80776535        0.00535688       -2.99564645
 S          -2.81590978       -0.00516172       -1.58990580
$end


$rem
             job_type = opt
               method = wb97m-v
                basis = def2-tzvppd
           gen_scfman = true
  geom_opt_max_cycles = 75
       max_scf_cycles = 300
        scf_algorithm = diis
            scf_guess = sad
           sym_ignore = true
             symmetry = false
               thresh = 14
$end


$opt
CONSTRAINT
tors 6 8 9 10 0.0
ENDCONSTRAINT
$end
"""
        sections_test = QCInput.find_sections(str_single_job_input)
        section_actual = ["molecule", "rem", "opt"]
        self.assertEqual(section_actual, sections_test)
Exemplo n.º 25
0
 def test_overwrite_input_addition(self):
     test_molecule = QCInput.from_file(
         os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule
     overwrite_inputs = {"rem": {'thresh': 14}}
     test_OptSet = OptSet(
         molecule=test_molecule, overwrite_inputs=overwrite_inputs)
     act_rem = {
         'job_type': 'opt',
         'gen_scfman': 'true',
         'basis': '6-311++g*',
         'max_scf_cycles': 200,
         'method': 'wb97xd',
         'scf_algorithm': 'gdm',
         'geom_opt_max_cycles': 200,
         'thresh': 14
     }
     self.assertDictEqual(act_rem, test_OptSet.rem)
Exemplo n.º 26
0
    def test_read_rem(self):
        str_rem = """Trying to break you!

$rem
   job_type  opt
  method  wb97m-v
   basis  def2-qzvppd
   max_scf_cycles  300
  gen_scfman = true
$end"""
        rem_test = QCInput.read_rem(str_rem)
        rem_actual = {
            "job_type": "opt",
            "method": "wb97m-v",
            "basis": "def2-qzvppd",
            "max_scf_cycles": "300",
            "gen_scfman": "true"
        }
        self.assertDictEqual(rem_actual, rem_test)
Exemplo n.º 27
0
    def test_double_solvation(self):
        test_molecule = QCInput.from_file(
            os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule
        raised_error = False
        dict_set = None
        try:
            dict_set = QChemDictSet(molecule=test_molecule,
                                    job_type='opt',
                                    basis_set='6-31g*',
                                    scf_algorithm='diis',
                                    dft_rung=1,
                                    pcm_dielectric=10.0,
                                    smd_solvent="water",
                                    max_scf_cycles=35)
        except ValueError:
            raised_error = True

        self.assertTrue(raised_error)
        self.assertEqual(dict_set, None)
Exemplo n.º 28
0
 def test_init(self):
     test_molecule = QCInput.from_file(
         os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule
     test_DictSet = QChemDictSet(
         molecule=test_molecule,
         job_type='opt',
         basis_set='6-31G*',
         scf_algorithm='diis')
     self.assertEqual(
         test_DictSet.rem, {
             'job_type': 'opt',
             'gen_scfman': 'true',
             'basis': '6-31g*',
             'max_scf_cycles': 200,
             'method': 'wb97xd',
             'scf_algorithm': 'diis',
             'geom_opt_max_cycles': 200
         })
     self.assertEqual(test_DictSet.pcm, {})
     self.assertEqual(test_DictSet.solvent, {})
     self.assertEqual(test_DictSet.molecule, test_molecule)
Exemplo n.º 29
0
    def __init__(self,
                 input_file="mol.qin",
                 output_file="mol.qout",
                 rca_gdm_thresh=1.0E-3,
                 scf_max_cycles=200):
        """
        Initializes the error handler from a set of input and output files.

        Args:
            input_file (str): Name of the QChem input file.
            output_file (str): Name of the QChem output file.
            rca_gdm_thresh (float): The threshold for the prior scf algorithm.
                If last deltaE is larger than the threshold try RCA_DIIS
                first, else, try DIIS_GDM first.
            scf_max_cycles (int): The max iterations to set to fix SCF failure.
        """
        self.input_file = input_file
        self.output_file = output_file
        self.scf_max_cycles = scf_max_cycles
        self.geom_max_cycles = geom_max_cycles
        self.qcinp = QCInput.from_file(self.input_file)
        self.outdata = None
        self.errors = None
        self.qchem_job = qchem_job
Exemplo n.º 30
0
    def test_Fragmentation(self):
        with patch("atomate.qchem.firetasks.fragmenter.FWAction") as FWAction_patch:
            mock_FWAction = MagicMock()
            FWAction_patch.return_value = mock_FWAction
            mock_FWAction.as_dict.return_value = {'stored_data': {}, 'exit': False, 'update_spec': {}, 'mod_spec': [], 'additions': [], 'detours': [], 'defuse_children': False, 'defuse_workflow': False}

            # location of test files
            test_FF_then_fragment_files = os.path.join(module_dir, "..", "..",
                                                "test_files", "FF_then_fragment_wf")
            # define starting molecule and workflow object
            initial_qcin = QCInput.from_file(
                os.path.join(test_FF_then_fragment_files, "block", "launcher_first",
                             "mol.qin.opt_0"))
            initial_mol = initial_qcin.molecule
            real_wf = get_fragmentation_wf(molecule=initial_mol, depth=0, do_triplets=False)
            # use powerup to replace run with fake run
            ref_dirs = {
                "first FF":
                os.path.join(test_FF_then_fragment_files, "block", "launcher_first"),
                "fragment and FF_opt":
                os.path.join(test_FF_then_fragment_files, "block", "launcher_second")
            }
            fake_wf = use_fake_qchem(real_wf, ref_dirs)
            self.lp.add_wf(fake_wf)
            rapidfire(
                self.lp,
                fworker=FWorker(env={"max_cores": 32, "db_file": os.path.join(db_dir, "db.json")}), pdb_on_exception=True)

            first_FF = self.get_task_collection().find_one({
                "task_label":
                "first FF"
            })
            self.assertEqual(first_FF["calcs_reversed"][0]["input"]["solvent"],
                             None)
            self.assertEqual(first_FF["num_frequencies_flattened"], 0)
            self.assertEqual(len(FWAction_patch.call_args[1]["additions"]), 5 * 3)
Exemplo n.º 31
0
    def correct(self):
        """
        Perform corrections
        """
        backup({self.input_file, self.output_file})
        actions = []
        self.qcinp = QCInput.from_file(self.input_file)

        if "SCF_failed_to_converge" in self.errors:
            # Check number of SCF cycles. If not set or less than scf_max_cycles,
            # increase to that value and rerun. If already set, check if
            # scf_algorithm is unset or set to DIIS, in which case set to GDM.
            # Otherwise, tell user to call SCF error handler and do nothing.
            if str(self.qcinp.rem.get("max_scf_cycles")) != str(
                    self.scf_max_cycles):
                self.qcinp.rem["max_scf_cycles"] = self.scf_max_cycles
                actions.append({"max_scf_cycles": self.scf_max_cycles})
            elif self.qcinp.rem.get("thresh", "10") != "14":
                self.qcinp.rem["thresh"] = "14"
                actions.append({"thresh": "14"})
            elif self.qcinp.rem.get("scf_algorithm", "diis").lower() == "diis":
                self.qcinp.rem["scf_algorithm"] = "diis_gdm"
                actions.append({"scf_algorithm": "diis_gdm"})
            elif self.qcinp.rem.get("scf_algorithm",
                                    "diis").lower() == "diis_gdm":
                self.qcinp.rem["scf_algorithm"] = "gdm"
                actions.append({"scf_algorithm": "gdm"})
            elif self.qcinp.rem.get("scf_guess_always",
                                    "none").lower() != "true":
                self.qcinp.rem["scf_guess_always"] = True
                actions.append({"scf_guess_always": True})
            else:
                print(
                    "More advanced changes may impact the SCF result. Use the SCF error handler"
                )

        elif "out_of_opt_cycles" in self.errors:
            # Check number of opt cycles. If less than geom_max_cycles, increase
            # to that value, set last geom as new starting geom and rerun.
            if str(self.qcinp.rem.get("geom_opt_max_cycles")) != str(
                    self.geom_max_cycles):
                self.qcinp.rem["geom_opt_max_cycles"] = self.geom_max_cycles
                actions.append({"geom_max_cycles:": self.scf_max_cycles})
                if len(self.outdata.get("energy_trajectory")) > 1:
                    self.qcinp.molecule = self.outdata.get(
                        "molecule_from_last_geometry")
                    actions.append({"molecule": "molecule_from_last_geometry"})
            elif self.qcinp.rem.get("thresh", "10") != "14":
                self.qcinp.rem["thresh"] = "14"
                actions.append({"thresh": "14"})
            # Will need to try and implement this dmax handler below when I have more time
            # to fix the tests and the general handling procedure.
            # elif self.qcinp.rem.get("geom_opt_dmax",300) != 150:
            #     self.qcinp.rem["geom_opt_dmax"] = 150
            #     actions.append({"geom_opt_dmax": "150"})
            # If already at geom_max_cycles, thresh 14, and dmax 150, often can just get convergence
            # by restarting from the geometry of the last cycle. But we'll also save any structural
            # changes that happened along the way.
            else:
                self.opt_error_history += [self.outdata["structure_change"]]
                if len(self.opt_error_history) > 1:
                    if self.opt_error_history[-1] == "no_change":
                        # If no structural changes occured in two consecutive optimizations,
                        # and we still haven't converged, then just exit.
                        return {
                            "errors": self.errors,
                            "actions": None,
                            "opt_error_history": self.opt_error_history,
                        }
                self.qcinp.molecule = self.outdata.get(
                    "molecule_from_last_geometry")
                actions.append({"molecule": "molecule_from_last_geometry"})

        elif "unable_to_determine_lamda" in self.errors:
            # Set last geom as new starting geom and rerun. If no opt cycles,
            # use diff SCF strat? Diff initial guess? Change basis? Unclear.
            if len(self.outdata.get("energy_trajectory")) > 1:
                self.qcinp.molecule = self.outdata.get(
                    "molecule_from_last_geometry")
                actions.append({"molecule": "molecule_from_last_geometry"})
            elif self.qcinp.rem.get("thresh", "10") != "14":
                self.qcinp.rem["thresh"] = "14"
                actions.append({"thresh": "14"})
            else:
                print(
                    "Use a different initial guess? Perhaps a different basis?"
                )

        elif "premature_end_FileMan_error" in self.errors:
            if self.qcinp.rem.get("thresh", "10") != "14":
                self.qcinp.rem["thresh"] = "14"
                actions.append({"thresh": "14"})
            elif self.qcinp.rem.get("scf_guess_always",
                                    "none").lower() != "true":
                self.qcinp.rem["scf_guess_always"] = True
                actions.append({"scf_guess_always": True})
            else:
                print(
                    "We're in a bad spot if we get a FileMan error while always generating a new SCF guess..."
                )

        elif "hessian_eigenvalue_error" in self.errors:
            if self.qcinp.rem.get("thresh", "10") != "14":
                self.qcinp.rem["thresh"] = "14"
                actions.append({"thresh": "14"})
            else:
                print(
                    "Not sure how to fix hessian_eigenvalue_error if thresh is already 14!"
                )

        elif "NLebdevPts" in self.errors:
            # this error should only be possible if resp_charges or esp_charges is set
            if self.qcinp.rem.get("resp_charges") or self.qcinp.rem.get(
                    "esp_charges"):
                # This error is caused by insufficient no. of Lebedev points on
                # the grid used to compute RESP charges
                # Increase the density of points on the Lebedev grid using the
                # esp_surface_density argument (see manual >= v5.4)
                # the default value is 500 (=0.001 Angstrom)
                # or disable RESP charges as a last resort
                if int(self.qcinp.rem.get("esp_surface_density", 500)) >= 500:
                    self.qcinp.rem["esp_surface_density"] = "250"
                    actions.append({"esp_surface_density": "250"})
                elif int(self.qcinp.rem.get("esp_surface_density",
                                            250)) >= 250:
                    self.qcinp.rem["esp_surface_density"] = "125"
                    actions.append({"esp_surface_density": "125"})
                elif int(self.qcinp.rem.get("esp_surface_density",
                                            125)) >= 125:
                    # switch from Lebedev mode to spherical harmonics mode
                    if self.qcinp.rem.get("resp_charges"):
                        self.qcinp.rem["resp_charges"] = "2"
                        actions.append({"resp_charges": "2"})
                    if self.qcinp.rem.get("esp_charges"):
                        self.qcinp.rem["esp_charges"] = "2"
                        actions.append({"esp_charges": "2"})
                else:
                    if self.qcinp.rem.get("resp_charges"):
                        self.qcinp.rem["resp_charges"] = "false"
                        actions.append({"resp_charges": "false"})
                    if self.qcinp.rem.get("esp_charges"):
                        self.qcinp.rem["esp_charges"] = "false"
                        actions.append({"esp_charges": "false"})
            else:
                print(
                    "Not sure how to fix NLebdevPts error if resp_charges is disabled!"
                )

        elif "failed_to_transform_coords" in self.errors:
            # Check for symmetry flag in rem. If not False, set to False and rerun.
            # If already False, increase threshold?
            if not self.qcinp.rem.get("sym_ignore") or self.qcinp.rem.get(
                    "symmetry"):
                self.qcinp.rem["sym_ignore"] = True
                self.qcinp.rem["symmetry"] = False
                actions.append({"sym_ignore": True})
                actions.append({"symmetry": False})
            else:
                print("Perhaps increase the threshold?")

        elif "basis_not_supported" in self.errors:
            print(
                "Specify a different basis set. At least one of the atoms is not supported."
            )
            return {"errors": self.errors, "actions": None}

        elif "input_file_error" in self.errors:
            print(
                "Something is wrong with the input file. Examine error message by hand."
            )
            return {"errors": self.errors, "actions": None}

        elif "failed_to_read_input" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun_job_no_changes": True})

        elif "read_molecule_error" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun_job_no_changes": True})

        elif "never_called_qchem" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun_job_no_changes": True})

        elif "licensing_error" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun_job_no_changes": True})

        elif "unknown_error" in self.errors:
            if self.qcinp.rem.get("scf_guess", "none").lower() == "read":
                del self.qcinp.rem["scf_guess"]
                actions.append({"scf_guess": "deleted"})
            elif self.qcinp.rem.get("thresh", "10") != "14":
                self.qcinp.rem["thresh"] = "14"
                actions.append({"thresh": "14"})
            else:
                print("Unknown error. Examine output and log files by hand.")
                return {"errors": self.errors, "actions": None}

        else:
            # You should never get here. If correct is being called then errors should have at least one entry,
            # in which case it should have been caught by the if/elifs above.
            print("Errors:", self.errors)
            print(
                "Must have gotten an error which is correctly parsed but not included in the handler. FIX!!!"
            )
            return {"errors": self.errors, "actions": None}

        if {
                "molecule": "molecule_from_last_geometry"
        } in actions and str(
                self.qcinp.rem.get("geom_opt_hessian")).lower() == "read":
            del self.qcinp.rem["geom_opt_hessian"]
            actions.append({"geom_opt_hessian": "deleted"})
        os.rename(self.input_file, self.input_file + ".last")
        self.qcinp.write_file(self.input_file)
        return {
            "errors": self.errors,
            "warnings": self.warnings,
            "actions": actions
        }
Exemplo n.º 32
0
    def opt_with_frequency_flattener(
        cls,
        qchem_command,
        multimode="openmp",
        input_file="mol.qin",
        output_file="mol.qout",
        qclog_file="mol.qclog",
        max_iterations=10,
        max_molecule_perturb_scale=0.3,
        check_connectivity=True,
        linked=True,
        save_final_scratch=False,
        **QCJob_kwargs
    ):
        """
        Optimize a structure and calculate vibrational frequencies to check if the
        structure is in a true minima. If a frequency is negative, iteratively
        perturbe the geometry, optimize, and recalculate frequencies until all are
        positive, aka a true minima has been found.

        Args:
            qchem_command (str): Command to run QChem.
            multimode (str): Parallelization scheme, either openmp or mpi.
            input_file (str): Name of the QChem input file.
            output_file (str): Name of the QChem output file.
            max_iterations (int): Number of perturbation -> optimization -> frequency
                iterations to perform. Defaults to 10.
            max_molecule_perturb_scale (float): The maximum scaled perturbation that
                can be applied to the molecule. Defaults to 0.3.
            check_connectivity (bool): Whether to check differences in connectivity
                introduced by structural perturbation. Defaults to True.
            linked (bool): Whether or not to use the linked flattener. Defaults to True.
            save_final_scratch (bool): Whether to save full scratch directory contents
                at the end of the flattening. Defaults to False.
            **QCJob_kwargs: Passthrough kwargs to QCJob. See
                :class:`custodian.qchem.jobs.QCJob`.
        """
        if not os.path.exists(input_file):
            raise AssertionError("Input file must be present!")

        if linked:

            energy_diff_cutoff = 0.0000001

            orig_input = QCInput.from_file(input_file)
            freq_rem = copy.deepcopy(orig_input.rem)
            freq_rem["job_type"] = "freq"
            opt_rem = copy.deepcopy(orig_input.rem)
            opt_rem["geom_opt_hessian"] = "read"
            opt_rem["scf_guess_always"] = True
            first = True
            energy_history = []

            for ii in range(max_iterations):
                yield (
                    QCJob(
                        qchem_command=qchem_command,
                        multimode=multimode,
                        input_file=input_file,
                        output_file=output_file,
                        qclog_file=qclog_file,
                        suffix=".opt_" + str(ii),
                        save_scratch=True,
                        backup=first,
                        **QCJob_kwargs
                    )
                )
                opt_outdata = QCOutput(output_file + ".opt_" + str(ii)).data
                opt_indata = QCInput.from_file(input_file + ".opt_" + str(ii))
                if opt_indata.rem["scf_algorithm"] != freq_rem["scf_algorithm"]:
                    freq_rem["scf_algorithm"] = opt_indata.rem["scf_algorithm"]
                    opt_rem["scf_algorithm"] = opt_indata.rem["scf_algorithm"]
                first = False
                if (
                    opt_outdata["structure_change"] == "unconnected_fragments"
                    and not opt_outdata["completion"]
                ):
                    print(
                        "Unstable molecule broke into unconnected fragments which failed to optimize! Exiting..."
                    )
                    break
                energy_history.append(opt_outdata.get("final_energy"))
                freq_QCInput = QCInput(
                    molecule=opt_outdata.get("molecule_from_optimized_geometry"),
                    rem=freq_rem,
                    opt=orig_input.opt,
                    pcm=orig_input.pcm,
                    solvent=orig_input.solvent,
                    smx=orig_input.smx,
                )
                freq_QCInput.write_file(input_file)
                yield (
                    QCJob(
                        qchem_command=qchem_command,
                        multimode=multimode,
                        input_file=input_file,
                        output_file=output_file,
                        qclog_file=qclog_file,
                        suffix=".freq_" + str(ii),
                        save_scratch=True,
                        backup=first,
                        **QCJob_kwargs
                    )
                )
                outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                indata = QCInput.from_file(input_file + ".freq_" + str(ii))
                if indata.rem["scf_algorithm"] != freq_rem["scf_algorithm"]:
                    freq_rem["scf_algorithm"] = indata.rem["scf_algorithm"]
                    opt_rem["scf_algorithm"] = indata.rem["scf_algorithm"]
                errors = outdata.get("errors")
                if len(errors) != 0:
                    raise AssertionError(
                        "No errors should be encountered while flattening frequencies!"
                    )
                if outdata.get("frequencies")[0] > 0.0:
                    print("All frequencies positive!")
                    break
                if (
                    abs(outdata.get("frequencies")[0]) < 15.0
                    and outdata.get("frequencies")[1] > 0.0
                ):
                    print(
                        "One negative frequency smaller than 15.0 - not worth further flattening!"
                    )
                    break
                if len(energy_history) > 1:
                    if (
                        abs(energy_history[-1] - energy_history[-2])
                        < energy_diff_cutoff
                    ):
                        print("Energy change below cutoff!")
                        break
                opt_QCInput = QCInput(
                    molecule=opt_outdata.get(
                        "molecule_from_optimized_geometry"
                    ),
                    rem=opt_rem,
                    opt=orig_input.opt,
                    pcm=orig_input.pcm,
                    solvent=orig_input.solvent,
                    smx=orig_input.smx,
                )
                opt_QCInput.write_file(input_file)
            if not save_final_scratch:
                shutil.rmtree(os.path.join(os.getcwd(), "scratch"))

        else:
            if not os.path.exists(input_file):
                raise AssertionError("Input file must be present!")
            orig_opt_input = QCInput.from_file(input_file)
            orig_opt_rem = copy.deepcopy(orig_opt_input.rem)
            orig_freq_rem = copy.deepcopy(orig_opt_input.rem)
            orig_freq_rem["job_type"] = "freq"
            first = True
            history = []

            for ii in range(max_iterations):
                yield (
                    QCJob(
                        qchem_command=qchem_command,
                        multimode=multimode,
                        input_file=input_file,
                        output_file=output_file,
                        qclog_file=qclog_file,
                        suffix=".opt_" + str(ii),
                        backup=first,
                        **QCJob_kwargs
                    )
                )
                opt_outdata = QCOutput(output_file + ".opt_" + str(ii)).data
                if first:
                    orig_species = copy.deepcopy(opt_outdata.get("species"))
                    orig_charge = copy.deepcopy(opt_outdata.get("charge"))
                    orig_multiplicity = copy.deepcopy(opt_outdata.get("multiplicity"))
                    orig_energy = copy.deepcopy(opt_outdata.get("final_energy"))
                first = False
                if (
                    opt_outdata["structure_change"] == "unconnected_fragments"
                    and not opt_outdata["completion"]
                ):
                    print(
                        "Unstable molecule broke into unconnected fragments which failed to optimize! Exiting..."
                    )
                    break
                freq_QCInput = QCInput(
                    molecule=opt_outdata.get("molecule_from_optimized_geometry"),
                    rem=orig_freq_rem,
                    opt=orig_opt_input.opt,
                    pcm=orig_opt_input.pcm,
                    solvent=orig_opt_input.solvent,
                    smx=orig_opt_input.smx,
                )
                freq_QCInput.write_file(input_file)
                yield (
                    QCJob(
                        qchem_command=qchem_command,
                        multimode=multimode,
                        input_file=input_file,
                        output_file=output_file,
                        qclog_file=qclog_file,
                        suffix=".freq_" + str(ii),
                        backup=first,
                        **QCJob_kwargs
                    )
                )
                outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                errors = outdata.get("errors")
                if len(errors) != 0:
                    raise AssertionError(
                        "No errors should be encountered while flattening frequencies!"
                    )
                if outdata.get("frequencies")[0] > 0.0:
                    print("All frequencies positive!")
                    if opt_outdata.get("final_energy") > orig_energy:
                        print(
                            "WARNING: Energy increased during frequency flattening!"
                        )
                    break
                hist = {}
                hist["molecule"] = copy.deepcopy(
                    outdata.get("initial_molecule")
                )
                hist["geometry"] = copy.deepcopy(
                    outdata.get("initial_geometry")
                )
                hist["frequencies"] = copy.deepcopy(outdata.get("frequencies"))
                hist["frequency_mode_vectors"] = copy.deepcopy(
                    outdata.get("frequency_mode_vectors")
                )
                hist["num_neg_freqs"] = sum(
                    1 for freq in outdata.get("frequencies") if freq < 0
                )
                hist["energy"] = copy.deepcopy(opt_outdata.get("final_energy"))
                hist["index"] = len(history)
                hist["children"] = []
                history.append(hist)

                ref_mol = history[-1]["molecule"]
                geom_to_perturb = history[-1]["geometry"]
                negative_freq_vecs = history[-1]["frequency_mode_vectors"][0]
                reversed_direction = False
                standard = True

                # If we've found one or more negative frequencies in two consecutive iterations, let's dig in
                # deeper:
                if len(history) > 1:
                    # Start by finding the latest iteration's parent:
                    if history[-1]["index"] in history[-2]["children"]:
                        parent_hist = history[-2]
                        history[-1]["parent"] = parent_hist["index"]
                    elif history[-1]["index"] in history[-3]["children"]:
                        parent_hist = history[-3]
                        history[-1]["parent"] = parent_hist["index"]
                    else:
                        raise AssertionError(
                            "ERROR: your parent should always be one or two iterations behind you! Exiting..."
                        )

                    # if the number of negative frequencies has remained constant or increased from parent to
                    # child,
                    if (
                        history[-1]["num_neg_freqs"]
                        >= parent_hist["num_neg_freqs"]
                    ):
                        # check to see if the parent only has one child, aka only the positive perturbation has
                        # been tried,
                        # in which case just try the negative perturbation from the same parent
                        if len(parent_hist["children"]) == 1:
                            ref_mol = parent_hist["molecule"]
                            geom_to_perturb = parent_hist["geometry"]
                            negative_freq_vecs = parent_hist[
                                "frequency_mode_vectors"
                            ][0]
                            reversed_direction = True
                            standard = False
                            parent_hist["children"].append(len(history))
                        # If the parent has two children, aka both directions have been tried, then we have to
                        # get creative:
                        elif len(parent_hist["children"]) == 2:
                            # If we're dealing with just one negative frequency,
                            if parent_hist["num_neg_freqs"] == 1:
                                make_good_child_next_parent = False
                                if (
                                    history[parent_hist["children"][0]][
                                        "energy"
                                    ]
                                    < history[-1]["energy"]
                                ):
                                    good_child = copy.deepcopy(
                                        history[parent_hist["children"][0]]
                                    )
                                else:
                                    good_child = copy.deepcopy(history[-1])
                                if good_child["num_neg_freqs"] > 1:
                                    raise Exception(
                                        "ERROR: Child with lower energy has more negative frequencies! "
                                        "Exiting..."
                                    )
                                if (
                                    good_child["energy"] < parent_hist["energy"]
                                ):
                                    make_good_child_next_parent = True
                                elif (
                                    vector_list_diff(
                                        good_child["frequency_mode_vectors"][0],
                                        parent_hist["frequency_mode_vectors"][
                                            0
                                        ],
                                    )
                                    > 0.2
                                ):
                                    make_good_child_next_parent = True
                                else:
                                    raise Exception(
                                        "ERROR: Good child not good enough! Exiting..."
                                    )
                                if make_good_child_next_parent:
                                    good_child["index"] = len(history)
                                    history.append(good_child)
                                    ref_mol = history[-1]["molecule"]
                                    geom_to_perturb = history[-1]["geometry"]
                                    negative_freq_vecs = history[-1][
                                        "frequency_mode_vectors"
                                    ][0]
                            else:
                                raise Exception(
                                    "ERROR: Can't deal with multiple neg frequencies yet! Exiting..."
                                )
                        else:
                            raise AssertionError(
                                "ERROR: Parent cannot have more than two childen! Exiting..."
                            )
                    # Implicitly, if the number of negative frequencies decreased from parent to child,
                    # continue normally.
                if standard:
                    history[-1]["children"].append(len(history))

                min_molecule_perturb_scale = 0.1
                scale_grid = 10
                perturb_scale_grid = (
                    max_molecule_perturb_scale - min_molecule_perturb_scale
                ) / scale_grid

                structure_successfully_perturbed = False
                for molecule_perturb_scale in np.arange(
                    max_molecule_perturb_scale,
                    min_molecule_perturb_scale,
                    -perturb_scale_grid,
                ):
                    new_coords = perturb_coordinates(
                        old_coords=geom_to_perturb,
                        negative_freq_vecs=negative_freq_vecs,
                        molecule_perturb_scale=molecule_perturb_scale,
                        reversed_direction=reversed_direction,
                    )
                    new_molecule = Molecule(
                        species=orig_species,
                        coords=new_coords,
                        charge=orig_charge,
                        spin_multiplicity=orig_multiplicity,
                    )
                    if check_connectivity:
                        structure_successfully_perturbed = (
                            check_for_structure_changes(ref_mol, new_molecule)
                            == "no_change"
                        )
                        if structure_successfully_perturbed:
                            break
                if not structure_successfully_perturbed:
                    raise Exception(
                        "ERROR: Unable to perturb coordinates to remove negative frequency without changing "
                        "the connectivity! Exiting..."
                    )

                new_opt_QCInput = QCInput(
                    molecule=new_molecule,
                    rem=orig_opt_rem,
                    opt=orig_opt_input.opt,
                    pcm=orig_opt_input.pcm,
                    solvent=orig_opt_input.solvent,
                    smx=orig_opt_input.smx,
                )
                new_opt_QCInput.write_file(input_file)
Exemplo n.º 33
0
    def opt_with_frequency_flattener(cls,
                                     qchem_command,
                                     multimode="openmp",
                                     input_file="mol.qin",
                                     output_file="mol.qout",
                                     qclog_file="mol.qclog",
                                     max_iterations=10,
                                     max_molecule_perturb_scale=0.3,
                                     reversed_direction=False,
                                     ignore_connectivity=False,
                                     **QCJob_kwargs):
        """
        Optimize a structure and calculate vibrational frequencies to check if the
        structure is in a true minima. If a frequency is negative, iteratively
        perturbe the geometry, optimize, and recalculate frequencies until all are
        positive, aka a true minima has been found.

        Args:
            qchem_command (str): Command to run QChem.
            multimode (str): Parallelization scheme, either openmp or mpi.
            input_file (str): Name of the QChem input file.
            output_file (str): Name of the QChem output file.
            max_iterations (int): Number of perturbation -> optimization -> frequency
                iterations to perform. Defaults to 10.
            max_molecule_perturb_scale (float): The maximum scaled perturbation that
                can be applied to the molecule. Defaults to 0.3.
            reversed_direction (bool): Whether to reverse the direction of the
                vibrational frequency vectors. Defaults to False.
            ignore_connectivity (bool): Whether to ignore differences in connectivity
                introduced by structural perturbation. Defaults to False.
            **QCJob_kwargs: Passthrough kwargs to QCJob. See
                :class:`custodian.qchem.jobs.QCJob`.
        """

        min_molecule_perturb_scale = 0.1
        scale_grid = 10
        perturb_scale_grid = (max_molecule_perturb_scale -
                              min_molecule_perturb_scale) / scale_grid
        msc = MoleculeStructureComparator()

        if not os.path.exists(input_file):
            raise AssertionError('Input file must be present!')
        orig_opt_input = QCInput.from_file(input_file)
        orig_opt_rem = copy.deepcopy(orig_opt_input.rem)
        orig_freq_rem = copy.deepcopy(orig_opt_input.rem)
        orig_freq_rem["job_type"] = "freq"
        first = True

        for ii in range(max_iterations):
            yield (QCJob(qchem_command=qchem_command,
                         multimode=multimode,
                         input_file=input_file,
                         output_file=output_file,
                         qclog_file=qclog_file,
                         suffix=".opt_" + str(ii),
                         backup=first,
                         **QCJob_kwargs))
            first = False
            opt_outdata = QCOutput(output_file + ".opt_" + str(ii)).data
            if opt_outdata["structure_change"] == "unconnected_fragments":
                print(
                    "Unstable molecule broke into unconnected fragments! Exiting..."
                )
                break
            else:
                freq_QCInput = QCInput(molecule=opt_outdata.get(
                    "molecule_from_optimized_geometry"),
                                       rem=orig_freq_rem,
                                       opt=orig_opt_input.opt,
                                       pcm=orig_opt_input.pcm,
                                       solvent=orig_opt_input.solvent)
                freq_QCInput.write_file(input_file)
                yield (QCJob(qchem_command=qchem_command,
                             multimode=multimode,
                             input_file=input_file,
                             output_file=output_file,
                             qclog_file=qclog_file,
                             suffix=".freq_" + str(ii),
                             backup=first,
                             **QCJob_kwargs))
                outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                errors = outdata.get("errors")
                if len(errors) != 0:
                    raise AssertionError(
                        'No errors should be encountered while flattening frequencies!'
                    )
                if outdata.get('frequencies')[0] > 0.0:
                    print("All frequencies positive!")
                    break
                else:
                    negative_freq_vecs = outdata.get(
                        "frequency_mode_vectors")[0]
                    old_coords = outdata.get("initial_geometry")
                    old_molecule = outdata.get("initial_molecule")
                    structure_successfully_perturbed = False

                    for molecule_perturb_scale in np.arange(
                            max_molecule_perturb_scale,
                            min_molecule_perturb_scale, -perturb_scale_grid):
                        new_coords = perturb_coordinates(
                            old_coords=old_coords,
                            negative_freq_vecs=negative_freq_vecs,
                            molecule_perturb_scale=molecule_perturb_scale,
                            reversed_direction=reversed_direction)
                        new_molecule = Molecule(
                            species=outdata.get('species'),
                            coords=new_coords,
                            charge=outdata.get('charge'),
                            spin_multiplicity=outdata.get('multiplicity'))
                        if msc.are_equal(old_molecule,
                                         new_molecule) or ignore_connectivity:
                            structure_successfully_perturbed = True
                            break
                    if not structure_successfully_perturbed:
                        raise Exception(
                            "Unable to perturb coordinates to remove negative frequency without changing the bonding structure"
                        )

                    new_opt_QCInput = QCInput(molecule=new_molecule,
                                              rem=orig_opt_rem,
                                              opt=orig_opt_input.opt,
                                              pcm=orig_opt_input.pcm,
                                              solvent=orig_opt_input.solvent)
                    new_opt_QCInput.write_file(input_file)
Exemplo n.º 34
0
    def test_full_init(self):
        test_molecule = QCInput.from_file(
            os.path.join(test_dir, "new_qchem_files/pcm.qin")).molecule

        test_DictSet = QChemDictSet(molecule=test_molecule,
                                    job_type='opt',
                                    basis_set='6-31g*',
                                    scf_algorithm='diis',
                                    dft_rung=1,
                                    pcm_dielectric=10.0,
                                    max_scf_cycles=35)
        self.assertEqual(
            test_DictSet.rem, {
                'job_type': 'opt',
                'gen_scfman': 'true',
                'basis': '6-31g*',
                'max_scf_cycles': 35,
                'method': 'b3lyp',
                'geom_opt_max_cycles': 200,
                'scf_algorithm': 'diis',
                'xc_grid': '3',
                'solvent_method': 'pcm',
                'symmetry': 'false',
                'sym_ignore': 'true',
                'resp_charges': 'true'
            })
        self.assertEqual(
            test_DictSet.pcm, {
                'heavypoints': '194',
                'hpoints': '194',
                'radii': 'uff',
                'theory': 'cpcm',
                'vdwscale': '1.1'
            })
        self.assertEqual(test_DictSet.solvent, {'dielectric': 10.0})
        self.assertEqual(test_DictSet.molecule, test_molecule)

        test_DictSet = QChemDictSet(molecule=test_molecule,
                                    job_type='opt',
                                    basis_set='6-31g*',
                                    scf_algorithm='diis',
                                    dft_rung=1,
                                    smd_solvent='water',
                                    max_scf_cycles=35)
        self.assertEqual(
            test_DictSet.rem, {
                'job_type': 'opt',
                'gen_scfman': 'true',
                'basis': '6-31g*',
                'max_scf_cycles': 35,
                'method': 'b3lyp',
                'geom_opt_max_cycles': 200,
                'scf_algorithm': 'diis',
                'xc_grid': '3',
                'solvent_method': 'smd',
                'ideriv': '1',
                'symmetry': 'false',
                'sym_ignore': 'true',
                'resp_charges': 'true'
            })
        self.assertEqual(test_DictSet.smx, {'solvent': 'water'})
Exemplo n.º 35
0
    def opt_with_frequency_flattener(
        cls,
        qchem_command,
        multimode="openmp",
        input_file="mol.qin",
        output_file="mol.qout",
        qclog_file="mol.qclog",
        max_iterations=10,
        max_molecule_perturb_scale=0.3,
        check_connectivity=True,
        linked=True,
        transition_state=False,
        freq_before_opt=False,
        save_final_scratch=False,
        **QCJob_kwargs,
    ):
        """
        Optimize a structure and calculate vibrational frequencies to check if the
        structure is in a true minima.

        If there are an inappropriate number of imaginary frequencies (>0 for a
         minimum-energy structure, >1 for a transition-state), attempt to re-calculate
         using one of two methods:
            - Perturb the geometry based on the imaginary frequencies and re-optimize
            - Use the exact Hessian to inform a subsequent optimization
         After each geometry optimization, the frequencies are re-calculated to
         determine if a true minimum (or transition-state) has been found.

        Note: Very small imaginary frequencies (-15cm^-1 < nu < 0) are allowed
        if there is only one more than there should be. In other words, if there
        is one very small imaginary frequency, it is still treated as a minimum,
        and if there is one significant imaginary frequency and one very small
        imaginary frequency, it is still treated as a transition-state.

        Args:
            qchem_command (str): Command to run QChem.
            multimode (str): Parallelization scheme, either openmp or mpi.
            input_file (str): Name of the QChem input file.
            output_file (str): Name of the QChem output file.
            max_iterations (int): Number of perturbation -> optimization -> frequency
                iterations to perform. Defaults to 10.
            max_molecule_perturb_scale (float): The maximum scaled perturbation that
                can be applied to the molecule. Defaults to 0.3.
            check_connectivity (bool): Whether to check differences in connectivity
                introduced by structural perturbation. Defaults to True.
            linked (bool): Whether or not to use the linked flattener. If set to True (default),
                then the explicit Hessians from a vibrational frequency analysis will be used
                as the initial Hessian of subsequent optimizations. In many cases, this can
                significantly improve optimization efficiency.
            transition_state (bool): If True (default False), use a ts
                optimization (search for a saddle point instead of a minimum)
            freq_before_opt (bool): If True (default False), run a frequency
                calculation before any opt/ts searches to improve understanding
                of the local potential energy surface.
            save_final_scratch (bool): Whether to save full scratch directory contents
                at the end of the flattening. Defaults to False.
            **QCJob_kwargs: Passthrough kwargs to QCJob. See
                :class:`custodian.qchem.jobs.QCJob`.
        """
        if not os.path.exists(input_file):
            raise AssertionError("Input file must be present!")

        if transition_state:
            opt_method = "ts"
            perturb_index = 1
        else:
            opt_method = "opt"
            perturb_index = 0

        energy_diff_cutoff = 0.0000001

        orig_input = QCInput.from_file(input_file)
        freq_rem = copy.deepcopy(orig_input.rem)
        freq_rem["job_type"] = "freq"
        opt_rem = copy.deepcopy(orig_input.rem)
        opt_rem["job_type"] = opt_method
        first = True
        energy_history = list()

        if freq_before_opt:
            if not linked:
                warnings.warn(
                    "WARNING: This first frequency calculation will not inform subsequent optimization!"
                )
            yield (QCJob(
                qchem_command=qchem_command,
                multimode=multimode,
                input_file=input_file,
                output_file=output_file,
                qclog_file=qclog_file,
                suffix=".freq_pre",
                save_scratch=True,
                backup=first,
                **QCJob_kwargs,
            ))

            if linked:
                opt_rem["geom_opt_hessian"] = "read"
                opt_rem["scf_guess_always"] = True

            opt_QCInput = QCInput(
                molecule=orig_input.molecule,
                rem=opt_rem,
                opt=orig_input.opt,
                pcm=orig_input.pcm,
                solvent=orig_input.solvent,
                smx=orig_input.smx,
                vdw_mode=orig_input.vdw_mode,
                van_der_waals=orig_input.van_der_waals,
            )
            opt_QCInput.write_file(input_file)
            first = False

        if linked:
            opt_rem["geom_opt_hessian"] = "read"
            opt_rem["scf_guess_always"] = True

            for ii in range(max_iterations):
                yield (QCJob(
                    qchem_command=qchem_command,
                    multimode=multimode,
                    input_file=input_file,
                    output_file=output_file,
                    qclog_file=qclog_file,
                    suffix=".{}_".format(opt_method) + str(ii),
                    save_scratch=True,
                    backup=first,
                    **QCJob_kwargs,
                ))
                opt_outdata = QCOutput(output_file +
                                       ".{}_".format(opt_method) +
                                       str(ii)).data
                opt_indata = QCInput.from_file(input_file +
                                               ".{}_".format(opt_method) +
                                               str(ii))
                if opt_indata.rem["scf_algorithm"] != freq_rem["scf_algorithm"]:
                    freq_rem["scf_algorithm"] = opt_indata.rem["scf_algorithm"]
                    opt_rem["scf_algorithm"] = opt_indata.rem["scf_algorithm"]
                first = False
                if opt_outdata[
                        "structure_change"] == "unconnected_fragments" and not opt_outdata[
                            "completion"]:
                    if not transition_state:
                        warnings.warn(
                            "Unstable molecule broke into unconnected fragments which failed to optimize! Exiting..."
                        )
                        break
                energy_history.append(opt_outdata.get("final_energy"))
                freq_QCInput = QCInput(
                    molecule=opt_outdata.get(
                        "molecule_from_optimized_geometry"),
                    rem=freq_rem,
                    opt=orig_input.opt,
                    pcm=orig_input.pcm,
                    solvent=orig_input.solvent,
                    smx=orig_input.smx,
                    vdw_mode=orig_input.vdw_mode,
                    van_der_waals=orig_input.van_der_waals,
                )
                freq_QCInput.write_file(input_file)
                yield (QCJob(
                    qchem_command=qchem_command,
                    multimode=multimode,
                    input_file=input_file,
                    output_file=output_file,
                    qclog_file=qclog_file,
                    suffix=".freq_" + str(ii),
                    save_scratch=True,
                    backup=first,
                    **QCJob_kwargs,
                ))
                outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                indata = QCInput.from_file(input_file + ".freq_" + str(ii))
                if indata.rem["scf_algorithm"] != freq_rem["scf_algorithm"]:
                    freq_rem["scf_algorithm"] = indata.rem["scf_algorithm"]
                    opt_rem["scf_algorithm"] = indata.rem["scf_algorithm"]
                errors = outdata.get("errors")
                if len(errors) != 0:
                    raise AssertionError(
                        "No errors should be encountered while flattening frequencies!"
                    )
                if not transition_state:
                    freq_0 = outdata.get("frequencies")[0]
                    freq_1 = outdata.get("frequencies")[1]
                    if freq_0 > 0.0:
                        warnings.warn("All frequencies positive!")
                        break
                    if abs(freq_0) < 15.0 and freq_1 > 0.0:
                        warnings.warn(
                            "One negative frequency smaller than 15.0 - not worth further flattening!"
                        )
                        break
                    if len(energy_history) > 1:
                        if abs(energy_history[-1] -
                               energy_history[-2]) < energy_diff_cutoff:
                            warnings.warn("Energy change below cutoff!")
                            break
                    opt_QCInput = QCInput(
                        molecule=opt_outdata.get(
                            "molecule_from_optimized_geometry"),
                        rem=opt_rem,
                        opt=orig_input.opt,
                        pcm=orig_input.pcm,
                        solvent=orig_input.solvent,
                        smx=orig_input.smx,
                        vdw_mode=orig_input.vdw_mode,
                        van_der_waals=orig_input.van_der_waals,
                    )
                    opt_QCInput.write_file(input_file)
                else:
                    freq_0 = outdata.get("frequencies")[0]
                    freq_1 = outdata.get("frequencies")[1]
                    freq_2 = outdata.get("frequencies")[2]
                    if freq_0 < 0.0 < freq_1:
                        warnings.warn("Saddle point found!")
                        break
                    if abs(freq_1) < 15.0 and freq_2 > 0.0:
                        warnings.warn(
                            "Second small imaginary frequency (smaller than 15.0) - not worth further flattening!"
                        )
                        break
                    opt_QCInput = QCInput(
                        molecule=opt_outdata.get(
                            "molecule_from_optimized_geometry"),
                        rem=opt_rem,
                        opt=orig_input.opt,
                        pcm=orig_input.pcm,
                        solvent=orig_input.solvent,
                        smx=orig_input.smx,
                        vdw_mode=orig_input.vdw_mode,
                        van_der_waals=orig_input.van_der_waals,
                    )
                    opt_QCInput.write_file(input_file)
            if not save_final_scratch:
                shutil.rmtree(os.path.join(os.getcwd(), "scratch"))

        else:
            orig_opt_input = QCInput.from_file(input_file)
            history = list()

            for ii in range(max_iterations):
                yield (QCJob(
                    qchem_command=qchem_command,
                    multimode=multimode,
                    input_file=input_file,
                    output_file=output_file,
                    qclog_file=qclog_file,
                    suffix=".{}_".format(opt_method) + str(ii),
                    backup=first,
                    **QCJob_kwargs,
                ))
                opt_outdata = QCOutput(output_file +
                                       ".{}_".format(opt_method) +
                                       str(ii)).data
                if first:
                    orig_species = copy.deepcopy(opt_outdata.get("species"))
                    orig_charge = copy.deepcopy(opt_outdata.get("charge"))
                    orig_multiplicity = copy.deepcopy(
                        opt_outdata.get("multiplicity"))
                    orig_energy = copy.deepcopy(
                        opt_outdata.get("final_energy"))
                first = False
                if opt_outdata[
                        "structure_change"] == "unconnected_fragments" and not opt_outdata[
                            "completion"]:
                    if not transition_state:
                        warnings.warn(
                            "Unstable molecule broke into unconnected fragments which failed to optimize! Exiting..."
                        )
                        break
                freq_QCInput = QCInput(
                    molecule=opt_outdata.get(
                        "molecule_from_optimized_geometry"),
                    rem=freq_rem,
                    opt=orig_opt_input.opt,
                    pcm=orig_opt_input.pcm,
                    solvent=orig_opt_input.solvent,
                    smx=orig_opt_input.smx,
                    vdw_mode=orig_opt_input.vdw_mode,
                    van_der_waals=orig_opt_input.van_der_waals,
                )
                freq_QCInput.write_file(input_file)
                yield (QCJob(
                    qchem_command=qchem_command,
                    multimode=multimode,
                    input_file=input_file,
                    output_file=output_file,
                    qclog_file=qclog_file,
                    suffix=".freq_" + str(ii),
                    backup=first,
                    **QCJob_kwargs,
                ))
                outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                errors = outdata.get("errors")
                if len(errors) != 0:
                    raise AssertionError(
                        "No errors should be encountered while flattening frequencies!"
                    )
                if not transition_state:
                    freq_0 = outdata.get("frequencies")[0]
                    freq_1 = outdata.get("frequencies")[1]
                    if freq_0 > 0.0:
                        warnings.warn("All frequencies positive!")
                        if opt_outdata.get("final_energy") > orig_energy:
                            warnings.warn(
                                "WARNING: Energy increased during frequency flattening!"
                            )
                        break
                    if abs(freq_0) < 15.0 and freq_1 > 0.0:
                        warnings.warn(
                            "One negative frequency smaller than 15.0 - not worth further flattening!"
                        )
                        break
                    if len(energy_history) > 1:
                        if abs(energy_history[-1] -
                               energy_history[-2]) < energy_diff_cutoff:
                            warnings.warn("Energy change below cutoff!")
                            break
                else:
                    freq_0 = outdata.get("frequencies")[0]
                    freq_1 = outdata.get("frequencies")[1]
                    freq_2 = outdata.get("frequencies")[2]
                    if freq_0 < 0.0 < freq_1:
                        warnings.warn("Saddle point found!")
                        break
                    if abs(freq_1) < 15.0 and freq_2 > 0.0:
                        warnings.warn(
                            "Second small imaginary frequency (smaller than 15.0) - not worth further flattening!"
                        )
                        break

                hist = {}
                hist["molecule"] = copy.deepcopy(
                    outdata.get("initial_molecule"))
                hist["geometry"] = copy.deepcopy(
                    outdata.get("initial_geometry"))
                hist["frequencies"] = copy.deepcopy(outdata.get("frequencies"))
                hist["frequency_mode_vectors"] = copy.deepcopy(
                    outdata.get("frequency_mode_vectors"))
                hist["num_neg_freqs"] = sum(
                    1 for freq in outdata.get("frequencies") if freq < 0)
                hist["energy"] = copy.deepcopy(opt_outdata.get("final_energy"))
                hist["index"] = len(history)
                hist["children"] = list()
                history.append(hist)

                ref_mol = history[-1]["molecule"]
                geom_to_perturb = history[-1]["geometry"]
                negative_freq_vecs = history[-1]["frequency_mode_vectors"][
                    perturb_index]
                reversed_direction = False
                standard = True

                # If we've found one or more negative frequencies in two consecutive iterations, let's dig in
                # deeper:
                if len(history) > 1:
                    # Start by finding the latest iteration's parent:
                    if history[-1]["index"] in history[-2]["children"]:
                        parent_hist = history[-2]
                        history[-1]["parent"] = parent_hist["index"]
                    elif history[-1]["index"] in history[-3]["children"]:
                        parent_hist = history[-3]
                        history[-1]["parent"] = parent_hist["index"]
                    else:
                        raise AssertionError(
                            "ERROR: your parent should always be one or two iterations behind you! Exiting..."
                        )

                    # if the number of negative frequencies has remained constant or increased from parent to
                    # child,
                    if history[-1]["num_neg_freqs"] >= parent_hist[
                            "num_neg_freqs"]:
                        # check to see if the parent only has one child, aka only the positive perturbation has
                        # been tried,
                        # in which case just try the negative perturbation from the same parent
                        if len(parent_hist["children"]) == 1:
                            ref_mol = parent_hist["molecule"]
                            geom_to_perturb = parent_hist["geometry"]
                            negative_freq_vecs = parent_hist[
                                "frequency_mode_vectors"][perturb_index]
                            reversed_direction = True
                            standard = False
                            parent_hist["children"].append(len(history))
                        # If the parent has two children, aka both directions have been tried, then we have to
                        # get creative:
                        elif len(parent_hist["children"]) == 2:
                            # If we're dealing with just one negative frequency,
                            if parent_hist["num_neg_freqs"] == 1:
                                if history[parent_hist["children"][0]][
                                        "energy"] < history[-1]["energy"]:
                                    good_child = copy.deepcopy(
                                        history[parent_hist["children"][0]])
                                else:
                                    good_child = copy.deepcopy(history[-1])
                                if good_child["num_neg_freqs"] > 1:
                                    raise Exception(
                                        "ERROR: Child with lower energy has more negative frequencies! "
                                        "Exiting...")
                                if good_child["energy"] < parent_hist["energy"]:
                                    make_good_child_next_parent = True
                                elif (vector_list_diff(
                                        good_child["frequency_mode_vectors"]
                                    [perturb_index],
                                        parent_hist["frequency_mode_vectors"]
                                    [perturb_index],
                                ) > 0.2):
                                    make_good_child_next_parent = True
                                else:
                                    raise Exception(
                                        "ERROR: Good child not good enough! Exiting..."
                                    )
                                if make_good_child_next_parent:
                                    good_child["index"] = len(history)
                                    history.append(good_child)
                                    ref_mol = history[-1]["molecule"]
                                    geom_to_perturb = history[-1]["geometry"]
                                    negative_freq_vecs = history[-1][
                                        "frequency_mode_vectors"][
                                            perturb_index]
                            else:
                                raise Exception(
                                    "ERROR: Can't deal with multiple neg frequencies yet! Exiting..."
                                )
                        else:
                            raise AssertionError(
                                "ERROR: Parent cannot have more than two childen! Exiting..."
                            )
                    # Implicitly, if the number of negative frequencies decreased from parent to child,
                    # continue normally.
                if standard:
                    history[-1]["children"].append(len(history))

                min_molecule_perturb_scale = 0.1
                scale_grid = 10
                perturb_scale_grid = (max_molecule_perturb_scale -
                                      min_molecule_perturb_scale) / scale_grid

                structure_successfully_perturbed = False
                for molecule_perturb_scale in np.arange(
                        max_molecule_perturb_scale,
                        min_molecule_perturb_scale,
                        -perturb_scale_grid,
                ):
                    new_coords = perturb_coordinates(
                        old_coords=geom_to_perturb,
                        negative_freq_vecs=negative_freq_vecs,
                        molecule_perturb_scale=molecule_perturb_scale,
                        reversed_direction=reversed_direction,
                    )
                    new_molecule = Molecule(
                        species=orig_species,
                        coords=new_coords,
                        charge=orig_charge,
                        spin_multiplicity=orig_multiplicity,
                    )
                    if check_connectivity and not transition_state:
                        structure_successfully_perturbed = (
                            check_for_structure_changes(
                                ref_mol, new_molecule) == "no_change")
                        if structure_successfully_perturbed:
                            break
                if not structure_successfully_perturbed:
                    raise Exception(
                        "ERROR: Unable to perturb coordinates to remove negative frequency without changing "
                        "the connectivity! Exiting...")

                new_opt_QCInput = QCInput(
                    molecule=new_molecule,
                    rem=opt_rem,
                    opt=orig_opt_input.opt,
                    pcm=orig_opt_input.pcm,
                    solvent=orig_opt_input.solvent,
                    smx=orig_opt_input.smx,
                    vdw_mode=orig_opt_input.vdw_mode,
                    van_der_waals=orig_opt_input.van_der_waals,
                )
                new_opt_QCInput.write_file(input_file)
    def test_read_nbo(self):
        str_molecule = """$molecule
 0 2
 C     -2.0338520000      0.0865500000     -1.4158570000
 C     -1.2819580000      0.3850830000     -0.1564990000
 C     -2.0067300000      1.1271820000      0.9225950000
 C      0.1219120000     -0.0366190000      0.0148810000
 C      0.6767790000     -1.0507090000     -0.7802400000
 C      2.0072450000     -1.4517610000     -0.6185380000
 C      2.8079970000     -0.8434840000      0.3427930000
 C      2.2778880000      0.1645690000      1.1416530000
 C      0.9468200000      0.5630060000      0.9784410000
 H     -1.3919850000      0.1591240000     -2.2995570000
 H     -2.4671570000     -0.9174600000     -1.3722490000
 H     -2.8505080000      0.8017250000     -1.5613060000
 H     -3.0889210000      0.9823990000      0.8362370000
 H     -1.7216740000      0.7761670000      1.9194500000
 H     -1.8021560000      2.1999010000      0.8510710000
 H      0.0793240000     -1.5592640000     -1.5324310000
 H      2.4136820000     -2.2421190000     -1.2440900000
 H      3.8415290000     -1.1539430000      0.4689660000
 H      2.8984450000      0.6464300000      1.8925800000
 H      0.5733200000      1.3632210000      1.6120990000
$end

$rem
   job_type = sp
   max_scf_cycles = 200
   gen_scfman = true
   xc_grid = 3
   scf_algorithm = diis
   method = wb97xv
   basis = def2-tzvp
   symmetry = false
   sym_ignore = true
   nbo = true
$end

$nbo
$end
"""
        qcinp = QCInput.from_string(str_molecule)
        self.assertEqual(str_molecule, str(qcinp))

        str_molecule = """$molecule
 0 2
 C     -2.0338520000      0.0865500000     -1.4158570000
 C     -1.2819580000      0.3850830000     -0.1564990000
 C     -2.0067300000      1.1271820000      0.9225950000
 C      0.1219120000     -0.0366190000      0.0148810000
 C      0.6767790000     -1.0507090000     -0.7802400000
 C      2.0072450000     -1.4517610000     -0.6185380000
 C      2.8079970000     -0.8434840000      0.3427930000
 C      2.2778880000      0.1645690000      1.1416530000
 C      0.9468200000      0.5630060000      0.9784410000
 H     -1.3919850000      0.1591240000     -2.2995570000
 H     -2.4671570000     -0.9174600000     -1.3722490000
 H     -2.8505080000      0.8017250000     -1.5613060000
 H     -3.0889210000      0.9823990000      0.8362370000
 H     -1.7216740000      0.7761670000      1.9194500000
 H     -1.8021560000      2.1999010000      0.8510710000
 H      0.0793240000     -1.5592640000     -1.5324310000
 H      2.4136820000     -2.2421190000     -1.2440900000
 H      3.8415290000     -1.1539430000      0.4689660000
 H      2.8984450000      0.6464300000      1.8925800000
 H      0.5733200000      1.3632210000      1.6120990000
$end

$rem
   job_type = sp
   max_scf_cycles = 200
   gen_scfman = true
   xc_grid = 3
   scf_algorithm = diis
   method = wb97xv
   basis = def2-tzvp
   symmetry = false
   sym_ignore = true
   nbo = true
$end

$nbo
   print = 1
$end
"""
        qcinp = QCInput.from_string(str_molecule)
        self.assertEqual(str_molecule, str(qcinp))
Exemplo n.º 37
0
 def test_OptFF(self):
     myjob = QCJob.opt_with_frequency_flattener(
         qchem_command="qchem -slurm",
         max_cores=32,
         input_file="mol.qin",
         output_file="mol.qout",
         linked=False,
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".opt_0",
         backup=True,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".freq_0",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "6004_frag12/mol.qin.freq_0")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".opt_1",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir, "6004_frag12/mol.qin.opt_1")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".freq_1",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "6004_frag12/mol.qin.freq_1")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".opt_2",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir, "6004_frag12/mol.qin.opt_2")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".freq_2",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "6004_frag12/mol.qin.freq_2")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
Exemplo n.º 38
0
    def ts_with_frequency_flattener(cls,
                                    qchem_command,
                                    multimode="openmp",
                                    input_file="mol.qin",
                                    output_file="mol.qout",
                                    qclog_file="mol.qclog",
                                    ts_guess_method="fsm",
                                    max_iterations=10,
                                    max_molecule_perturb_scale=0.3,
                                    check_connectivity=True,
                                    **QCJob_kwargs):
        """
        Optimize the transition state for a reaction based on reactant and product geometries and calculate vibrational
        frequencies to check if the structure is in a true minima. If a frequency is negative, iteratively
        perturbe the geometry, optimize, and recalculate frequencies until all are
        positive, aka a true minima has been found.

        Args:
            qchem_command (str): Command to run QChem.
            multimode (str): Parallelization scheme, either openmp or mpi.
            input_file (str): Name of the QChem input file.
            output_file (str): Name of the QChem output file.
            qclog_file (str): Name of the QChem log file.
            ts_guess_method (str): Name of the method to be used to generate a guess for the transition state. By
                default, this will be "fsm", meaning that the Freezing-String Method will be used. In the future, "gsm"
                for the Growing-String Method will also be supported.
            max_iterations (int): Number of perturbation -> optimization -> frequency
                iterations to perform. Defaults to 10.
            max_molecule_perturb_scale (float): The maximum scaled perturbation that
                can be applied to the molecule. Defaults to 0.3.
            check_connectivity (bool): Whether to check differences in connectivity
                introduced by structural perturbation. Defaults to True.
            **QCJob_kwargs: Passthrough kwargs to QCJob. See
                :class:`custodian.qchem.jobs.QCJob`.
        """

        min_molecule_perturb_scale = 0.1
        scale_grid = 10
        perturb_scale_grid = (
            max_molecule_perturb_scale - min_molecule_perturb_scale
        ) / scale_grid

        orig_ts_input = QCInput.from_file(input_file)
        orig_ts_rem = copy.deepcopy(orig_ts_input.rem)
        orig_ts_rem["job_type"] = "ts"
        del orig_ts_rem["fsm_ngrad"]
        del orig_ts_rem["fsm_nnode"]
        del orig_ts_rem["fsm_mode"]
        del orig_ts_rem["fsm_opt_mode"]

        orig_freq_rem = copy.deepcopy(orig_ts_input.rem)
        orig_freq_rem["job_type"] = "freq"

        orig_ts_rem["geom_opt_max_cycles"] = 250

        if not os.path.exists(input_file):
            raise AssertionError('Input file must be present!')

        # First job should be the transition state guess, via FSM or GSM
        yield (QCJob(qchem_command=qchem_command,
                     multimode=multimode,
                     input_file=input_file,
                     output_file=output_file,
                     qclog_file=qclog_file,
                     suffix="." + ts_guess_method,
                     backup=True,
                     **QCJob_kwargs))

        fsm_outdata = QCOutput(output_file + "." + ts_guess_method).data
        #TODO: Consider possible errors or other problems which might arise in an FSM job
        ts_QCInput = QCInput(molecule=fsm_outdata.get("molecule_from_last_geometry"),
                             rem=orig_ts_rem,
                             opt=orig_ts_input.opt,
                             pcm=orig_ts_input.pcm,
                             solvent=orig_ts_input.solvent,
                             smx=orig_ts_input.smx)
        ts_QCInput.write_file(input_file)

        reversed_direction = False
        num_neg_freqs = []

        for ii in range(max_iterations):
            yield (QCJob(
                qchem_command=qchem_command,
                multimode=multimode,
                input_file=input_file,
                output_file=output_file,
                qclog_file=qclog_file,
                suffix=".ts_" + str(ii),
                backup=False,
                **QCJob_kwargs))
            first = False
            ts_outdata = QCOutput(output_file + ".ts_" + str(ii)).data
            if ts_outdata["structure_change"] == "unconnected_fragments" and not ts_outdata["completion"]:
                print("Unstable molecule broke into unconnected fragments which failed to optimize! Exiting...")
                break
            else:
                freq_QCInput = QCInput(
                    molecule=ts_outdata.get("molecule_from_optimized_geometry"),
                    rem=orig_freq_rem,
                    opt=orig_ts_input.opt,
                    pcm=orig_ts_input.pcm,
                    solvent=orig_ts_input.solvent,
                    smx=orig_ts_input.smx)
                freq_QCInput.write_file(input_file)
                yield (QCJob(
                    qchem_command=qchem_command,
                    multimode=multimode,
                    input_file=input_file,
                    output_file=output_file,
                    qclog_file=qclog_file,
                    suffix=".freq_" + str(ii),
                    backup=first,
                    **QCJob_kwargs))
                outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                errors = outdata.get("errors")
                if len(errors) != 0:
                    raise AssertionError('No errors should be encountered while flattening frequencies!')
                if outdata.get('frequencies')[0] > 0.0:
                    print("All frequencies positive!")
                    break
                else:
                    num_neg_freqs += [sum(1 for freq in outdata.get('frequencies') if freq < 0)]
                    if len(num_neg_freqs) > 1:
                        if num_neg_freqs[-1] == num_neg_freqs[-2] and not reversed_direction:
                            reversed_direction = True
                        elif num_neg_freqs[-1] == num_neg_freqs[-2] and reversed_direction:
                            if len(num_neg_freqs) < 3:
                                raise AssertionError("ERROR: This should only be possible after at least three frequency flattening iterations! Exiting...")
                            else:
                                raise Exception("ERROR: Reversing the perturbation direction still could not flatten any frequencies. Exiting...")
                        elif num_neg_freqs[-1] != num_neg_freqs[-2] and reversed_direction:
                            reversed_direction = False

                    negative_freq_vecs = outdata.get("frequency_mode_vectors")[0]
                    structure_successfully_perturbed = False

                    for molecule_perturb_scale in np.arange(
                            max_molecule_perturb_scale, min_molecule_perturb_scale,
                            -perturb_scale_grid):
                        new_coords = perturb_coordinates(
                            old_coords=outdata.get("initial_geometry"),
                            negative_freq_vecs=negative_freq_vecs,
                            molecule_perturb_scale=molecule_perturb_scale,
                            reversed_direction=reversed_direction)
                        new_molecule = Molecule(
                            species=outdata.get('species'),
                            coords=new_coords,
                            charge=outdata.get('charge'),
                            spin_multiplicity=outdata.get('multiplicity'))
                        if check_connectivity:
                            old_molgraph = MoleculeGraph.with_local_env_strategy(outdata.get("initial_molecule"),
                                                               OpenBabelNN(),
                                                               reorder=False,
                                                               extend_structure=False)
                            new_molgraph = MoleculeGraph.with_local_env_strategy(new_molecule,
                                                               OpenBabelNN(),
                                                               reorder=False,
                                                               extend_structure=False)
                            if old_molgraph.isomorphic_to(new_molgraph):
                                structure_successfully_perturbed = True
                                break
                    if not structure_successfully_perturbed:
                        raise Exception(
                            "ERROR: Unable to perturb coordinates to remove negative frequency without changing the connectivity! Exiting..."
                        )

                    new_ts_QCInput = QCInput(
                        molecule=new_molecule,
                        rem=orig_ts_rem,
                        opt=orig_ts_input.opt,
                        pcm=orig_ts_input.pcm,
                        solvent=orig_ts_input.solvent)
                    new_ts_QCInput.write_file(input_file)
Exemplo n.º 39
0
    def correct(self):
        backup({self.input_file, self.output_file})
        actions = []
        self.qcinp = QCInput.from_file(self.input_file)

        if "SCF_failed_to_converge" in self.errors:
            # Check number of SCF cycles. If not set or less than scf_max_cycles,
            # increase to that value and rerun. If already set, check if
            # scf_algorithm is unset or set to DIIS, in which case set to GDM.
            # Otherwise, tell user to call SCF error handler and do nothing.
            if str(self.qcinp.rem.get("max_scf_cycles")) != str(
                    self.scf_max_cycles):
                self.qcinp.rem["max_scf_cycles"] = self.scf_max_cycles
                actions.append({"max_scf_cycles": self.scf_max_cycles})
            elif self.qcinp.rem.get("scf_algorithm", "diis").lower() == "diis":
                self.qcinp.rem["scf_algorithm"] = "gdm"
                actions.append({"scf_algorithm": "gdm"})
            elif self.qcinp.rem.get("scf_algorithm", "gdm").lower() == "gdm":
                self.qcinp.rem["scf_algorithm"] = "diis_gdm"
                actions.append({"scf_algorithm": "diis_gdm"})
            else:
                print(
                    "More advanced changes may impact the SCF result. Use the SCF error handler"
                )

        elif "out_of_opt_cycles" in self.errors:
            # Check number of opt cycles. If less than geom_max_cycles, increase
            # to that value, set last geom as new starting geom and rerun.
            if str(self.qcinp.rem.get("geom_opt_max_cycles")) != str(
                    self.geom_max_cycles):
                self.qcinp.rem["geom_opt_max_cycles"] = self.geom_max_cycles
                actions.append({"geom_max_cycles:": self.scf_max_cycles})
                if len(self.outdata.get("energy_trajectory")) > 1:
                    self.qcinp.molecule = self.outdata.get(
                        "molecule_from_last_geometry")
                    actions.append({"molecule": "molecule_from_last_geometry"})
            else:
                self.opt_error_history += [self.outdata["structure_change"]]
                if len(self.opt_error_history) > 1:
                    if self.opt_error_history[-1] == "no_change":
                        return {
                            "errors": self.errors,
                            "actions": None,
                            "opt_error_history": self.opt_error_history
                        }
                self.qcinp.molecule = self.outdata.get(
                    "molecule_from_last_geometry")
                actions.append({"molecule": "molecule_from_last_geometry"})

        elif "unable_to_determine_lamda" in self.errors:
            # Set last geom as new starting geom and rerun. If no opt cycles,
            # use diff SCF strat? Diff initial guess? Change basis?
            if len(self.outdata.get("energy_trajectory")) > 1:
                self.qcinp.molecule = self.outdata.get(
                    "molecule_from_last_geometry")
                actions.append({"molecule": "molecule_from_last_geometry"})
            elif self.qcinp.rem.get("scf_algorithm", "diis").lower() == "diis":
                self.qcinp.rem["scf_algorithm"] = "rca_diis"
                actions.append({"scf_algorithm": "rca_diis"})
                if self.qcinp.rem.get("gen_scfman"):
                    self.qcinp.rem["gen_scfman"] = False
                    actions.append({"gen_scfman": False})
            else:
                print(
                    "Use a different initial guess? Perhaps a different basis?"
                )

        elif "linear_dependent_basis" in self.errors:
            # DIIS -> RCA_DIIS. If already RCA_DIIS, change basis?
            if self.qcinp.rem.get("scf_algorithm", "diis").lower() == "diis":
                self.qcinp.rem["scf_algorithm"] = "rca_diis"
                actions.append({"scf_algorithm": "rca_diis"})
                if self.qcinp.rem.get("gen_scfman"):
                    self.qcinp.rem["gen_scfman"] = False
                    actions.append({"gen_scfman": False})
            else:
                print("Perhaps use a better basis?")

        elif "failed_to_transform_coords" in self.errors:
            # Check for symmetry flag in rem. If not False, set to False and rerun.
            # If already False, increase threshold?
            if not self.qcinp.rem.get("sym_ignore") or self.qcinp.rem.get(
                    "symmetry"):
                self.qcinp.rem["sym_ignore"] = True
                self.qcinp.rem["symmetry"] = False
                actions.append({"sym_ignore": True})
                actions.append({"symmetry": False})
            else:
                print("Perhaps increase the threshold?")

        elif "input_file_error" in self.errors:
            print(
                "Something is wrong with the input file. Examine error message by hand."
            )
            return {"errors": self.errors, "actions": None}

        elif "failed_to_read_input" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun job as-is"})

        elif "IO_error" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun job as-is"})

        elif "read_molecule_error" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun job as-is"})

        elif "never_called_qchem" in self.errors:
            # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is.
            actions.append({"rerun job as-is"})

        elif "unknown_error" in self.errors:
            print("Examine error message by hand.")
            return {"errors": self.errors, "actions": None}

        else:
            # You should never get here. If correct is being called then errors should have at least one entry,
            # in which case it should have been caught by the if/elifs above.
            print(
                "If you get this message, something has gone terribly wrong!")
            return {"errors": self.errors, "actions": None}

        os.rename(self.input_file, self.input_file + ".last")
        self.qcinp.write_file(self.input_file)
        return {"errors": self.errors, "actions": actions}
Exemplo n.º 40
0
 def test_OptFF(self):
     myjob = QCJob.opt_with_frequency_flattener(
         qchem_command="qchem -slurm",
         max_cores=32,
         input_file="mol.qin",
         output_file="mol.qout",
         linked=True,
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".opt_0",
         save_scratch=True,
         backup=True,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".freq_0",
         save_scratch=True,
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "small_neg_freq/mol.qin.freq_0")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     shutil.copyfile(
         os.path.join(scr_dir, "mol.qin"),
         os.path.join(scr_dir, "mol.qin.freq_0"),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".opt_1",
         save_scratch=True,
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "small_neg_freq/mol.qin.opt_1")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     shutil.copyfile(
         os.path.join(scr_dir, "mol.qin"),
         os.path.join(scr_dir, "mol.qin.opt_1"),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".freq_1",
         save_scratch=True,
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "small_neg_freq/mol.qin.freq_1")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     shutil.copyfile(
         os.path.join(scr_dir, "mol.qin"),
         os.path.join(scr_dir, "mol.qin.freq_1"),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".opt_2",
         save_scratch=True,
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "small_neg_freq/mol.qin.opt_2")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     shutil.copyfile(
         os.path.join(scr_dir, "mol.qin"),
         os.path.join(scr_dir, "mol.qin.opt_2"),
     )
     expected_next = QCJob(
         qchem_command="qchem -slurm",
         max_cores=32,
         multimode="openmp",
         input_file="mol.qin",
         output_file="mol.qout",
         suffix=".freq_2",
         save_scratch=True,
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "small_neg_freq/mol.qin.freq_2")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "mol.qin")).as_dict(),
     )
     shutil.copyfile(
         os.path.join(scr_dir, "mol.qin"),
         os.path.join(scr_dir, "mol.qin.freq_2"),
     )
     self.assertRaises(StopIteration, myjob.__next__)
Exemplo n.º 41
0
    def test_from_string(self):
        string = """$molecule
 0  1
 S          -0.00250959       -0.05817469       -0.02921636
 C           1.70755408       -0.03033788       -0.01382912
 H           2.24317221       -0.05215019        0.92026728
 C           2.21976393        0.01718014       -1.27293235
 H           3.27786220        0.04082146       -1.48539646
 C           1.20867399        0.04478540       -2.27007793
 H           1.40292257        0.10591684       -3.33110912
 C          -0.05341046        0.01577217       -1.74839343
 C          -1.32843436        0.03545064       -2.45531187
 C          -1.55195156        0.08743920       -3.80184635
 H          -0.75245172        0.10267657       -4.52817967
 C          -2.93293778        0.08408786       -4.13352169
 H          -3.31125108        0.11340328       -5.14405819
 C          -3.73173288        0.02741365       -3.03412864
 H          -4.80776535        0.00535688       -2.99564645
 S          -2.81590978       -0.00516172       -1.58990580
$end


$rem
              jobtype = opt
               method = wb97m-v
                basis = def2-tzvppd
           gen_scfman = true
  geom_opt_max_cycles = 75
       max_scf_cycles = 300
        scf_algorithm = diis
            scf_guess = sad
           sym_ignore = true
             symmetry = false
               thresh = 14
$end


$opt
CONSTRAINT
tors 6 8 9 10 0.0
ENDCONSTRAINT
$end
"""
        qcinput_test = QCInput.from_string(string)
        species = [
            "S",
            "C",
            "H",
            "C",
            "H",
            "C",
            "H",
            "C",
            "C",
            "C",
            "H",
            "C",
            "H",
            "C",
            "H",
            "S",
        ]
        coords = [
            [-0.00250959, -0.05817469, -0.02921636],
            [1.70755408, -0.03033788, -0.01382912],
            [2.24317221, -0.05215019, 0.92026728],
            [2.21976393, 0.01718014, -1.27293235],
            [3.27786220, 0.04082146, -1.48539646],
            [1.20867399, 0.04478540, -2.27007793],
            [1.40292257, 0.10591684, -3.33110912],
            [-0.05341046, 0.01577217, -1.74839343],
            [-1.32843436, 0.03545064, -2.45531187],
            [-1.55195156, 0.08743920, -3.80184635],
            [-0.75245172, 0.10267657, -4.52817967],
            [-2.93293778, 0.08408786, -4.13352169],
            [-3.31125108, 0.11340328, -5.14405819],
            [-3.73173288, 0.02741365, -3.03412864],
            [-4.80776535, 0.00535688, -2.99564645],
            [-2.81590978, -0.00516172, -1.58990580],
        ]
        molecule_actual = Molecule(species, coords)
        self.assertEqual(molecule_actual, qcinput_test.molecule)
        rem_actual = {
            "job_type": "opt",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": "75",
            "max_scf_cycles": "300",
            "scf_algorithm": "diis",
            "scf_guess": "sad",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": "14",
        }
        self.assertDictEqual(rem_actual, qcinput_test.rem)
        opt_actual = {"CONSTRAINT": ["tors 6 8 9 10 0.0"]}
        self.assertDictEqual(opt_actual, qcinput_test.opt)
Exemplo n.º 42
0
    def test_multi_job_string(self):
        species = [
            "S",
            "C",
            "H",
            "C",
            "H",
            "C",
            "H",
            "C",
            "C",
            "C",
            "H",
            "C",
            "H",
            "C",
            "H",
            "S",
        ]
        coords = [
            [-0.00250959, -0.05817469, -0.02921636],
            [1.70755408, -0.03033788, -0.01382912],
            [2.24317221, -0.05215019, 0.92026728],
            [2.21976393, 0.01718014, -1.27293235],
            [3.27786220, 0.04082146, -1.48539646],
            [1.20867399, 0.04478540, -2.27007793],
            [1.40292257, 0.10591684, -3.33110912],
            [-0.05341046, 0.01577217, -1.74839343],
            [-1.32843436, 0.03545064, -2.45531187],
            [-1.55195156, 0.08743920, -3.80184635],
            [-0.75245172, 0.10267657, -4.52817967],
            [-2.93293778, 0.08408786, -4.13352169],
            [-3.31125108, 0.11340328, -5.14405819],
            [-3.73173288, 0.02741365, -3.03412864],
            [-4.80776535, 0.00535688, -2.99564645],
            [-2.81590978, -0.00516172, -1.58990580],
        ]
        molecule_1 = Molecule(species, coords)
        rem_1 = {
            "jobtype": "opt",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": "75",
            "max_scf_cycles": "300",
            "scf_algorithm": "diis",
            "scf_guess": "sad",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": "14",
        }
        opt_1 = {"CONSTRAINT": ["tors 6 8 9 10 0.0"]}
        job_1 = QCInput(molecule=molecule_1, rem=rem_1, opt=opt_1)
        molecule_2 = "read"
        rem_2 = {
            "jobtype": "sp",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": "75",
            "max_scf_cycles": "300",
            "scf_algorithm": "diis",
            "scf_guess": "read",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": "14",
        }
        job_2 = QCInput(molecule=molecule_2, rem=rem_2)
        job_list = [job_1, job_2]
        multi_job_str_test = QCInput.multi_job_string(
            job_list=job_list).split("\n")
        multi_job_str_actual_list = [
            "$molecule",
            " 0 1",
            " S     -0.0025095900     -0.0581746900     -0.0292163600",
            " C      1.7075540800     -0.0303378800     -0.0138291200",
            " H      2.2431722100     -0.0521501900      0.9202672800",
            " C      2.2197639300      0.0171801400     -1.2729323500",
            " H      3.2778622000      0.0408214600     -1.4853964600",
            " C      1.2086739900      0.0447854000     -2.2700779300",
            " H      1.4029225700      0.1059168400     -3.3311091200",
            " C     -0.0534104600      0.0157721700     -1.7483934300",
            " C     -1.3284343600      0.0354506400     -2.4553118700",
            " C     -1.5519515600      0.0874392000     -3.8018463500",
            " H     -0.7524517200      0.1026765700     -4.5281796700",
            " C     -2.9329377800      0.0840878600     -4.1335216900",
            " H     -3.3112510800      0.1134032800     -5.1440581900",
            " C     -3.7317328800      0.0274136500     -3.0341286400",
            " H     -4.8077653500      0.0053568800     -2.9956464500",
            " S     -2.8159097800     -0.0051617200     -1.5899058000",
            "$end",
            "$rem",
            "   job_type = opt",
            "   method = wb97m-v",
            "   basis = def2-tzvppd",
            "   gen_scfman = true",
            "   geom_opt_max_cycles = 75",
            "   max_scf_cycles = 300",
            "   scf_algorithm = diis",
            "   scf_guess = sad",
            "   sym_ignore = true",
            "   symmetry = false",
            "   thresh = 14",
            "$end",
            "$opt",
            "CONSTRAINT",
            "   tors 6 8 9 10 0.0",
            "ENDCONSTRAINT",
            "$end",
            "@@@",
            "$molecule",
            " read",
            "$end",
            "$rem",
            "   job_type = opt",
            "   method = wb97m-v",
            "   basis = def2-tzvppd",
            "   gen_scfman = true",
            "   geom_opt_max_cycles = 75",
            "   max_scf_cycles = 300",
            "   scf_algorithm = diis",
            "   scf_guess = sad",
            "   sym_ignore = true",
            "   symmetry = false",
            "   thresh = 14",
            "$end",
        ]

        for i_str in multi_job_str_actual_list:
            self.assertIn(i_str, multi_job_str_test)
Exemplo n.º 43
0
    def test_from_multi_jobs_file(self):
        job_list_test = QCInput.from_multi_jobs_file(
            os.path.join(PymatgenTest.TEST_FILES_DIR, "qchem",
                         "pt_n2_wb97mv_0.0.in"))
        species = [
            "S",
            "C",
            "H",
            "C",
            "H",
            "C",
            "H",
            "C",
            "C",
            "C",
            "H",
            "C",
            "H",
            "C",
            "H",
            "S",
        ]
        coords = [
            [-0.00250959, -0.05817469, -0.02921636],
            [1.70755408, -0.03033788, -0.01382912],
            [2.24317221, -0.05215019, 0.92026728],
            [2.21976393, 0.01718014, -1.27293235],
            [3.27786220, 0.04082146, -1.48539646],
            [1.20867399, 0.04478540, -2.27007793],
            [1.40292257, 0.10591684, -3.33110912],
            [-0.05341046, 0.01577217, -1.74839343],
            [-1.32843436, 0.03545064, -2.45531187],
            [-1.55195156, 0.08743920, -3.80184635],
            [-0.75245172, 0.10267657, -4.52817967],
            [-2.93293778, 0.08408786, -4.13352169],
            [-3.31125108, 0.11340328, -5.14405819],
            [-3.73173288, 0.02741365, -3.03412864],
            [-4.80776535, 0.00535688, -2.99564645],
            [-2.81590978, -0.00516172, -1.58990580],
        ]
        molecule_1_actual = Molecule(species, coords)
        rem_1_actual = {
            "job_type": "opt",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": "75",
            "max_scf_cycles": "300",
            "scf_algorithm": "diis",
            "scf_guess": "sad",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": "14",
        }
        opt_1_actual = {"CONSTRAINT": ["tors 6 8 9 10 0.0"]}
        self.assertEqual(molecule_1_actual, job_list_test[0].molecule)
        self.assertEqual(rem_1_actual, job_list_test[0].rem)
        self.assertEqual(opt_1_actual, job_list_test[0].opt)

        molecule_2_actual = "read"
        rem_2_actual = {
            "job_type": "sp",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": "75",
            "max_scf_cycles": "300",
            "scf_algorithm": "diis",
            "scf_guess": "read",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": "14",
        }
        self.assertEqual(molecule_2_actual, job_list_test[1].molecule)
        self.assertEqual(rem_2_actual, job_list_test[1].rem)
Exemplo n.º 44
0
    def test_FFopt_and_critic(self):
        # location of test files
        test_files = os.path.join(
            module_dir, "..", "..", "test_files", "critic_test_files"
        )
        # define starting molecule and workflow object
        initial_qcin = QCInput.from_file(
            os.path.join(test_files, "FFopt", "mol.qin.orig")
        )
        initial_mol = initial_qcin.molecule

        real_wf = get_wf_FFopt_and_critic(
            molecule=initial_mol,
            suffix="testing",
            qchem_input_params={
                "dft_rung": 4,
                "smd_solvent": "custom",
                "custom_smd": "18.5,1.415,0.00,0.735,20.2,0.00,0.00",
                "overwrite_inputs": {
                    "rem": {"thresh": "14", "scf_guess_always": "True"}
                },
            },
        )
        # use powerup to replace run with fake run
        ref_dirs = {
            "{}:{}".format(
                initial_mol.composition.alphabetical_formula, "FFopt_testing"
            ): os.path.join(test_files, "FFopt"),
            "{}:{}".format(
                initial_mol.composition.alphabetical_formula, "CC2_testing"
            ): os.path.join(test_files, "critic_example"),
        }
        fake_wf = use_fake_qchem(real_wf, ref_dirs)
        self.lp.add_wf(fake_wf)
        rapidfire(
            self.lp,
            fworker=FWorker(
                env={"max_cores": 32, "db_file": os.path.join(db_dir, "db.json")}
            ),
        )

        wf_test = self.lp.get_wf_by_fw_id(1)
        self.assertTrue(all([s == "COMPLETED" for s in wf_test.fw_states.values()]))

        FFopt = self.get_task_collection().find_one(
            {
                "task_label": "{}:{}".format(
                    initial_mol.composition.alphabetical_formula, "FFopt_testing"
                )
            }
        )
        self.assertEqual(FFopt["calcs_reversed"][0]["input"]["smx"]["solvent"], "other")
        self.assertEqual(FFopt["num_frequencies_flattened"], 0)
        FFopt_final_mol = Molecule.from_dict(FFopt["output"]["optimized_molecule"])

        CC2 = self.get_task_collection().find_one(
            {
                "task_label": "{}:{}".format(
                    initial_mol.composition.alphabetical_formula, "CC2_testing"
                )
            }
        )
        CC2_initial_mol = Molecule.from_dict(CC2["input"]["initial_molecule"])

        self.assertEqual(FFopt_final_mol, CC2_initial_mol)
        self.assertEqual(CC2["output"]["job_type"], "sp")
        self.assertEqual(CC2["output"]["final_energy"], -343.4820411597)
        critic2_drone_ref = loadfn(
            os.path.join(test_files, "critic_example", "critic2_drone_ref.json")
        )
        self.assertEqual(CC2["critic2"], critic2_drone_ref)
Exemplo n.º 45
0
 def test_OptFF(self):
     myjob = QCJob.opt_with_frequency_flattener(
         qchem_command="qchem",
         max_cores=32,
         input_file="test.qin",
         output_file="test.qout",
         linked=False,
     )
     expected_next = QCJob(
         qchem_command="qchem",
         max_cores=32,
         multimode="openmp",
         input_file="test.qin",
         output_file="test.qout",
         suffix=".opt_0",
         backup=True,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     expected_next = QCJob(
         qchem_command="qchem",
         max_cores=32,
         multimode="openmp",
         input_file="test.qin",
         output_file="test.qout",
         suffix=".freq_0",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "FF_working/test.qin.freq_0")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "test.qin")).as_dict(),
     )
     expected_next = QCJob(
         qchem_command="qchem",
         max_cores=32,
         multimode="openmp",
         input_file="test.qin",
         output_file="test.qout",
         suffix=".opt_1",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir, "FF_working/test.qin.opt_1")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "test.qin")).as_dict(),
     )
     expected_next = QCJob(
         qchem_command="qchem",
         max_cores=32,
         multimode="openmp",
         input_file="test.qin",
         output_file="test.qout",
         suffix=".freq_1",
         backup=False,
     ).as_dict()
     self.assertEqual(next(myjob).as_dict(), expected_next)
     self.assertEqual(
         QCInput.from_file(
             os.path.join(test_dir,
                          "FF_working/test.qin.freq_1")).as_dict(),
         QCInput.from_file(os.path.join(scr_dir, "test.qin")).as_dict(),
     )
     self.assertRaises(StopIteration, myjob.__next__)
Exemplo n.º 46
0
    def generate_doc(self, dir_name, qcinput_files, qcoutput_files, multirun):
        try:
            fullpath = os.path.abspath(dir_name)
            d = jsanitize(self.additional_fields, strict=True)
            d["schema"] = {
                "code": "atomate",
                "version": QChemDrone.__version__
            }
            d["dir_name"] = fullpath

            # If a saved "orig" input file is present, parse it incase the error handler made changes
            # to the initial input molecule or rem params, which we might want to filter for later
            if len(qcinput_files) > len(qcoutput_files):
                orig_input = QCInput.from_file(os.path.join(dir_name, qcinput_files.pop("orig")))
                d["orig"] = {}
                d["orig"]["molecule"] = orig_input.molecule.as_dict()
                d["orig"]["molecule"]["charge"] = int(d["orig"]["molecule"]["charge"])
                d["orig"]["rem"] = orig_input.rem
                d["orig"]["opt"] = orig_input.opt
                d["orig"]["pcm"] = orig_input.pcm
                d["orig"]["solvent"] = orig_input.solvent
                d["orig"]["smx"] = orig_input.smx

            if multirun:
                d["calcs_reversed"] = self.process_qchem_multirun(
                    dir_name, qcinput_files, qcoutput_files)
            else:
                d["calcs_reversed"] = [
                    self.process_qchemrun(dir_name, taskname,
                                          qcinput_files.get(taskname),
                                          output_filename)
                    for taskname, output_filename in qcoutput_files.items()
                ]

            # reverse the calculations data order so newest calc is first
            d["calcs_reversed"].reverse()

            d["structure_change"] = []
            d["warnings"] = {}
            for entry in d["calcs_reversed"]:
                if "structure_change" in entry and "structure_change" not in d["warnings"]:
                    if entry["structure_change"] != "no_change":
                        d["warnings"]["structure_change"] = True
                if "structure_change" in entry:
                    d["structure_change"].append(entry["structure_change"])
                for key in entry["warnings"]:
                    if key not in d["warnings"]:
                        d["warnings"][key] = True

            d_calc_init = d["calcs_reversed"][-1]
            d_calc_final = d["calcs_reversed"][0]

            d["input"] = {
                "initial_molecule": d_calc_init["initial_molecule"],
                "job_type": d_calc_init["input"]["rem"]["job_type"]
            }
            d["output"] = {
                "initial_molecule": d_calc_final["initial_molecule"],
                "job_type": d_calc_final["input"]["rem"]["job_type"],
                "mulliken": d_calc_final["Mulliken"][-1]
            }
            if "RESP" in d_calc_final:
                d["output"]["resp"] = d_calc_final["RESP"][-1]
            elif "ESP" in d_calc_final:
                d["output"]["esp"] = d_calc_final["ESP"][-1]

            if d["output"]["job_type"] == "opt" or d["output"]["job_type"] == "optimization":
                if "molecule_from_optimized_geometry" in d_calc_final:
                    d["output"]["optimized_molecule"] = d_calc_final[
                        "molecule_from_optimized_geometry"]
                    d["output"]["final_energy"] = d_calc_final["final_energy"]
                else:
                    d["output"]["final_energy"] = "unstable"
                if d_calc_final["opt_constraint"]:
                    d["output"]["constraint"] = [
                        d_calc_final["opt_constraint"][0],
                        float(d_calc_final["opt_constraint"][6])
                    ]
            if d["output"]["job_type"] == "freq" or d["output"]["job_type"] == "frequency":
                d["output"]["frequencies"] = d_calc_final["frequencies"]
                d["output"]["enthalpy"] = d_calc_final["total_enthalpy"]
                d["output"]["entropy"] = d_calc_final["total_entropy"]
                if d["input"]["job_type"] == "opt" or d["input"]["job_type"] == "optimization":
                    d["output"]["optimized_molecule"] = d_calc_final[
                        "initial_molecule"]
                    d["output"]["final_energy"] = d["calcs_reversed"][1][
                        "final_energy"]

            if "final_energy" not in d["output"]:
                if d_calc_final["final_energy"] != None:
                    d["output"]["final_energy"] = d_calc_final["final_energy"]
                else:
                    d["output"]["final_energy"] = d_calc_final["SCF"][-1][-1][0]
                # else:
                #     print(d_calc_final)

            if d_calc_final["completion"]:
                total_cputime = 0.0
                total_walltime = 0.0
                for calc in d["calcs_reversed"]:
                    if calc["walltime"] is not None:
                        total_walltime += calc["walltime"]
                    if calc["cputime"] is not None:
                        total_cputime += calc["cputime"]
                d["walltime"] = total_walltime
                d["cputime"] = total_cputime
            else:
                d["walltime"] = None
                d["cputime"] = None

            comp = d["output"]["initial_molecule"].composition
            d["formula_pretty"] = comp.reduced_formula
            d["formula_anonymous"] = comp.anonymized_formula
            d["formula_alphabetical"] = comp.alphabetical_formula
            d["chemsys"] = "-".join(sorted(set(d_calc_final["species"])))
            if d_calc_final["point_group"] != None:
                d["pointgroup"] = d_calc_final["point_group"]
            else:
                try:
                    d["pointgroup"] = PointGroupAnalyzer(d["output"]["initial_molecule"]).sch_symbol
                except ValueError:
                    d["pointgroup"] = "PGA_error"

            bb = BabelMolAdaptor(d["output"]["initial_molecule"])
            pbmol = bb.pybel_mol
            smiles = pbmol.write(str("smi")).split()[0]
            d["smiles"] = smiles

            d["state"] = "successful" if d_calc_final["completion"] else "unsuccessful"
            if "special_run_type" in d:
                if d["special_run_type"] == "frequency_flattener":
                    opt_traj = []
                    for entry in d["calcs_reversed"]:
                        if entry["input"]["rem"]["job_type"] == "opt" or entry["input"]["rem"]["job_type"] == "optimization":
                            doc = {"initial": {}, "final": {}}
                            doc["initial"]["molecule"] = entry["initial_molecule"]
                            doc["final"]["molecule"] = entry["molecule_from_last_geometry"]
                            doc["initial"]["total_energy"] = entry["energy_trajectory"][0]
                            doc["final"]["total_energy"] = entry["energy_trajectory"][-1]
                            doc["initial"]["scf_energy"] = entry["SCF"][0][-1][0]
                            doc["final"]["scf_energy"] = entry["SCF"][-1][-1][0]
                            doc["structure_change"] = entry["structure_change"]
                            opt_traj.append(doc)
                    opt_traj.reverse()
                    opt_trajectory = {"trajectory": opt_traj, "structure_change": [[ii, entry["structure_change"]] for ii,entry in enumerate(opt_traj)], "energy_increase": []}
                    for ii, entry in enumerate(opt_traj):
                        if entry["final"]["total_energy"] > entry["initial"]["total_energy"]:
                            opt_trajectory["energy_increase"].append([ii, entry["final"]["total_energy"]-entry["initial"]["total_energy"]])
                        if ii != 0:
                            if entry["final"]["total_energy"] > opt_traj[ii-1]["final"]["total_energy"]:
                                opt_trajectory["energy_increase"].append([ii-1, ii, entry["final"]["total_energy"]-opt_traj[ii-1]["final"]["total_energy"]])
                            struct_change = check_for_structure_changes(opt_traj[ii-1]["final"]["molecule"], entry["final"]["molecule"])
                            if struct_change != entry["structure_change"]:
                                opt_trajectory["structure_change"].append([ii-1, ii, struct_change])
                                d["warnings"]["between_iteration_structure_change"] = True
                    if "linked" in d:
                        if d["linked"] == True:
                            opt_trajectory["discontinuity"] = {"structure": [], "scf_energy": [], "total_energy": []}
                            for ii, entry in enumerate(opt_traj):
                                if ii != 0:
                                    if entry["initial"]["molecule"] != opt_traj[ii-1]["final"]["molecule"]:
                                        opt_trajectory["discontinuity"]["structure"].append([ii-1,ii])
                                        d["warnings"]["linked_structure_discontinuity"] = True
                                    if entry["initial"]["total_energy"] != opt_traj[ii-1]["final"]["total_energy"]:
                                        opt_trajectory["discontinuity"]["total_energy"].append([ii-1,ii])
                                    if entry["initial"]["scf_energy"] != opt_traj[ii-1]["final"]["scf_energy"]:
                                        opt_trajectory["discontinuity"]["scf_energy"].append([ii-1,ii])
                    d["opt_trajectory"] = opt_trajectory

                    if d["state"] == "successful":
                        orig_num_neg_freq = sum(1 for freq in d["calcs_reversed"][-2]["frequencies"] if freq < 0)
                        orig_energy = d_calc_init["final_energy"]
                        final_num_neg_freq = sum(1 for freq in d_calc_final["frequencies"] if freq < 0)
                        final_energy = d["calcs_reversed"][1]["final_energy"]
                        d["num_frequencies_flattened"] = orig_num_neg_freq - final_num_neg_freq
                        if final_num_neg_freq > 0: # If a negative frequency remains,
                            # and it's too large to ignore,
                            if final_num_neg_freq > 1 or abs(d["output"]["frequencies"][0]) >= 15.0:
                                d["state"] = "unsuccessful" # then the flattening was unsuccessful
                        if final_energy > orig_energy:
                            d["warnings"]["energy_increased"] = True

            d["last_updated"] = datetime.datetime.utcnow()
            return d

        except Exception:
            logger.error(traceback.format_exc())
            logger.error("Error in " + os.path.abspath(dir_name) + ".\n" +
                         traceback.format_exc())
            raise
Exemplo n.º 47
0
    def test_double_FF_opt(self):
        # location of test files
        test_double_FF_files = os.path.join(module_dir, "..", "..",
                                            "test_files", "double_FF_wf")
        # define starting molecule and workflow object
        initial_qcin = QCInput.from_file(
            os.path.join(test_double_FF_files, "block", "launcher_first",
                         "mol.qin.opt_0"))
        initial_mol = initial_qcin.molecule

        real_wf = get_wf_double_FF_opt(
            molecule=initial_mol,
            pcm_dielectric=10.0,
            qchem_input_params={
                "basis_set": "6-311++g**",
                "scf_algorithm": "diis",
                "overwrite_inputs": {
                    "rem": {
                        "sym_ignore": "true"
                    }
                },
            },
        )
        # use powerup to replace run with fake run
        ref_dirs = {
            "first_FF_no_pcm":
            os.path.join(test_double_FF_files, "block", "launcher_first"),
            "second_FF_with_pcm":
            os.path.join(test_double_FF_files, "block", "launcher_second"),
        }
        fake_wf = use_fake_qchem(real_wf, ref_dirs)
        self.lp.add_wf(fake_wf)
        rapidfire(
            self.lp,
            fworker=FWorker(env={
                "max_cores": 32,
                "db_file": os.path.join(db_dir, "db.json")
            }),
        )

        wf_test = self.lp.get_wf_by_fw_id(1)
        self.assertTrue(
            all([s == "COMPLETED" for s in wf_test.fw_states.values()]))

        first_FF = self.get_task_collection().find_one(
            {"task_label": "first_FF_no_pcm"})
        self.assertEqual(first_FF["calcs_reversed"][0]["input"]["solvent"],
                         None)
        self.assertEqual(first_FF["num_frequencies_flattened"], 1)
        first_FF_final_mol = Molecule.from_dict(
            first_FF["output"]["optimized_molecule"])

        second_FF = self.get_task_collection().find_one(
            {"task_label": "second_FF_with_pcm"})
        self.assertEqual(second_FF["calcs_reversed"][0]["input"]["solvent"],
                         {"dielectric": "10.0"})
        self.assertEqual(second_FF["num_frequencies_flattened"], 1)
        second_FF_initial_mol = Molecule.from_dict(
            second_FF["input"]["initial_molecule"])

        self.assertEqual(first_FF_final_mol, second_FF_initial_mol)
Exemplo n.º 48
0
    def test_torsion_potential(self):
        # location of test files
        test_tor_files = os.path.join(module_dir, "..", "..", "test_files",
                                      "torsion_wf")
        # define starting molecule and torsion potential workflow object
        initial_qcin = QCInput.from_file(
            os.path.join(test_tor_files, "initial_opt", "mol.qin"))
        initial_mol = initial_qcin.molecule
        atom_indexes = [6, 8, 9, 10]
        angles = [0.0, 90.0, 180.0]
        rem = []
        # add the first rem section
        rem.append({
            "jobtype": "opt",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "gen_scfman": "true",
            "geom_opt_max_cycles": 75,
            "max_scf_cycles": 300,
            "scf_algorithm": "diis",
            "scf_guess": "sad",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": 14
        })

        # the second rem section
        rem.append({
            "jobtype": "opt",
            "method": "wb97m-v",
            "basis": "def2-tzvppd",
            "geom_opt_max_cycles": 75,
            "max_scf_cycles": 300,
            "scf_algorithm": "diis",
            "scf_guess": "sad",
            "sym_ignore": "true",
            "symmetry": "false",
            "thresh": 14
        })

        real_wf = get_wf_torsion_potential(molecule=initial_mol,
                                           atom_indexes=atom_indexes,
                                           angles=angles,
                                           rem=rem,
                                           db_file=">>db_file<<")
        # use powerup to replace run with fake run
        # def ref_dirs
        ref_dirs = {
            "initial_opt": os.path.join(test_tor_files, "initial_opt"),
            "opt_0": os.path.join(test_tor_files, "opt_0"),
            "opt_90": os.path.join(test_tor_files, "opt_90"),
            "opt_180": os.path.join(test_tor_files, "opt_180")
        }
        fake_wf = use_fake_qchem(real_wf, ref_dirs)

        self.lp.add_wf(fake_wf)
        rapidfire(
            self.lp,
            fworker=FWorker(env={"db_file": os.path.join(db_dir, "db.json")}))

        wf_test = self.lp.get_wf_by_fw_id(1)
        self.assertTrue(
            all([s == "COMPLETED" for s in wf_test.fw_states.values()]))

        # Checking of the inputs happens in fake_run_qchem so there is no point to retest the inputs
        # Check the output info that gets inserted in the DB
        init_opt = self.get_task_collection().find_one(
            {"task_label": "initial_opt"})
        init_opt_final_mol = Molecule.from_dict(
            init_opt["output"]["optimized_molecule"])
        init_opt_final_e = init_opt["output"]["final_energy"]
        # parse output file
        act_init_opt_out = QCOutput(
            os.path.join(test_tor_files, "initial_opt", "mol.qout"))
        act_init_opt_mol = act_init_opt_out.data[
            "molecule_from_optimized_geometry"]
        act_init_opt_final_e = act_init_opt_out.data["final_energy"]

        np.testing.assert_equal(act_init_opt_mol.species,
                                init_opt_final_mol.species)
        np.testing.assert_allclose(act_init_opt_mol.cart_coords,
                                   init_opt_final_mol.cart_coords,
                                   atol=0.0001)
        np.testing.assert_equal(act_init_opt_final_e, init_opt_final_e)

        # Optimization of 0 torsion
        opt_0 = self.get_task_collection().find_one({"task_label": "opt_0"})
        opt_0_final_mol = Molecule.from_dict(
            opt_0["output"]["optimized_molecule"])
        opt_0_final_e = opt_0["output"]["final_energy"]
        # parse output file
        act_opt_0_out = QCOutput(
            os.path.join(test_tor_files, "opt_0", "mol.qout"))
        act_opt_0_mol = act_opt_0_out.data["molecule_from_optimized_geometry"]
        act_opt_0_final_e = act_opt_0_out.data["final_energy"]

        np.testing.assert_equal(act_opt_0_mol.species, opt_0_final_mol.species)
        np.testing.assert_allclose(act_opt_0_mol.cart_coords,
                                   opt_0_final_mol.cart_coords,
                                   atol=0.0001)
        np.testing.assert_equal(act_opt_0_final_e, opt_0_final_e)

        # Optimization of 90 torsion
        opt_90 = self.get_task_collection().find_one({"task_label": "opt_90"})
        opt_90_final_mol = Molecule.from_dict(
            opt_90["output"]["optimized_molecule"])
        opt_90_final_e = opt_90["output"]["final_energy"]
        # parse output file
        act_opt_90_out = QCOutput(
            os.path.join(test_tor_files, "opt_90", "mol.qout"))
        act_opt_90_mol = act_opt_90_out.data[
            "molecule_from_optimized_geometry"]
        act_opt_90_final_e = act_opt_90_out.data["final_energy"]

        np.testing.assert_equal(act_opt_90_mol.species,
                                opt_90_final_mol.species)
        np.testing.assert_allclose(act_opt_90_mol.cart_coords,
                                   opt_90_final_mol.cart_coords,
                                   atol=0.0001)
        np.testing.assert_equal(act_opt_90_final_e, opt_90_final_e)

        # Optimization of 180 torsion
        opt_180 = self.get_task_collection().find_one(
            {"task_label": "opt_180"})
        opt_180_final_mol = Molecule.from_dict(
            opt_180["output"]["optimized_molecule"])
        opt_180_final_e = opt_180["output"]["final_energy"]
        # parse output file
        act_opt_180_out = QCOutput(
            os.path.join(test_tor_files, "opt_180", "mol.qout"))
        act_opt_180_mol = act_opt_180_out.data[
            "molecule_from_optimized_geometry"]
        act_opt_180_final_e = act_opt_180_out.data["final_energy"]

        np.testing.assert_equal(act_opt_180_mol.species,
                                opt_180_final_mol.species)
        np.testing.assert_allclose(act_opt_180_mol.cart_coords,
                                   opt_180_final_mol.cart_coords,
                                   atol=0.0001)
        np.testing.assert_equal(act_opt_180_final_e, opt_180_final_e)
Exemplo n.º 49
0
    def opt_with_frequency_flattener(cls,
                                     qchem_command,
                                     multimode="openmp",
                                     input_file="mol.qin",
                                     output_file="mol.qout",
                                     qclog_file="mol.qclog",
                                     sp_params=None,
                                     max_iterations=10,
                                     max_molecule_perturb_scale=0.3,
                                     check_connectivity=True,
                                     **QCJob_kwargs):
        """
        Optimize a structure and calculate vibrational frequencies to check if the
        structure is in a true minima. If a frequency is negative, iteratively
        perturbe the geometry, optimize, and recalculate frequencies until all are
        positive, aka a true minima has been found.

        Args:
            qchem_command (str): Command to run QChem.
            multimode (str): Parallelization scheme, either openmp or mpi.
            input_file (str): Name of the QChem input file.
            output_file (str): Name of the QChem output file
            qclog_file (str): Name of the QChem log file.
            sp_params (dict): Dictionary with any special parameters for the single-point calculation.
            max_iterations (int): Number of perturbation -> optimization -> frequency
                iterations to perform. Defaults to 10.
            max_molecule_perturb_scale (float): The maximum scaled perturbation that
                can be applied to the molecule. Defaults to 0.3.
            check_connectivity (bool): Whether to check differences in connectivity
                introduced by structural perturbation. Defaults to True.
            **QCJob_kwargs: Passthrough kwargs to QCJob. See
                :class:`custodian.qchem.new_jobs.QCJob`.
        """

        min_molecule_perturb_scale = 0.1
        scale_grid = 10
        perturb_scale_grid = (
            max_molecule_perturb_scale - min_molecule_perturb_scale
        ) / scale_grid

        if not os.path.exists(input_file):
            raise AssertionError('Input file must be present!')
        orig_opt_input = QCInput.from_file(input_file)
        orig_opt_rem = copy.deepcopy(orig_opt_input.rem)
        orig_freq_rem = copy.deepcopy(orig_opt_input.rem)
        orig_freq_rem["job_type"] = "freq"
        first = True
        reversed_direction = False
        num_neg_freqs = []

        for ii in range(max_iterations):
            yield (QCJob(
                qchem_command=qchem_command,
                multimode=multimode,
                input_file=input_file,
                output_file=output_file,
                qclog_file=qclog_file,
                suffix=".opt_" + str(ii),
                backup=first,
                **QCJob_kwargs))
            first = False
            opt_outdata = QCOutput(output_file + ".opt_" + str(ii)).data
            if opt_outdata["structure_change"] == "unconnected_fragments" and not opt_outdata["completion"]:
                print("Unstable molecule broke into unconnected fragments which failed to optimize! Exiting...")
                break
            else:
                freq_QCInput = QCInput(
                    molecule=opt_outdata.get("molecule_from_optimized_geometry"),
                    rem=orig_freq_rem,
                    opt=orig_opt_input.opt,
                    pcm=orig_opt_input.pcm,
                    solvent=orig_opt_input.solvent,
                    smx=orig_opt_input.smx)
                freq_QCInput.write_file(input_file)
                yield (QCJob(
                    qchem_command=qchem_command,
                    multimode=multimode,
                    input_file=input_file,
                    output_file=output_file,
                    qclog_file=qclog_file,
                    suffix=".freq_" + str(ii),
                    backup=first,
                    **QCJob_kwargs))
                outdata = QCOutput(output_file + ".freq_" + str(ii)).data
                errors = outdata.get("errors")
                if len(errors) != 0:
                    raise AssertionError('No errors should be encountered while flattening frequencies!')
                if outdata.get('frequencies')[0] > 0.0:
                    print("All frequencies positive!")
                    break
                else:
                    num_neg_freqs += [sum(1 for freq in outdata.get('frequencies') if freq < 0)]
                    if len(num_neg_freqs) > 1:
                        if num_neg_freqs[-1] == num_neg_freqs[-2] and not reversed_direction:
                            reversed_direction = True
                        elif num_neg_freqs[-1] == num_neg_freqs[-2] and reversed_direction:
                            if len(num_neg_freqs) < 3:
                                raise AssertionError("ERROR: This should only be possible after at least three frequency flattening iterations! Exiting...")
                            else:
                                raise Exception("ERROR: Reversing the perturbation direction still could not flatten any frequencies. Exiting...")
                        elif num_neg_freqs[-1] != num_neg_freqs[-2] and reversed_direction:
                            reversed_direction = False

                    negative_freq_vecs = outdata.get("frequency_mode_vectors")[0]
                    structure_successfully_perturbed = False

                    for molecule_perturb_scale in np.arange(
                            max_molecule_perturb_scale, min_molecule_perturb_scale,
                            -perturb_scale_grid):
                        new_coords = perturb_coordinates(
                            old_coords=outdata.get("initial_geometry"),
                            negative_freq_vecs=negative_freq_vecs,
                            molecule_perturb_scale=molecule_perturb_scale,
                            reversed_direction=reversed_direction)
                        new_molecule = Molecule(
                            species=outdata.get('species'),
                            coords=new_coords,
                            charge=outdata.get('charge'),
                            spin_multiplicity=outdata.get('multiplicity'))
                        if check_connectivity:
                            old_molgraph = MoleculeGraph.with_local_env_strategy(outdata.get("initial_molecule"),
                                                                                 OpenBabelNN(),
                                                                                 reorder=False,
                                                                                 extend_structure=False)
                            new_molgraph = MoleculeGraph.with_local_env_strategy(new_molecule,
                                                                                 OpenBabelNN(),
                                                                                 reorder=False,
                                                                                 extend_structure=False)
                            if old_molgraph.isomorphic_to(new_molgraph):
                                structure_successfully_perturbed = True
                                break
                    if not structure_successfully_perturbed:
                        raise Exception(
                            "ERROR: Unable to perturb coordinates to remove negative frequency without changing the connectivity! Exiting..."
                        )

                    new_opt_QCInput = QCInput(
                        molecule=new_molecule,
                        rem=orig_opt_rem,
                        opt=orig_opt_input.opt,
                        pcm=orig_opt_input.pcm,
                        solvent=orig_opt_input.solvent,
                        smx=orig_opt_input.smx)
                    new_opt_QCInput.write_file(input_file)

        if sp_params is not None:
            sp_input = QCInput(
                molecule=opt_outdata.get("molecule_from_optimized_geometry"),
                rem=sp_params.get("rem", {"method": "wb97x-d",
                                          "basis": "6-311++g(d,p)"}),
                opt=sp_params.get("opt", None),
                pcm=sp_params.get("pcm", None),
                solvent=sp_params.get("solvent", None),
                smx=sp_params.get("smx", None))
        else:
            orig_sp_rem = copy.deepcopy(orig_opt_input.rem)
            orig_sp_rem["job_type"] = "sp"
            sp_input = QCInput(
                molecule=opt_outdata.get("molecule_from_optimized_geometry"),
                rem=orig_sp_rem,
                opt=orig_opt_input.opt,
                pcm=orig_opt_input.pcm,
                solvent=orig_opt_input.solvent,
                smx=orig_opt_input.smx)

        sp_input.write_file(input_file)
        yield (QCJob(qchem_command=qchem_command,
                     multimode=multimode,
                     input_file=input_file,
                     output_file=output_file,
                     qclog_file=qclog_file,
                     suffix=".sp",
                     **QCJob_kwargs))