def smirnoff_force_field() -> OFFForceField: from simtk import unit off_force_field = OFFForceField( '<SMIRNOFF version="0.3" aromaticity_model="OEAroModel_MDL"></SMIRNOFF>' ) vdw_handler = vdWHandler(**{"version": "0.3"}) vdw_handler.add_parameter( parameter_kwargs={ "smirks": "[#6:1]", "epsilon": 1.0 * unit.kilojoules_per_mole, "sigma": 1.0 * unit.angstrom, }) off_force_field.register_parameter_handler(vdw_handler) charge_handler = ChargeIncrementModelHandler(**{"version": "0.3"}) charge_handler.add_parameter( parameter_kwargs={ "smirks": "[#6:1]-[#6:2]", "charge_increment1": 1.0 * unit.elementary_charge, "charge_increment2": -1.0 * unit.elementary_charge, }) off_force_field.register_parameter_handler(charge_handler) return off_force_field
def hydrogen_chloride_force_field(library_charge: bool, charge_increment: bool) -> ForceField: """Returns a SMIRNOFF force field which is able to parameterize hydrogen chloride.""" # Create the FF force_field = ForceField() # Add a Vdw handler. vdw_handler = vdWHandler(version=0.3) vdw_handler.method = "cutoff" vdw_handler.cutoff = 6.0 * simtk_unit.angstrom vdw_handler.scale14 = 1.0 vdw_handler.add_parameter({ "smirks": "[#1:1]", "epsilon": 0.0 * simtk_unit.kilojoules_per_mole, "sigma": 1.0 * simtk_unit.angstrom, }) vdw_handler.add_parameter({ "smirks": "[#17:1]", "epsilon": 2.0 * simtk_unit.kilojoules_per_mole, "sigma": 2.0 * simtk_unit.angstrom, }) force_field.register_parameter_handler(vdw_handler) # Add an electrostatic, a library charge and a charge increment handler. electrostatics_handler = ElectrostaticsHandler(version=0.3) electrostatics_handler.cutoff = 6.0 * simtk_unit.angstrom electrostatics_handler.method = "PME" force_field.register_parameter_handler(electrostatics_handler) if library_charge: library_charge_handler = LibraryChargeHandler(version=0.3) library_charge_handler.add_parameter( parameter_kwargs={ "smirks": "[#1:1]", "charge1": 1.0 * simtk_unit.elementary_charge, }) library_charge_handler.add_parameter( parameter_kwargs={ "smirks": "[#17:1]", "charge1": -1.0 * simtk_unit.elementary_charge, }) force_field.register_parameter_handler(library_charge_handler) if charge_increment: charge_increment_handler = ChargeIncrementModelHandler(version=0.3) charge_increment_handler.add_parameter( parameter_kwargs={ "smirks": "[#1:1]-[#17:2]", "charge_increment1": -1.0 * simtk_unit.elementary_charge, "charge_increment2": 1.0 * simtk_unit.elementary_charge, }) force_field.register_parameter_handler(charge_increment_handler) return force_field
def system_subset( parameter_key: ParameterGradientKey, force_field: "ForceField", topology: "Topology", scale_amount: Optional[float] = None, ) -> Tuple["openmm.System", "simtk_unit.Quantity"]: """Produces an OpenMM system containing the minimum number of forces while still containing a specified force field parameter, and those other parameters which may interact with it (e.g. in the case of vdW parameters). The value of the parameter of interest may optionally be perturbed by an amount specified by ``scale_amount``. Parameters ---------- parameter_key The parameter of interest. force_field The force field to create the system from (and optionally perturb). topology The topology of the system to apply the force field to. scale_amount: float, optional The optional amount to perturb the ``parameter`` by such that ``parameter = (1.0 + scale_amount) * parameter``. Returns ------- The created system as well as the value of the specified ``parameter``. """ # As this method deals mainly with the toolkit, we stick to # simtk units here. from openff.toolkit.typing.engines.smirnoff import ForceField # Create the force field subset. force_field_subset = ForceField() handlers_to_register = {parameter_key.tag} if parameter_key.tag in {"ChargeIncrementModel", "LibraryCharges"}: # Make sure to retain all of the electrostatic handlers when dealing with # charges as the applied charges will depend on which charges have been applied # by previous handlers. handlers_to_register.update( {"Electrostatics", "ChargeIncrementModel", "LibraryCharges"}) registered_handlers = force_field.registered_parameter_handlers for handler_to_register in handlers_to_register: if handler_to_register not in registered_handlers: continue force_field_subset.register_parameter_handler( copy.deepcopy( force_field.get_parameter_handler(handler_to_register))) handler = force_field_subset.get_parameter_handler(parameter_key.tag) parameter = handler.parameters[parameter_key.smirks] parameter_value = getattr(parameter, parameter_key.attribute) # Optionally perturb the parameter of interest. if scale_amount is not None: if numpy.isclose(parameter_value.value_in_unit(parameter_value.unit), 0.0): # Careful thought needs to be given to this. Consider cases such as # epsilon or sigma where negative values are not allowed. parameter_value = (scale_amount if scale_amount > 0.0 else 0.0) * parameter_value.unit else: parameter_value *= 1.0 + scale_amount setattr(parameter, parameter_key.attribute, parameter_value) # Create the parameterized sub-system. system = force_field_subset.create_openmm_system(topology) return system, parameter_value