Repository: qubekit/QUBEKit (language: python)

Issue:
No molecule conformer could be optimised to GAU TIGHT
Sorry for opening yet another issue, I would not (yet) have done this if this was only with one molecule 
but I even get the following error

```
Traceback (most recent call last):
  File "/work/scratch/b_mayer/miniconda3/envs/qubekit/lib/python3.10/site-packages/qubekit/workflow/workflow.py", line 335, in _run_stage
    result_mol = stage.run(
  File "/work/scratch/b_mayer/miniconda3/envs/qubekit/lib/python3.10/site-packages/qubekit/workflow/helper_stages.py", line 144, in run
    return self._run_qm_opt(
  File "/work/scratch/b_mayer/miniconda3/envs/qubekit/lib/python3.10/site-packages/qubekit/workflow/helper_stages.py", line 206, in _run_qm_opt
    raise GeometryOptimisationError(
qubekit.utils.exceptions.GeometryOptimisationError: No molecule conformer could be optimised to GAU TIGHT
```

while running one of the examples:

```
qubekit run -sm CO -n methanol
```

and as mentioned I also got this with one other molecule. So I have the feeling that something is wrong and this will 
always be triggered. 
Hope you have any ideas on how to solve this, I would really like to be able to use the parameters from your very nice toolkit in my MD simulations.


Discussion / hints:
Hi @entropybit ,

The error reporting is not great at that stage so its not always clear what has happened, there should be within the optimisation folder a json file called `result.json` which should have more information could you post that here it should help diagnose what's gone wrong. 
Sure, I assume you meant workflow_result.json:

```json
{
  "version": "2.1.1",
  "creation_date": "2023_03_07",
  "modified_date": "2023_03_07",
  "input_molecule": {
    "atoms": [
      {
        "atomic_number": 6,
        "atom_index": 0,
        "atom_name": "C1",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          1,
          2,
          3,
          4
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 8,
        "atom_index": 1,
        "atom_name": "O1",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0,
          5
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 2,
        "atom_name": "H1",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 3,
        "atom_name": "H2",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 4,
        "atom_name": "H3",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 5,
        "atom_name": "H4",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          1
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      }
    ],
    "bonds": [
      {
        "atom1_index": 0,
        "atom2_index": 1,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 0,
        "atom2_index": 2,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 0,
        "atom2_index": 3,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 0,
        "atom2_index": 4,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 1,
        "atom2_index": 5,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      }
    ],
    "coordinates": [
      -0.35075347726940603,
      -0.005072983373261349,
      -0.018028132595706226,
      0.9643704239531457,
      -0.36240238530810037,
      -0.2601199990033363,
      -0.5984566510608368,
      0.061649323412126845,
      1.0523734303936758,
      -0.9862545465164104,
      -0.8156752796030018,
      -0.4626034355081824,
      -0.6578568673402398,
      0.9268983092448698,
      -0.5010770555240878,
      1.6289511182337482,
      0.19460301562736884,
      0.18945519223763826
    ],
    "multiplicity": 1,
    "name": "methanol",
    "provenance": {
      "creator": "QUBEKit",
      "version": "2.1.1",
      "routine": [
        "QUBEKit.ligand.from_rdkit",
        "QUBEKit.ligand.from_smiles",
        "CO"
      ]
    },
    "extra_sites": {
      "sites": {}
    },
    "BondForce": {
      "type": "HarmonicBondForce",
      "parameters": null
    },
    "AngleForce": {
      "type": "HarmonicAngleForce",
      "parameters": null
    },
    "TorsionForce": {
      "type": "PeriodicTorsionForce",
      "parameters": null,
      "ordering": "default"
    },
    "ImproperTorsionForce": {
      "type": "PeriodicImproperTorsionForce",
      "parameters": null
    },
    "RBTorsionForce": {
      "type": "RBProperTorsionForce",
      "parameters": null
    },
    "ImproperRBTorsionForce": {
      "type": "RBImproperTorsionForce",
      "parameters": null
    },
    "NonbondedForce": {
      "type": "NonbondedForce",
      "parameters": null,
      "coulomb14scale": 0.8333333333,
      "lj14scale": 0.5,
      "combination": "amber"
    },
    "chargemol_coords": null,
    "hessian": null,
    "qm_scans": null,
    "wbo": null
  },
  "qc_spec": {
    "program": "gaussian",
    "basis": "6-311++G(d,p)",
    "method": "wb97x-d",
    "td_settings": null
  },
  "local_resources": {
    "cores": 96,
    "memory": 200
  },
  "current_molecule": {
    "atoms": [
      {
        "atomic_number": 6,
        "atom_index": 0,
        "atom_name": "C1",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          1,
          2,
          3,
          4
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 8,
        "atom_index": 1,
        "atom_name": "O1",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0,
          5
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 2,
        "atom_name": "H1",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 3,
        "atom_name": "H2",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 4,
        "atom_name": "H3",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          0
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      },
      {
        "atomic_number": 1,
        "atom_index": 5,
        "atom_name": "H4",
        "formal_charge": 0,
        "aromatic": false,
        "stereochemistry": null,
        "bonds": [
          1
        ],
        "aim": {
          "volume": null,
          "charge": null,
          "c8": null
        },
        "dipole": null,
        "quadrupole": null,
        "cloud_pen": null
      }
    ],
    "bonds": [
      {
        "atom1_index": 0,
        "atom2_index": 1,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 0,
        "atom2_index": 2,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 0,
        "atom2_index": 3,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 0,
        "atom2_index": 4,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      },
      {
        "atom1_index": 1,
        "atom2_index": 5,
        "bond_order": 1.0,
        "aromatic": false,
        "stereochemistry": null
      }
    ],
    "coordinates": [
      -0.35075347726940603,
      -0.005072983373261349,
      -0.018028132595706226,
      0.9643704239531457,
      -0.36240238530810037,
      -0.2601199990033363,
      -0.5984566510608368,
      0.061649323412126845,
      1.0523734303936758,
      -0.9862545465164104,
      -0.8156752796030018,
      -0.4626034355081824,
      -0.6578568673402398,
      0.9268983092448698,
      -0.5010770555240878,
      1.6289511182337482,
      0.19460301562736884,
      0.18945519223763826
    ],
    "multiplicity": 1,
    "name": "methanol",
    "provenance": {
      "creator": "QUBEKit",
      "version": "2.1.1",
      "routine": [
        "QUBEKit.ligand.from_rdkit",
        "QUBEKit.ligand.from_smiles",
        "CO"
      ]
    },
    "extra_sites": {
      "sites": {}
    },
    "BondForce": {
      "type": "HarmonicBondForce",
      "parameters": [
        {
          "type": "HarmonicBondParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            0,
            1
          ],
          "length": 0.14293303934379997,
          "k": 277982.70612434536
        },
        {
          "type": "HarmonicBondParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            0,
            2
          ],
          "length": 0.10939105249969999,
          "k": 315503.50521639985
        },
        {
          "type": "HarmonicBondParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            0,
            3
          ],
          "length": 0.10939105249969999,
          "k": 315503.50521639985
        },
        {
          "type": "HarmonicBondParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            0,
            4
          ],
          "length": 0.10939105249969999,
          "k": 315503.50521639985
        },
        {
          "type": "HarmonicBondParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            1,
            5
          ],
          "length": 0.09713231822139,
          "k": 464991.4883167736
        }
      ]
    },
    "AngleForce": {
      "type": "HarmonicAngleForce",
      "parameters": [
        {
          "type": "HarmonicAngleParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            0,
            1,
            5
          ],
          "k": 562.7562748394744,
          "angle": 1.9249208206429511
        },
        {
          "type": "HarmonicAngleParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            1,
            0,
            2
          ],
          "k": 415.19503141569464,
          "angle": 1.9836878140552876
        },
        {
          "type": "HarmonicAngleParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            1,
            0,
            3
          ],
          "k": 415.19503141569464,
          "angle": 1.9836878140552876
        },
        {
          "type": "HarmonicAngleParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            1,
            0,
            4
          ],
          "k": 415.19503141569464,
          "angle": 1.9836878140552876
        },
        {
          "type": "HarmonicAngleParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            2,
            0,
            3
          ],
          "k": 278.45479940981784,
          "angle": 1.9948080932715693
        },
        {
          "type": "HarmonicAngleParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            2,
            0,
            4
          ],
          "k": 278.45479940981784,
          "angle": 1.9948080932715693
        },
        {
          "type": "HarmonicAngleParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            3,
            0,
            4
          ],
          "k": 278.45479940981784,
          "angle": 1.9948080932715693
        }
      ]
    },
    "TorsionForce": {
      "type": "PeriodicTorsionForce",
      "parameters": [
        {
          "type": "PeriodicTorsionParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            2,
            0,
            1,
            5
          ],
          "k1": 0,
          "k2": 0,
          "k3": 0.9743050933247757,
          "k4": 0,
          "periodicity1": 1,
          "periodicity2": 2,
          "periodicity3": 3,
          "periodicity4": 4,
          "phase1": 0,
          "phase2": 3.141592653589793,
          "phase3": 0.0,
          "phase4": 3.141592653589793
        },
        {
          "type": "PeriodicTorsionParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            3,
            0,
            1,
            5
          ],
          "k1": 0,
          "k2": 0,
          "k3": 0.9743050933247757,
          "k4": 0,
          "periodicity1": 1,
          "periodicity2": 2,
          "periodicity3": 3,
          "periodicity4": 4,
          "phase1": 0,
          "phase2": 3.141592653589793,
          "phase3": 0.0,
          "phase4": 3.141592653589793
        },
        {
          "type": "PeriodicTorsionParameter",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            4,
            0,
            1,
            5
          ],
          "k1": 0,
          "k2": 0,
          "k3": 0.9743050933247757,
          "k4": 0,
          "periodicity1": 1,
          "periodicity2": 2,
          "periodicity3": 3,
          "periodicity4": 4,
          "phase1": 0,
          "phase2": 3.141592653589793,
          "phase3": 0.0,
          "phase4": 3.141592653589793
        }
      ],
      "ordering": "smirnoff"
    },
    "ImproperTorsionForce": {
      "type": "PeriodicImproperTorsionForce",
      "parameters": []
    },
    "RBTorsionForce": {
      "type": "RBProperTorsionForce",
      "parameters": []
    },
    "ImproperRBTorsionForce": {
      "type": "RBImproperTorsionForce",
      "parameters": []
    },
    "NonbondedForce": {
      "type": "NonbondedForce",
      "parameters": [
        {
          "type": "LennardJones612",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            0
          ],
          "charge": 0.11669999999999998,
          "epsilon": 0.4577296,
          "sigma": 0.33996695084235345
        },
        {
          "type": "LennardJones612",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            1
          ],
          "charge": -0.5988,
          "epsilon": 0.8803136,
          "sigma": 0.30664733878390477
        },
        {
          "type": "LennardJones612",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            2
          ],
          "charge": 0.02869999999999999,
          "epsilon": 0.06568879999999999,
          "sigma": 0.24713530441213008
        },
        {
          "type": "LennardJones612",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            3
          ],
          "charge": 0.02869999999999999,
          "epsilon": 0.06568879999999999,
          "sigma": 0.24713530441213008
        },
        {
          "type": "LennardJones612",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            4
          ],
          "charge": 0.02869999999999999,
          "epsilon": 0.06568879999999999,
          "sigma": 0.24713530441213008
        },
        {
          "type": "LennardJones612",
          "attributes": [],
          "parameter_eval": [],
          "atoms": [
            5
          ],
          "charge": 0.396,
          "epsilon": 0.0002204968,
          "sigma": 0.05345392308842035
        }
      ],
      "coulomb14scale": 0.8333333333,
      "lj14scale": 0.5,
      "combination": "amber"
    },
    "chargemol_coords": null,
    "hessian": null,
    "qm_scans": null,
    "wbo": null
  },
  "results": {
    "parametrisation": {
      "stage": "OpenFF",
      "stage_settings": {
        "type": "OpenFF",
        "force_field": "openff_unconstrained-1.3.0.offxml"
      },
      "status": "done",
      "error": null
    },
    "optimisation": {
      "stage": "Optimiser",
      "stage_settings": {
        "type": "Optimiser",
        "pre_optimisation_method": "gfn2xtb",
        "seed_conformers": 40,
        "maxiter": 350,
        "convergence_criteria": "GAU_TIGHT"
      },
      "status": "error",
      "error": [
        "  File \"/work/scratch/b_mayer/miniconda3/envs/qubekit/lib/python3.10/site-packages/qubekit/workflow/workflow.py\", line 335, in _run_stage\n    result_mol = stage.run(\n",
        "  File \"/work/scratch/b_mayer/miniconda3/envs/qubekit/lib/python3.10/site-packages/qubekit/workflow/helper_stages.py\", line 144, in run\n    return self._run_qm_opt(\n",
        "  File \"/work/scratch/b_mayer/miniconda3/envs/qubekit/lib/python3.10/site-packages/qubekit/workflow/helper_stages.py\", line 206, in _run_qm_opt\n    raise GeometryOptimisationError(\n"
      ]
    },
    "hessian": {
      "stage": "Hessian",
      "stage_settings": {
        "type": "Hessian"
      },
      "status": "waiting",
      "error": null
    },
    "charges": {
      "stage": "DDECCharges",
      "stage_settings": {
        "type": "DDECCharges",
        "solvent_settings": {
          "program": "gaussian",
          "solver_type": "IPCM",
          "epsilon": 4.0,
          "volume_contour": 0.0004
        },
        "program": "gaussian",
        "basis": null,
        "method": null,
        "td_settings": null,
        "ddec_version": 6
      },
      "status": "waiting",
      "error": null
    },
    "virtual_sites": {
      "stage": null,
      "stage_settings": null,
      "status": "skip",
      "error": null
    },
    "non_bonded": {
      "stage": "LennardJones612",
      "stage_settings": {
        "type": "LennardJones612",
        "lj_on_polar_h": true,
        "free_parameters": {
          "H": {
            "v_free": 7.6,
            "b_free": 6.5,
            "r_free": 1.738
          },
          "X": {
            "v_free": 7.6,
            "b_free": 6.5,
            "r_free": 1.083
          },
          "C": {
            "v_free": 34.4,
            "b_free": 46.6,
            "r_free": 2.008
          },
          "N": {
            "v_free": 25.9,
            "b_free": 24.2,
            "r_free": 1.765
          },
          "O": {
            "v_free": 22.1,
            "b_free": 15.6,
            "r_free": 1.499
          }
        },
        "alpha": 1.0,
        "beta": 0.0
      },
      "status": "waiting",
      "error": null
    },
    "bonded_parameters": {
      "stage": "ModSeminario",
      "stage_settings": {
        "type": "ModSeminario",
        "vibrational_scaling": 1.0
      },
      "status": "waiting",
      "error": null
    },
    "torsion_scanner": {
      "stage": "TorsionScan1D",
      "stage_settings": {
        "type": "TorsionScan1D",
        "special_torsions": [],
        "default_scan_range": [
          -165,
          180
        ],
        "avoided_torsions": [
          {
            "smirks": "[*:1]-[CH3:2]"
          },
          {
            "smirks": "[*:1]-[NH2:2]"
          }
        ],
        "torsion_driver": {
          "type": "torsiondriver",
          "n_workers": 1,
          "grid_spacing": 15,
          "energy_decrease_thresh": null,
          "energy_upper_limit": null,
          "starting_conformations": 4
        }
      },
      "status": "waiting",
      "error": null
    },
    "torsion_optimisation": {
      "stage": "ForceBalanceFitting",
      "stage_settings": {
        "type": "ForceBalanceFitting",
        "penalty_type": "L1",
        "job_type": "optimize",
        "max_iterations": 10,
        "convergence_step_criteria": 0.01,
        "convergence_objective_criteria": 0.01,
        "convergence_gradient_criteria": 0.01,
        "n_criteria": 1,
        "eig_lowerbound": 0.01,
        "finite_difference_h": 0.01,
        "penalty_additive": 0.1,
        "constrain_charge": false,
        "initial_trust_radius": -0.25,
        "minimum_trust_radius": 0.05,
        "error_tolerance": 1.0,
        "adaptive_factor": 0.2,
        "adaptive_damping": 1.0,
        "normalize_weights": false,
        "extras": {},
        "priors": {
          "Proper_k": 6.0
        },
        "targets": {
          "TorsionProfile_OpenMM": {
            "target_name": "TorsionProfile_OpenMM",
            "description": "Relaxed energy and RMSD fitting for torsion drives only.",
            "writelevel": 2,
            "keywords": {
              "pdb": "molecule.pdb",
              "coords": "scan.xyz"
            },
            "openmm_platform": "Reference",
            "energy_denom": 1.0,
            "energy_upper": 10.0,
            "attenuate": false,
            "restrain_k": 1.0
          }
        }
      },
      "status": "waiting",
      "error": null
    }
  }
}
```
No there should be an output from qcengine in a folder called `optimisation`, the json file should contain the raw output from the QM software.
Okay I have simply appended a zip of the entire results folder here:

[methanol.zip](https://github.com/qubekit/QUBEKit/files/10943092/methanol.zip)

Thanks very much! It looks like our most recent version on conda-forge (2.1.1) is missing a fix to work with Gaussian16. Ill look into updating this but for now, it should work if you install the main branch from github. These commands should help

Remove qubekit only from conda 
`conda uninstall qubekit --force`
clone  github code
`git clone https://github.com/qubekit/QUBEKit.git`
move into package 
`cd QUBEKit`
install package
`pip install .`
Thanks a lot, I did all of this and just submitted a job with the fresh install. Let's see if it works this time ^^.
If so I'll close it afterwards.
I think you should really update your requirements.txt.
openmm should be in it I guess, and after doing what you just proposed it first failed with module not found chemper ^^'.
To be continued:

```
qubekit.utils.exceptions.WorkFlowExecutionError: The workflow stopped unexpectedly 
due to the following error at stage: torsion_optimisation
```

[methanol2.zip](https://github.com/qubekit/QUBEKit/files/10943430/methanol2.zip)


Fix patch:
diff --git a/.github/workflows/Lint.yaml b/.github/workflows/Lint.yaml
index 7ed8112c..9147d1d0 100644
--- a/.github/workflows/Lint.yaml
+++ b/.github/workflows/Lint.yaml
@@ -7,7 +7,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.7]
+        python-version: [3.9]
 
     steps:
       - uses: actions/checkout@v2
diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml
new file mode 100644
index 00000000..2538eb3b
--- /dev/null
+++ b/.github/workflows/integration.yaml
@@ -0,0 +1,63 @@
+name: Integration tests
+
+on:
+  push:
+    branches:
+      - "main"
+  pull_request:
+    branches:
+      - "main"
+  schedule:
+    - cron: "0 0 * * *"
+
+defaults:
+  run:
+    shell: bash -l {0}
+
+jobs:
+  test:
+    name: 🖥️ ${{ matrix.os }}, 🐍 ${{ matrix.python-version }}
+    runs-on: ${{ matrix.os }}
+
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        python-version: ["3.8"]
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Additional info about the build
+        run: |
+          uname -a
+          df -h
+          ulimit -a
+
+      - name: Create Environment
+        uses: conda-incubator/setup-miniconda@v2.1.1
+        with:
+          activate-environment: test
+          environment-file: devtools/conda-envs/everything.yaml
+          python-version: ${{ matrix.python-version }}
+          auto-update-conda: true
+          auto-activate-base: false
+          show-channel-urls: true
+          mamba-version: "*"
+          miniforge-version: latest
+          miniforge-variant: Mambaforge
+          use-mamba: true
+
+      - name: Install QUBEKit
+        run: |
+          pip install -e .
+
+      - name: Environment Information
+        run: |
+          conda info
+          conda list --show-channel-urls
+
+      - name: Integration test
+        run: |
+          export OMP_NUM_THREADS=1 
+          qubekit run -sm "CCO" -n "ethanol" -c qubekit/data/integration_workflow.json
diff --git a/devtools/conda-envs/everything.yaml b/devtools/conda-envs/everything.yaml
index c29c5d04..48d56814 100644
--- a/devtools/conda-envs/everything.yaml
+++ b/devtools/conda-envs/everything.yaml
@@ -13,8 +13,10 @@ dependencies:
   - deepdiff >=5.8.0
 
     # Core deps
-  - openff-toolkit-base >=0.10.6, <0.11
-  - openff-fragmenter
+#  - openff-toolkit-base >=0.10.6, <0.11
+  - _openmp_mutex=4.5=2_gnu #required by psi4 ie intel-openmp it seems
+  - pytorch
+  - openff-fragmenter=0.1.2
   - ambertools
   - pydantic
   - forcebalance=1.9.3
@@ -45,5 +47,16 @@ dependencies:
   - msgpack-python
   - chemper
 
+  # OpenFF-toolkit missing
+  - openmm >=7.6
+  - smirnoff99Frosst
+  - openff-forcefields
+  - mdtraj
+  - xmltodict
+  - packaging
+  - python-constraint
+  - importlib-metadata >=4.10
+
   - pip:
       - qforce==0.6.3
+      - git+https://github.com/openforcefield/openff-toolkit.git@0-10-x
diff --git a/qubekit/data/integration_workflow.json b/qubekit/data/integration_workflow.json
new file mode 100644
index 00000000..5563f1bf
--- /dev/null
+++ b/qubekit/data/integration_workflow.json
@@ -0,0 +1,192 @@
+{
+  "type": "WorkFlow",
+  "qc_options": {
+    "program": "torchani",
+    "basis": null,
+    "method": "ani1ccx",
+    "td_settings": null
+  },
+  "local_resources": {
+    "cores": 2,
+    "memory": 2
+  },
+  "fragmentation": {
+    "functional_groups": {
+      "hydrazine": "[NX3:1][NX3:2]",
+      "hydrazone": "[NX3:1][NX2:2]",
+      "nitric_oxide": "[N:1]-[O:2]",
+      "amide": "[#7:1][#6:2](=[#8:3])",
+      "amide_n": "[#7:1][#6:2](-[O-:3])",
+      "amide_2": "[NX3:1][CX3:2](=[OX1:3])[NX3:4]",
+      "aldehyde": "[CX3H1:1](=[O:2])[#6:3]",
+      "sulfoxide_1": "[#16X3:1]=[OX1:2]",
+      "sulfoxide_2": "[#16X3+:1][OX1-:2]",
+      "sulfonyl": "[#16X4:1](=[OX1:2])=[OX1:3]",
+      "sulfinic_acid": "[#16X3:1](=[OX1:2])[OX2H,OX1H0-:3]",
+      "sulfinamide": "[#16X4:1](=[OX1:2])(=[OX1:3])([NX3R0:4])",
+      "sulfonic_acid": "[#16X4:1](=[OX1:2])(=[OX1:3])[OX2H,OX1H0-:4]",
+      "phosphine_oxide": "[PX4:1](=[OX1:2])([#6:3])([#6:4])([#6:5])",
+      "phosphonate": "[P:1](=[OX1:2])([OX2H,OX1-:3])([OX2H,OX1-:4])",
+      "phosphate": "[PX4:1](=[OX1:2])([#8:3])([#8:4])([#8:5])",
+      "carboxylic_acid": "[CX3:1](=[O:2])[OX1H0-,OX2H1:3]",
+      "nitro_1": "[NX3+:1](=[O:2])[O-:3]",
+      "nitro_2": "[NX3:1](=[O:2])=[O:3]",
+      "ester": "[CX3:1](=[O:2])[OX2H0:3]",
+      "tri_halide": "[#6:1]([F,Cl,I,Br:2])([F,Cl,I,Br:3])([F,Cl,I,Br:4])"
+    },
+    "scheme": "WBO",
+    "wbo_options": {
+      "method": "am1-wiberg-elf10",
+      "max_conformers": 800,
+      "rms_threshold": 1.0
+    },
+    "threshold": 0.03,
+    "heuristic": "path_length",
+    "keep_non_rotor_ring_substituents": false,
+    "type": "WBOFragmentation",
+    "rotatable_smirks": [
+      "[!#1]~[!$(*#*)&!D1:1]-,=;!@[!$(*#*)&!D1:2]~[!#1]"
+    ]
+  },
+  "parametrisation": {
+    "type": "OpenFF",
+    "force_field": "openff_unconstrained-2.0.0.offxml"
+  },
+  "optimisation": {
+    "type": "Optimiser",
+    "pre_optimisation_method": "gfn2xtb",
+    "seed_conformers": 40,
+    "maxiter": 350,
+    "convergence_criteria": "GAU_TIGHT"
+  },
+  "charges": {
+    "type": "MBISCharges",
+    "solvent_settings": {
+      "program": "psi4",
+      "units": "au",
+      "codata": 2010,
+      "cavity_Type": "GePol",
+      "cavity_Area": 0.3,
+      "cavity_Scaling": true,
+      "cavity_RadiiSet": "bondi",
+      "cavity_MinRadius": 100,
+      "cavity_Mode": "Implicit",
+      "medium_SolverType": "IEFPCM",
+      "medium_Nonequilibrium": false,
+      "medium_Solvent": "CHCL3",
+      "medium_MatrixSymm": true,
+      "medium_Correction": 0.0,
+      "medium_DiagonalScaling": 1.07,
+      "medium_ProbeRadius": 1.0
+    },
+    "program": "psi4",
+    "basis": "DZVP",
+    "method": "b3lyp-d3bj",
+    "td_settings": null
+  },
+  "virtual_sites": {
+    "type": "VirtualSites",
+    "site_error_factor": 1.005,
+    "site_error_threshold": 1.0,
+    "freeze_site_angles": true,
+    "regularisation_epsilon": 0.0
+  },
+  "non_bonded": {
+    "type": "LennardJones612",
+    "lj_on_polar_h": true,
+    "free_parameters": {
+      "H": {
+        "v_free": 7.6,
+        "b_free": 6.5,
+        "r_free": 1.68
+      },
+      "X": {
+        "v_free": 7.6,
+        "b_free": 6.5,
+        "r_free": 1.464
+      },
+      "C": {
+        "v_free": 34.4,
+        "b_free": 46.6,
+        "r_free": 2.043
+      },
+      "N": {
+        "v_free": 25.9,
+        "b_free": 24.2,
+        "r_free": 1.693
+      },
+      "O": {
+        "v_free": 22.1,
+        "b_free": 15.6,
+        "r_free": 1.68
+      }
+    },
+    "alpha": 0.999,
+    "beta": 0.491
+  },
+  "bonded_parameters": {
+    "type": "ModSeminario",
+    "vibrational_scaling": 1.0
+  },
+  "torsion_scanner": {
+    "type": "TorsionScan1D",
+    "special_torsions": [],
+    "default_scan_range": [
+      -165,
+      180
+    ],
+    "avoided_torsions": [
+      {
+        "smirks": "[*:1]-[CH3:2]"
+      },
+      {
+        "smirks": "[*:1]-[NH2:2]"
+      }
+    ],
+    "torsion_driver": {
+      "type": "torsiondriver",
+      "n_workers": 2,
+      "grid_spacing": 60,
+      "energy_decrease_thresh": null,
+      "energy_upper_limit": null,
+      "starting_conformations": 4
+    }
+  },
+  "torsion_optimisation": {
+    "type": "ForceBalanceFitting",
+    "penalty_type": "L1",
+    "job_type": "optimize",
+    "max_iterations": 10,
+    "convergence_step_criteria": 0.01,
+    "convergence_objective_criteria": 0.01,
+    "convergence_gradient_criteria": 0.01,
+    "n_criteria": 1,
+    "eig_lowerbound": 0.01,
+    "finite_difference_h": 0.01,
+    "penalty_additive": 0.1,
+    "constrain_charge": false,
+    "initial_trust_radius": -0.25,
+    "minimum_trust_radius": 0.05,
+    "error_tolerance": 1.0,
+    "adaptive_factor": 0.2,
+    "adaptive_damping": 1.0,
+    "normalize_weights": false,
+    "extras": {},
+    "priors": {
+      "Proper_k": 6.0
+    },
+    "targets": {
+      "TorsionProfile_OpenFF": {
+        "target_name": "TorsionProfile_SMIRNOFF",
+        "description": "Relaxed energy and RMSD fitting for torsion drives only.",
+        "writelevel": 2,
+        "keywords": {},
+        "openmm_platform": "Reference",
+        "energy_denom": 1.0,
+        "energy_upper": 10.0,
+        "attenuate": true,
+        "restrain_k": 1.0
+      }
+    }
+  }
+}
diff --git a/qubekit/data/templates/optimize.txt b/qubekit/data/templates/optimize.txt
index be40acae..51f36d35 100644
--- a/qubekit/data/templates/optimize.txt
+++ b/qubekit/data/templates/optimize.txt
@@ -55,8 +55,11 @@ normalize_weights {{normalize_weights}}
 # Charge constraints are taken care of using "evals".
 constrain_charge false
 
+{%- if asynchronous %}
 wq_port {{ wq_port }}
-asynchronous
+asynchronous true
+{% endif %}
+
 
 priors
    {%- for key, value in priors.items() %}
@@ -73,8 +76,10 @@ name {{entry_name}}
 type {{target}}
 {%- for key, value in target_options[target].items() %}
 {{key}} {{ value if value is not none else "" }}
-remote 1 # use work_queue worker instead of running directly
 {%- endfor %}
+{%- if asynchronous %}
+remote true
+{% endif %}
 $end
 {%- endfor %}
 {%- endfor %}
\ No newline at end of file
diff --git a/qubekit/engines/torsiondrive.py b/qubekit/engines/torsiondrive.py
index b4fab0e3..7c9eeb5c 100644
--- a/qubekit/engines/torsiondrive.py
+++ b/qubekit/engines/torsiondrive.py
@@ -1,8 +1,8 @@
 import copy
 import json
+import multiprocessing
 import os
 import shutil
-from multiprocessing import Pool
 from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
 
 import numpy as np
@@ -172,7 +172,9 @@ def _run_torsiondrive(
                 # create a new local resource object by dividing the current one by n workers
                 resource_settings = local_options.divide_resource(n_tasks=workers)
                 # start worker pool for multiple optimisers
-                with Pool(processes=self.n_workers) as pool:
+
+                ctx = multiprocessing.get_context("spawn")
+                with ctx.Pool(processes=self.n_workers) as pool:
                     print(f"setting up {workers} workers to compute optimisations")
                     for grid_id_str, job_geo_list in new_jobs.items():
                         for geo_job in job_geo_list:
diff --git a/qubekit/torsions/fitting/forcebalance_wrapper.py b/qubekit/torsions/fitting/forcebalance_wrapper.py
index 8c690d4f..e929ec5c 100644
--- a/qubekit/torsions/fitting/forcebalance_wrapper.py
+++ b/qubekit/torsions/fitting/forcebalance_wrapper.py
@@ -5,7 +5,7 @@
 import os
 import socket
 import subprocess
-from typing import Any, Dict, List
+from typing import Any, Dict, List, Optional
 
 from pydantic import BaseModel, Field, PositiveFloat, PositiveInt
 from qcelemental.util import which_import
@@ -105,7 +105,7 @@ class TorsionProfileSmirnoff(TargetBase):
         description="The upper limit for energy differences in kcal/mol which are included in fitting. Relative energies above this value do not contribute to the objective.",
     )
     attenuate: bool = Field(
-        False,
+        True,
         description="If the weights should be attenuated as a function of the energy above the minimum.",
     )
     restrain_k: float = Field(
@@ -251,15 +251,21 @@ def is_available(cls) -> bool:
             "forcebalance",
             return_bool=True,
             raise_error=True,
-            raise_msg="Please install via `conda install forcebalance -c conda-forge`.",
+            raise_msg="Please install ForceBalance via `conda install forcebalance -c conda-forge`.",
         )
         openmm = which_import(
             "openmm",
             return_bool=True,
             raise_error=True,
-            raise_msg="Please install via `conda install openmm -c conda-forge`.",
+            raise_msg="Please install openmm via `conda install openmm -c conda-forge`.",
         )
-        return fb and openmm
+        cctools = which_import(
+            "work_queue",
+            return_bool=True,
+            raise_error=True,
+            raise_msg="Please install cctools via `conda install ndcctools -c conda-forge`.",
+        )
+        return fb and openmm and cctools
 
     def run(self, molecule: "Ligand", *args, **kwargs) -> "Ligand":
         return self._run(molecule, *args, **kwargs)
@@ -281,7 +287,9 @@ def _run(self, molecule: "Ligand", **kwargs) -> "Ligand":
             )
 
         # now we have validated the data run the optimiser
-        return self._optimise(molecule=molecule, **kwargs)
+        return self._optimise(
+            molecule=molecule, local_options=kwargs.get("local_options", None)
+        )
 
     def add_target(self, target: TargetBase) -> None:
         """
@@ -327,26 +335,42 @@ def _optimise(self, molecule: Ligand, local_options=None) -> Ligand:
 
             # back to fitting folder
             os.chdir(fitting_folder)
+            # total number of torsion targets
+            total_targets = sum(len(torsions) for torsions in fitting_targets.values())
+            # if we have 1 target only use one worker as its faster
+            max_workers = min([total_targets, local_options.cores])
+            # aysnc used for more than one target/worker only
+            use_workers = True if max_workers > 1 else False
             # now we can make the optimize in file
-            wq_port = self.generate_optimise_in(target_data=fitting_targets)
+            wq_port = self.generate_optimise_in(
+                target_data=fitting_targets, use_workers=use_workers
+            )
             # now make the forcefield file
             self.generate_forcefield(molecule=molecule)
 
             # now execute forcebalance
             with open("log.txt", "w") as log:
-                import work_queue
-
-                workers = work_queue.Factory(
-                    "local", manager_host_port=f"localhost:{wq_port}"
-                )
-                # the optimisation takes space in gas, so 1 core for each OpenMM is good enough most likely
-                workers.cores = 1
-                # divide the memory for each worker
-                workers.memory = local_options.memory / local_options.cores
-                workers.min_workers = 1
-                workers.max_workers = local_options.cores
-
-                with workers:
+                if use_workers:
+                    import work_queue
+
+                    workers = work_queue.Factory(
+                        "local", manager_host_port=f"localhost:{wq_port}"
+                    )
+                    # the optimisation takes space in gas, so 1 core for each OpenMM is good enough most likely
+                    workers.cores = 1
+                    # divide the memory for each worker
+                    workers.memory = local_options.memory / max_workers
+                    workers.min_workers = 1
+                    workers.max_workers = max_workers
+
+                    with workers:
+                        subprocess.run(
+                            "ForceBalance optimize.in",
+                            shell=True,
+                            stdout=log,
+                            stderr=log,
+                        )
+                else:
                     subprocess.run(
                         "ForceBalance optimize.in", shell=True, stdout=log, stderr=log
                     )
@@ -375,7 +399,9 @@ def generate_forcefield(self, molecule: Ligand) -> None:
             file_name=os.path.join("forcefield", "bespoke.offxml"), h_constraints=False
         )
 
-    def generate_optimise_in(self, target_data: Dict[str, List[str]]) -> None:
+    def generate_optimise_in(
+        self, target_data: Dict[str, List[str]], use_workers: bool
+    ) -> Optional[int]:
         """
         For the given list of targets and entries produce an optimize.in file which contains all of the run time settings to be used in the optimization.
         this uses jinja templates to generate the required file from the template distributed with qubekit.
@@ -397,11 +423,17 @@ def generate_optimise_in(self, target_data: Dict[str, List[str]]) -> None:
             target_options[target.target_name] = target.fb_options()
         data["target_options"] = target_options
 
-        # select an empty socket and bind it
-        sock = socket.socket()
-        sock.bind(("localhost", 0))
-        wq_port = sock.getsockname()[1]
-        data["wq_port"] = wq_port
+        wq_port = None
+
+        if use_workers:
+            # add the async settings
+            data["asynchronous"] = True
+            # select an empty socket and bind it
+            sock = socket.socket()
+            sock.bind(("localhost", 0))
+            wq_port = sock.getsockname()[1]
+            sock.close()
+            data["wq_port"] = wq_port
 
         rendered_template = template.render(**data)
 
diff --git a/qubekit/utils/datastructures.py b/qubekit/utils/datastructures.py
index 0fee8775..2efe63d4 100644
--- a/qubekit/utils/datastructures.py
+++ b/qubekit/utils/datastructures.py
@@ -108,7 +108,7 @@ def keywords(self) -> Dict[str, Union[str, int]]:
         """
         keywords = {
             "scf_type": "df",
-            # make sure we always use an ultrafine grid
+            # make sure we always use an ultra-fine grid
             "dft_spherical_points": 590,
             "dft_radial_points": 99,
         }
@@ -117,9 +117,12 @@ def keywords(self) -> Dict[str, Union[str, int]]:
             keywords["tdscf_states"] = self.td_settings.n_states
             keywords["tdscf_tda"] = self.td_settings.use_tda
 
-        # work around a setting in psi4, fixes range seperated functionals
+        # work around a setting in psi4, fixes range separated functionals
         if self.program.lower() == "psi4":
             keywords["wcombine"] = False
+        elif self.program.lower() == "xtb":
+            # stop too many open files error see <https://github.com/grimme-lab/xtb-python/issues/73>
+            keywords["verbosity"] = "muted"
         return keywords
 
     @property
diff --git a/qubekit/workflow/helper_stages.py b/qubekit/workflow/helper_stages.py
index 4e0de718..8715355b 100644
--- a/qubekit/workflow/helper_stages.py
+++ b/qubekit/workflow/helper_stages.py
@@ -1,5 +1,4 @@
 import os.path
-from copy import deepcopy
 from typing import List, Optional
 
 import numpy as np
@@ -163,7 +162,7 @@ def _run_qm_opt(
             qc_spec: The QCSpec used to run the QM optimisation.
             local_options: The local resource that is available for the optimisation.
         """
-        opt_mol = deepcopy(molecule)
+        opt_mol = molecule.copy(deep=True)
         g_opt = GeometryOptimiser(convergence=self.convergence_criteria, maxiter=50)
 
         for i, conformer in enumerate(
@@ -222,7 +221,6 @@ def _pre_opt(
         Returns:
             A list of final coordinates from the optimisations.
         """
-        from multiprocessing import Pool
 
         g_opt = GeometryOptimiser(convergence="GAU", maxiter=self.maxiter)
 
@@ -232,44 +230,30 @@ def _pre_opt(
             molecule.to_multiconformer_file(
                 file_name=f"starting_coords{molecule.name}.xyz", positions=geometries
             )
-            opt_list = []
-            with Pool(processes=local_options.cores) as pool:
-                for confomer in geometries:
-                    opt_mol = deepcopy(molecule)
-                    opt_mol.coordinates = confomer
-                    opt_list.append(
-                        pool.apply_async(
-                            # make sure to divide the total resource between the number of workers
-                            g_opt.optimise,
-                            (
-                                opt_mol,
-                                qc_spec,
-                                local_options.divide_resource(
-                                    n_tasks=local_options.cores
-                                ),
-                                True,
-                                True,
-                            ),
-                        )
-                    )
+            results = []
+            for conformer in tqdm(
+                geometries,
+                desc=f"Optimising conformers for {molecule.name} with {self.pre_optimisation_method}",
+                total=len(geometries),
+                ncols=80,
+            ):
+                opt_mol = molecule.copy(deep=True)
+                opt_mol.coordinates = conformer
+                # errors are auto raised from the class so catch the result, and write to file
+                result_mol, opt_result = g_opt.optimise(
+                    molecule=opt_mol,
+                    qc_spec=qc_spec,
+                    local_options=local_options,
+                    allow_fail=True,
+                    return_result=True,
+                )
 
-                results = []
-                for result in tqdm(
-                    opt_list,
-                    desc=f"Optimising conformers for {molecule.name} with {self.pre_optimisation_method}",
-                    total=len(opt_list),
-                    ncols=80,
-                ):
-                    # errors are auto raised from the class so catch the result, and write to file
-                    result_mol, opt_result = result.get()
-                    if opt_result.success:
-                        # save the final energy and molecule
-                        results.append((result_mol, opt_result.energies[-1]))
-                    else:
-                        # save the molecule and final energy from the last step if it fails
-                        results.append(
-                            (result_mol, opt_result.input_data["energies"][-1])
-                        )
+                if opt_result.success:
+                    # save the final energy and molecule
+                    results.append((result_mol, opt_result.energies[-1]))
+                else:
+                    # save the molecule and final energy from the last step if it fails
+                    results.append((result_mol, opt_result.input_data["energies"][-1]))
 
             # sort the results
             results.sort(key=lambda x: x[1])


Test patch:
diff --git a/qubekit/tests/torsions/test_forcebalance.py b/qubekit/tests/torsions/test_forcebalance.py
index 954bd6bd..5fd87a6f 100644
--- a/qubekit/tests/torsions/test_forcebalance.py
+++ b/qubekit/tests/torsions/test_forcebalance.py
@@ -125,7 +125,10 @@ def test_generate_forcefield(tmpdir, biphenyl):
                     assert tuple(sorted(match[1:3])) == (10, 11)
 
 
-def test_generate_optimise_in(tmpdir, biphenyl):
+@pytest.mark.parametrize(
+    "use_workers", [pytest.param(True, id="True"), pytest.param(False, id="False")]
+)
+def test_generate_optimise_in(tmpdir, biphenyl, use_workers: bool):
     """
     Test generating an optimize in file which captures the correct forcebalance run time settings.
     """
@@ -140,7 +143,9 @@ def test_generate_optimise_in(tmpdir, biphenyl):
         fb.add_target(target=tp)
         # now run the setup
         target_folders = tp.prep_for_fitting(molecule=biphenyl)
-        fb.generate_optimise_in(target_data={tp.target_name: target_folders})
+        fb.generate_optimise_in(
+            target_data={tp.target_name: target_folders}, use_workers=use_workers
+        )
         # read the file back in
         with open("optimize.in") as opt_in:
             opt_data = opt_in.read()
@@ -150,6 +155,13 @@ def test_generate_optimise_in(tmpdir, biphenyl):
         assert "mintrust 10" in opt_data
         assert "restrain_k 100" in opt_data
         assert "type TorsionProfile_SMIRNOFF" in opt_data
+        async_tag = "asynchronous" in opt_data
+        wq_tag = "wq_port" in opt_data
+        remote_tag = "remote true" in opt_data
+        # these tags will be present depending on use_workers
+        assert async_tag is use_workers
+        assert wq_tag is use_workers
+        assert remote_tag is use_workers
 
 
 def test_full_optimise(tmpdir, biphenyl):