def _interpolation_grid_force(self): self._widths = [] self._bounds = [] self._extra_points = [] for v in self.bias_variables: extra_points = min(self.grid_expansion, v.grid_size) if v.periodic else 0 extra_range = extra_points*v._range/(v.grid_size - 1) self._widths.append(v.grid_size + 2*extra_points) self._bounds += [v.min_value - extra_range, v.max_value + extra_range] self._extra_points.append(extra_points) self._bias = np.zeros(np.prod(self._widths)) num_bias_variables = len(self.bias_variables) if num_bias_variables == 1: self._table = openmm.Continuous1DFunction(self._bias, *self._bounds) elif num_bias_variables == 2: self._table = openmm.Continuous2DFunction(*self._widths, self._bias, *self._bounds) else: self._table = openmm.Continuous3DFunction(*self._widths, self._bias, *self._bounds) expression = f'bias({",".join(v.id for v in self.bias_variables)})' for i, v in enumerate(self.bias_variables): expression += f';{v.id}={v._get_energy_function(i+1)}' force = openmm.CustomCVForce(expression) for i in range(num_bias_variables): x = openmm.CustomExternalForce('x') x.addParticle(0, []) force.addCollectiveVariable(f'x{i+1}', x) force.addTabulatedFunction('bias', self._table) force.addGlobalParameter('Lx', 0) return force
def add_external_field(system: mm.System, args: ListOfArgs): """Add external forcefield for image-driven modelling purposes.""" print(' Adding external forcefield.') size = os.stat(args.EF_PATH).st_size print(f" Reading {args.EF_PATH} file ({sizeof_fmt(size)})...") img = np.load(args.EF_PATH) print(f" Array of shape {img.shape} loaded.") print(f" Number of values: {img.size}") print(f" Min: {np.min(img)}") print(f" Max: {np.max(img)}") if args.EF_NORMALIZE: print(' [INFO] Field will be normalized to [0, -1]') img = standardize_image(img) print(f' [INFO] IMG min = {np.min(img)}, max = {np.max(img)}') print(f' [INFO] Adding funnel like border to image') mask_p = (img < -0.1) mask_n = np.logical_not(mask_p) img = add_funnel(img, mask_n) print(" Creating a force based on density...") voxel_size = np.array((args.EF_VOXEL_SIZE_X, args.EF_VOXEL_SIZE_Y, args.EF_VOXEL_SIZE_Z)) real_size = img.shape * voxel_size density_fun_args = dict( xsize=img.shape[2], ysize=img.shape[1], zsize=img.shape[0], values=img.flatten().astype(np.float64), xmin=0 * simtk.unit.angstrom - 0.5 * voxel_size[0], ymin=0 * simtk.unit.angstrom - 0.5 * voxel_size[1], zmin=0 * simtk.unit.angstrom - 0.5 * voxel_size[2], xmax=(img.shape[0] - 1) * voxel_size[0] + 0.5 * voxel_size[0], ymax=(img.shape[1] - 1) * voxel_size[1] + 0.5 * voxel_size[1], zmax=(img.shape[2] - 1) * voxel_size[2] + 0.5 * voxel_size[2]) print(f' [INFO] Voxel size: ({args.EF_VOXEL_SIZE_X}, {args.EF_VOXEL_SIZE_Y}, {args.EF_VOXEL_SIZE_Z})') print(f' [INFO] Real size (Shape * voxel size): ({real_size[0]}, {real_size[1]}, {real_size[2]})') print( f" [INFO] begin coords: ({density_fun_args['xmin']}, {density_fun_args['ymin']}, {density_fun_args['zmin']})") print( f" [INFO] end coords: ({density_fun_args['xmax']}, {density_fun_args['ymax']}, {density_fun_args['zmax']})") center_x = (density_fun_args['xmax'] - density_fun_args['xmin']) / 2 + density_fun_args['xmin'] center_y = (density_fun_args['ymax'] - density_fun_args['ymin']) / 2 + density_fun_args['ymin'] center_z = (density_fun_args['zmax'] - density_fun_args['zmin']) / 2 + density_fun_args['zmin'] print(f" [INFO] Image central point: ({center_x}, {center_y}, {center_z}) ") field_function = mm.Continuous3DFunction(**density_fun_args) field_force = mm.CustomCompoundBondForce(1, 'ksi*fi(x1,y1,z1)') field_force.addTabulatedFunction('fi', field_function) field_force.addGlobalParameter('ksi', args.EF_SCALING_FACTOR) print(" Adding force to the system...") for i in range(system.getNumParticles()): field_force.addBond([i], []) system.addForce(field_force)
def __init__(self, variables, height, frequency, grid_expansion): self.bias_variables = [cv for cv in variables if cv.sigma is not None] self.height = height self.frequency = frequency self.grid_expansion = grid_expansion self._widths = [] self._bounds = [] self._expanded = [] self._extra_points = [] for cv in self.bias_variables: expanded = cv.periodic # and len(self.bias_variables) > 1 extra_points = min(grid_expansion, cv.grid_size) if expanded else 0 extra_range = extra_points * cv._range / (cv.grid_size - 1) self._widths += [cv.grid_size + 2 * extra_points] self._bounds += [ cv.min_value - extra_range, cv.max_value + extra_range ] self._expanded += [expanded] self._extra_points += [extra_points] self._bias = np.zeros(tuple(reversed(self._widths))) if len(variables) == 1: self._table = openmm.Continuous1DFunction( self._bias.flatten(), *self._bounds, # self.bias_variables[0].periodic, ) elif len(variables) == 2: self._table = openmm.Continuous2DFunction( *self._widths, self._bias.flatten(), *self._bounds, ) elif len(variables) == 3: self._table = openmm.Continuous3DFunction( *self._widths, self._bias.flatten(), *self._bounds, ) else: raise ValueError( 'UFED requires 1, 2, or 3 biased collective variables') parameter_list = ', '.join(f's_{cv.id}' for cv in self.bias_variables) self.force = openmm.CustomCVForce(f'bias({parameter_list})') for cv in self.bias_variables: expression = f'{cv.min_value}+{cv._range}*(x/Lx-floor(x/Lx))' parameter = openmm.CustomExternalForce(expression) parameter.addGlobalParameter('Lx', 0.0) parameter.addParticle(0, []) self.force.addCollectiveVariable(f's_{cv.id}', parameter) self.force.addTabulatedFunction('bias', self._table)
def __init__(self, system, variables, temperature, biasFactor, height, frequency, saveFrequency=None, biasDir=None): """Create a Metadynamics object. Parameters ---------- system: System the System to simulate. A CustomCVForce implementing the bias is created and added to the System. variables: list of BiasVariables the collective variables to sample temperature: temperature the temperature at which the simulation is being run. This is used in computing the free energy. biasFactor: float used in scaling the height of the Gaussians added to the bias. The collective variables are sampled as if the effective temperature of the simulation were temperature*biasFactor. height: energy the initial height of the Gaussians to add frequency: int the interval in time steps at which Gaussians should be added to the bias potential saveFrequency: int (optional) the interval in time steps at which to write out the current biases to disk. At the same time it writes biases, it also checks for updated biases written by other processes and loads them in. This must be a multiple of frequency. biasDir: str (optional) the directory to which biases should be written, and from which biases written by other processes should be loaded """ if not unit.is_quantity(temperature): temperature = temperature * unit.kelvin if not unit.is_quantity(height): height = height * unit.kilojoules_per_mole if biasFactor < 1.0: raise ValueError('biasFactor must be >= 1') if (saveFrequency is None and biasDir is not None) or (saveFrequency is not None and biasDir is None): raise ValueError('Must specify both saveFrequency and biasDir') if saveFrequency is not None and (saveFrequency < frequency or saveFrequency % frequency != 0): raise ValueError('saveFrequency must be a multiple of frequency') self.variables = variables self.temperature = temperature self.biasFactor = biasFactor self.height = height self.frequency = frequency self.biasDir = biasDir self.saveFrequency = saveFrequency self._id = np.random.randint(0x7FFFFFFF) self._saveIndex = 0 self._selfBias = np.zeros(tuple(v.gridWidth for v in variables)) self._totalBias = np.zeros(tuple(v.gridWidth for v in variables)) self._loadedBiases = {} self._deltaT = temperature * (biasFactor - 1) varNames = ['cv%d' % i for i in range(len(variables))] self._force = mm.CustomCVForce('table(%s)' % ', '.join(varNames)) for name, var in zip(varNames, variables): self._force.addCollectiveVariable(name, var.force) widths = [v.gridWidth for v in variables] mins = [v.minValue for v in variables] maxs = [v.maxValue for v in variables] if len(variables) == 1: self._table = mm.Continuous1DFunction(self._totalBias.flatten(), mins[0], maxs[0]) elif len(variables) == 2: self._table = mm.Continuous2DFunction(widths[0], widths[1], self._totalBias.flatten(), mins[0], maxs[0], mins[1], maxs[1]) elif len(variables) == 3: self._table = mm.Continuous3DFunction(widths[0], widths[1], widths[2], self._totalBias.flatten(), mins[0], maxs[0], mins[1], maxs[1], mins[2], maxs[2]) else: raise ValueError( 'Metadynamics requires 1, 2, or 3 collective variables') self._force.addTabulatedFunction('table', self._table) self._force.setForceGroup(31) system.addForce(self._force) self._syncWithDisk()