def test_short_simulation(tmp_path): system, pars = get_system('uio66') configuration = Configuration(system, pars) # conversion conversion = ExplicitConversion() openmm_seed = conversion.apply(configuration) system = openmm_seed.get_system() # necessary to create Simulation object topology, positions = configuration.create_topology() a, b, c = topology.getPeriodicBoxVectors() # instantiate simulation for each platform platforms = ['Reference', 'CPU', 'CUDA', 'OpenCL'] for name in platforms: integrator = mm.LangevinMiddleIntegrator( 300 * unit.kelvin, # temperature 0.1 * unit.picosecond, # friction coefficient 0.5 * unit.femtosecond, # step size ) try: platform = mm.Platform.getPlatformByName(name) except mm.OpenMMException: continue simulation = mm.app.Simulation( topology, system, integrator, platform, ) simulation.context.setPositions(positions) #simulation.context.setPeriodicBoxVectors(box[0], box[1], box[2]) simulation.context.setPeriodicBoxVectors(a, b, c) simulation.step(20)
def test_update_properties(tmp_path): system, pars = get_system('mil53') configuration = Configuration(system, pars) config = configuration.write() config['yaff']['rcut'] = 15.0 config['yaff']['interaction_radius'] = 15.0 configuration.update_properties(config) assert configuration.rcut == 15.0
def test_initialize_nonperiodic(tmp_path): system, pars = get_system('alanine') configuration = Configuration(system, pars) configuration.log_system() # write defaults path_config = tmp_path / 'config.yml' config = configuration.write(path_config) with open(path_config, 'r') as f: content = f.read() assert content == """yaff: {}\n"""
def save(cwd, input_files, file_formats): assert 'chk' in input_files assert 'txt' in input_files assert 'yml' in input_files configuration = Configuration.from_files(**input_files) logger.info('saving configured system in the following formats:') for file_format in file_formats: logger.info('\t\t' + file_format) logger.info( 'If the system is periodic, its box vectors are stored in reduced form' ) seed = configuration.create_seed() for file_format in file_formats: path_file = cwd / ('configured_system.' + file_format) if path_file.exists(): path_file.unlink() if (file_format == 'xyz') or (file_format == 'h5'): seed.system.to_file(str(path_file)) elif file_format == 'pdb': topology, positions = configuration.create_topology() path_pdb = cwd / 'topology.pdb' if path_pdb.exists(): path_pdb.unlink() mm.app.PDBFile.writeFile( topology, positions * unit.angstrom, open(path_pdb, 'w+'), keepIds=True, )
def initialize(cwd, input_files): assert 'chk' in input_files assert 'txt' in input_files path_yml = cwd / 'config.yml' if path_yml.exists(): # remove file if it exists path_yml.unlink() default_classes = [ # classes for which to initialize .yml keywords ExplicitConversion, SinglePointValidation, StressValidation, ] # initialize Configuration based on defaults; log configuration = Configuration.from_files(**input_files) configuration.log_system() configuration.log_config() logger.info('writing configuration file to') logger.info(str(path_yml)) configuration.write(path_yml) for default in default_classes: default().write(path_yml) # add annotations to config file for clarity; this cannot be done using # pyyaml and therefore proceeds manually add_header_to_config(path_yml) configuration.annotate(path_yml) for default in default_classes: default().annotate(path_yml)
def test_nonperiodic(): systems = ['alanine'] platforms = ['Reference'] seed_kinds = ['covalent', 'dispersion', 'electrostatic'] tolerance = { ('Reference', 'covalent'): 1e-6, ('Reference', 'dispersion'): 1e-6, ('Reference', 'electrostatic'): 1e-6, #('Cuda', 'covalent'): 1e-5, #('Cuda', 'dispersion'): 1e-5, #('Cuda', 'electrostatic'): 1e-5, } nstates = 10 disp_ampl = 1.0 box_ampl = 1.0 for name in systems: for platform in platforms: for kind in seed_kinds: system, pars = get_system(name) configuration = Configuration(system, pars) tol = tolerance[(platform, kind)] conversion = ExplicitConversion() seed_mm = conversion.apply(configuration, seed_kind=kind) seed_yaff = configuration.create_seed(kind=kind) wrapper_mm = OpenMMForceFieldWrapper.from_seed( seed_mm, platform) wrapper_yaff = YaffForceFieldWrapper.from_seed(seed_yaff) assert not wrapper_yaff.periodic # system should not be considered periodic assert not wrapper_mm.periodic # system should not be considered periodic pos = seed_yaff.system.pos.copy() for i in range(nstates): dpos = np.random.uniform(-disp_ampl, disp_ampl, size=pos.shape) energy_mm, forces_mm = wrapper_mm.evaluate( (pos + dpos) / molmod.units.angstrom, ) energy, forces = wrapper_yaff.evaluate( (pos + dpos) / molmod.units.angstrom, ) assert_tol(energy, energy_mm, tol) assert_tol(forces, forces_mm, 10 * tol)
def test_from_files(tmp_path): system, pars = get_system('mil53') configuration = Configuration(system, pars) configuration.write(tmp_path / 'config.yml') system.to_file(str(tmp_path / 'system.chk')) with open(tmp_path / 'pars.txt', 'w+') as f: f.write(pars) path_system = tmp_path / 'system.chk' path_pars = tmp_path / 'pars.txt' path_config = tmp_path / 'config.yml' configuration = Configuration.from_files( chk=path_system, txt=path_pars, yml=path_config, )
def test_create_seed_nonperiodic(): system, pars = get_system('alanine') configuration = Configuration(system, pars) seed_covalent = configuration.create_seed(kind='covalent') ff = yaff_generate(seed_covalent) energy_covalent = ff.compute() seed_dispersion = configuration.create_seed(kind='dispersion') ff = yaff_generate(seed_dispersion) energy_dispersion = ff.compute() seed_electrostatic = configuration.create_seed(kind='electrostatic') ff = yaff_generate(seed_electrostatic) energy_electrostatic = ff.compute() seed_nonbonded = configuration.create_seed(kind='nonbonded') ff = yaff_generate(seed_nonbonded) energy_nonbonded = ff.compute() seed_full = configuration.create_seed(kind='all') ff = yaff_generate(seed_full) energy_full = ff.compute() assert abs(energy_covalent) > 0.0 assert abs(energy_dispersion) > 0.0 assert abs(energy_electrostatic) > 0.0 np.testing.assert_almost_equal( energy_nonbonded, energy_dispersion + energy_electrostatic, ) np.testing.assert_almost_equal( energy_full, energy_covalent + energy_nonbonded, )
def validate(cwd, input_files): assert 'chk' in input_files assert 'txt' in input_files assert 'yml' in input_files configuration = Configuration.from_files(**input_files) configuration.log_config() conversion = load_conversion(input_files['yml']) validations = load_validations(input_files['yml']) for validation in validations: validation.run(configuration, conversion)
def test_check_compatibility(): system, _ = get_system('lennardjones') conversion = ExplicitConversion() seed_kind = 'dispersion' # generate pars with unsupported prefix pars_unsupported = """ BLAAA:UNIT SIGMA angstrom BLAAA:UNIT EPSILON kcalmol BLAAA:SCALE 1 1.0 BLAAA:SCALE 2 1.0 BLAAA:SCALE 3 1.0 # --------------------------------------------- # KEY ffatype SIGMA EPSILON ONLYPAULI # --------------------------------------------- BLAAA:PARS C 2.360 0.116 0""" with pytest.raises(AssertionError): configuration = Configuration(system, pars_unsupported) # generate pars with unsupported scaling pars_unsupported = """ LJ:UNIT SIGMA angstrom LJ:UNIT EPSILON kcalmol LJ:SCALE 1 0.5 LJ:SCALE 2 1.0 LJ:SCALE 3 1.0 # --------------------------------------------- # KEY ffatype SIGMA EPSILON ONLYPAULI # --------------------------------------------- LJ:PARS C 2.360 0.116 0""" configuration = Configuration(system, pars_unsupported) with pytest.raises(AssertionError): seed_mm = conversion.apply(configuration, seed_kind=seed_kind)
def test_wrapper_openmm_mic(): system, pars = get_system('mil53') configuration = Configuration(system, pars) kind = 'all' # YAFF and OpenMM use a different switching function. If it is disabled, # the results between both are identical up to 6 decimals configuration.switch_width = 0.0 # disable switching configuration.rcut = 10.0 # request cutoff of 10 angstorm configuration.cell_interaction_radius = 10.0 configuration.supercell = [2, 3, 5] configuration.update_properties(configuration.write()) conversion = ExplicitConversion(pme_error_thres=1e-5) seed_mm = conversion.apply(configuration, seed_kind=kind) wrapper = OpenMMForceFieldWrapper.from_seed(seed_mm, 'Reference') u = molmod.units.angstrom seed_yaff = configuration.create_seed(kind=kind) positions = seed_yaff.system.pos.copy() / u rvecs = seed_yaff.system.cell._get_rvecs().copy() / u e, _ = wrapper.evaluate(positions, rvecs, do_forces=True) # make random periodic displacements for i in range(5): coefficients = np.random.randint(-3, high=3, size=(3, 1)) atom = np.random.randint(0, high=seed_yaff.system.natom, size=(10,)) positions[atom, :] += np.sum(coefficients * rvecs, axis=0) e_ = wrapper.evaluate(positions, rvecs, do_forces=False) assert np.allclose(e, e_) # make random periodic displacements and rewrap coordinates for i in range(5): coefficients = np.random.randint(-3, high=3, size=(3, 1)) atom = np.random.randint(0, high=seed_yaff.system.natom, size=(10,)) positions[atom, :] += np.sum(coefficients * rvecs, axis=0) wrap_coordinates(positions, rvecs, rectangular=True) e_ = wrapper.evaluate(positions, rvecs, do_forces=False) assert np.allclose(e, e_)
def test_save_load_pdb(tmp_path): system, pars = get_system('mil53') configuration = Configuration(system, pars) # YAFF and OpenMM use a different switching function. If it is disabled, # the results between both are identical up to 6 decimals configuration.switch_width = 0.0 # disable switching configuration.rcut = 10.0 # request cutoff of 10 angstorm configuration.interaction_radius = 11.0 #configuration.update_properties(configuration.write()) conversion = ExplicitConversion(pme_error_thres=5e-4) seed_mm = conversion.apply(configuration, seed_kind='all') seed_yaff = configuration.create_seed(kind='all') topology, pos = configuration.create_topology() box = seed_yaff.system.cell._get_rvecs() / molmod.units.angstrom wrapper_mm = OpenMMForceFieldWrapper.from_seed(seed_mm, 'Reference') wrapper_yaff = YaffForceFieldWrapper.from_seed(seed_yaff) assert wrapper_yaff.periodic # system should not be considered periodic assert wrapper_mm.periodic # system should not be considered periodic #positions = seed_yaff.system.pos.copy() / molmod.units.angstrom #rvecs = seed_yaff.system.cell._get_rvecs().copy() / molmod.units.angstrom e0, f0 = wrapper_mm.evaluate(pos, box, do_forces=True) e1, f1 = wrapper_yaff.evaluate(pos, box, do_forces=True) assert np.allclose(e0, e1, rtol=1e-3) path_pdb = tmp_path / 'top.pdb' mm.app.PDBFile.writeFile( topology, pos * unit.angstrom, open(path_pdb, 'w+'), keepIds=True, ) pdb = mm.app.PDBFile(str(path_pdb)) positions = pdb.getPositions(asNumpy=True).value_in_unit(unit.angstrom) a, b, c = pdb.getTopology().getPeriodicBoxVectors() rvecs = np.array([ a.value_in_unit(unit.angstrom), b.value_in_unit(unit.angstrom), c.value_in_unit(unit.angstrom) ]) e2, f2 = wrapper_mm.evaluate(positions, rvecs, do_forces=True) e3, f3 = wrapper_yaff.evaluate(positions, rvecs, do_forces=True) assert np.allclose(e2, e3, rtol=1e-3) assert np.allclose(e1, e3, rtol=1e-4) # rounding errors during saving pdb assert np.allclose(e0, e2, rtol=1e-4)
def test_get_prefixes(): system, pars = get_system('cau13') configuration = Configuration(system, pars) prefixes = configuration.get_prefixes('all') covalent_prefixes = configuration.get_prefixes('covalent') dispersion_prefixes = configuration.get_prefixes('dispersion') electrostatic_prefixes = configuration.get_prefixes('electrostatic') nonbonded_prefixes = configuration.get_prefixes('nonbonded') _ = dispersion_prefixes + electrostatic_prefixes assert tuple(sorted(_)) == tuple(sorted(nonbonded_prefixes)) __ = covalent_prefixes + nonbonded_prefixes assert tuple(sorted(__)) == tuple(sorted(prefixes))
def convert(cwd, input_files, seed_kind, full): assert 'chk' in input_files assert 'txt' in input_files assert 'yml' in input_files configuration = Configuration.from_files(**input_files) configuration.log_config() conversion = load_conversion(input_files['yml']) if conversion.kind == 'explicit': path_xml = cwd / 'system.xml' logger.info('saving OpenMM System object to ') logger.info(path_xml) openmm_seed = conversion.apply(configuration, seed_kind) openmm_seed.serialize_system(path_xml) elif conversion.kind == 'implicit': openmm_seed = conversion.apply(configuration, seed_kind) path_xml = cwd / 'ff.xml' logger.info('saving OpenMM ForceField object to ') logger.info(path_xml) openmm_seed.serialize_forcefield(path_xml) path_xml = cwd / 'system.xml' logger.info('saving OpenMM ForceField object to ') logger.info(path_xml) openmm_seed.serialize_system(path_xml) if full: # write additional files topology, positions = configuration.create_topology() path_pdb = cwd / 'topology.pdb' if path_pdb.exists(): path_pdb.unlink() mm.app.PDBFile.writeFile( topology, positions * unit.angstrom, open(path_pdb, 'w+'), keepIds=True, )
def test_periodic(): systems = ['uio66', 'cau13', 'mil53', 'ppycof', 'cof5', 'mof5'] platforms = ['Reference'] seed_kinds = ['covalent', 'dispersion', 'electrostatic'] # systematic constant offset in dispersion energy for COFs, unclear why tolerance = { ('Reference', 'covalent'): 1e-6, # some MM3 terms have error 1e-7 ('Reference', 'dispersion'): 1e-2, # some MM3 terms have error 1e-3 ('Reference', 'electrostatic'): 1e-3, #('CUDA', 'covalent'): 1e-3, #('CUDA', 'dispersion'): 1e-3, #('CUDA', 'electrostatic'): 1e-3, } nstates = 5 disp_ampl = 0.3 box_ampl = 0.3 for name in systems: for platform in platforms: for kind in seed_kinds: system, pars = get_system(name) configuration = Configuration(system, pars) tol = tolerance[(platform, kind)] # YAFF and OpenMM use a different switching function. If it is disabled, # the results between both are identical up to 6 decimals configuration.switch_width = 0.0 # disable switching configuration.rcut = 13.0 # request cutoff of 13 angstorm configuration.interaction_radius = 15.0 configuration.update_properties(configuration.write()) conversion = ExplicitConversion(pme_error_thres=5e-4) seed_mm = conversion.apply(configuration, seed_kind=kind) seed_yaff = configuration.create_seed(kind=kind) wrapper_mm = OpenMMForceFieldWrapper.from_seed( seed_mm, platform) wrapper_yaff = YaffForceFieldWrapper.from_seed(seed_yaff) assert wrapper_yaff.periodic # system should not be considered periodic assert wrapper_mm.periodic # system should not be considered periodic pos = seed_yaff.system.pos.copy() rvecs = seed_yaff.system.cell._get_rvecs().copy() for i in range(nstates): dpos = np.random.uniform(-disp_ampl, disp_ampl, size=pos.shape) drvecs = np.random.uniform(-box_ampl, box_ampl, size=rvecs.shape) drvecs[0, 1] = 0 drvecs[0, 2] = 0 drvecs[1, 2] = 0 tmp = rvecs + drvecs reduce_box_vectors(tmp) energy_mm, forces_mm = wrapper_mm.evaluate( (pos + dpos) / molmod.units.angstrom, rvecs=tmp / molmod.units.angstrom, ) energy, forces = wrapper_yaff.evaluate( (pos + dpos) / molmod.units.angstrom, rvecs=tmp / molmod.units.angstrom, ) assert_tol(energy, energy_mm, tol) assert_tol(forces, forces_mm, 10 * tol)
def test_implicit_nonperiodic(): system, pars = get_system('alanine') configuration = Configuration(system, pars) conversion = ImplicitConversion() seed_mm = conversion.apply(configuration, seed_kind='all')
def test_topology_templates(tmp_path): def check_consistency_system_topology(system, topology): assert system.natom == len(list(topology.atoms())) for i, atom in zip(range(system.natom), topology.atoms()): assert system.numbers[i] == atom.element._atomic_number assert system.bonds.shape[0] == len(list(topology.bonds())) bonds_tuples = [tuple(sorted(list(bond))) for bond in system.bonds] for bond in topology.bonds(): indices_top = tuple(sorted((bond[0].index, bond[1].index))) bonds_tuples.remove(indices_top) assert len(bonds_tuples) == 0 system, pars = get_system('methane') configuration = Configuration(system, pars, topology=None) assert len(configuration.templates) == 1 assert len(configuration.templates[0]) == 5 # five atoms for CH4 template nresidues = np.sum([len(r) for r in configuration.residues.values()]) supercell = [3, 1, 2] configuration.supercell = supercell top, _ = configuration.create_topology() assert len(list(top.residues())) == np.prod(supercell) * nresidues seed = configuration.create_seed() check_consistency_system_topology(seed.system, top) system, pars = get_system('uio66') configuration = Configuration(system, pars, topology=None) assert len(configuration.templates) == 1 assert len(configuration.templates[0]) == system.natom supercell = [2, 2, 2] configuration.supercell = supercell top, positions = configuration.create_topology() assert len(list(top.residues())) == np.prod(supercell) seed = configuration.create_seed() check_consistency_system_topology(seed.system, top) assert np.allclose(positions, seed.system.pos / molmod.units.angstrom) (system, topology), pars = get_system('polymer') configuration = Configuration(system, pars, topology=topology) assert len(configuration.templates) == 3 top, positions = configuration.create_topology() assert len(list(top.residues())) == len(list(topology.residues())) seed = configuration.create_seed() check_consistency_system_topology(seed.system, top) assert np.allclose(positions, seed.system.pos / molmod.units.angstrom) a, b, c = top.getPeriodicBoxVectors() box = np.array([ a.value_in_unit(unit.angstrom), b.value_in_unit(unit.angstrom), c.value_in_unit(unit.angstrom), ]) assert np.allclose(box, seed.system.cell._get_rvecs() / molmod.units.angstrom) # test nonperiodic system, pars = get_system('alanine') configuration = Configuration(system, pars) assert len(configuration.templates) == 1 top, positions = configuration.create_topology() seed = configuration.create_seed() check_consistency_system_topology(seed.system, top) assert np.allclose(positions, seed.system.pos / molmod.units.angstrom)
def test_create_seed_periodic(): system, pars = get_system('cau13') configuration = Configuration(system, pars) # change parameters randomly with pytest.raises(ValueError): # cell is too small configuration.supercell = [3, 1, 1] configuration.rcut = 12.0 assert configuration.interaction_radius == 12.0 # should change too configuration.switch_width = 5.0 configuration.tailcorrections = False seed_covalent = configuration.create_seed(kind='covalent') ff = yaff_generate(seed_covalent) energy_covalent = ff.compute() seed_dispersion = configuration.create_seed(kind='dispersion') ff = yaff_generate(seed_dispersion) energy_dispersion = ff.compute() seed_electrostatic = configuration.create_seed(kind='electrostatic') ff = yaff_generate(seed_electrostatic) energy_electrostatic = ff.compute() seed_nonbonded = configuration.create_seed(kind='nonbonded') ff = yaff_generate(seed_nonbonded) energy_nonbonded = ff.compute() seed_full = configuration.create_seed(kind='all') ff = yaff_generate(seed_full) energy_full = ff.compute() assert abs(energy_covalent) > 0.0 assert abs(energy_dispersion) > 0.0 assert abs(energy_electrostatic) > 0.0 np.testing.assert_almost_equal( energy_nonbonded, energy_dispersion + energy_electrostatic, ) np.testing.assert_almost_equal( energy_full, energy_covalent + energy_nonbonded, )