def test_set_binary_interaction(): """Regression test for adding binary interaction parameter with customNonbondedForce""" # Load in cgmodel cgmodel = pickle.load(open(f"{data_path}/stored_cgmodel.pkl", "rb")) binary_interaction_parameters = {"bb_sc_binary_interaction": 0.0} cgmodel_new = CGModel( particle_type_list=cgmodel.particle_type_list, bond_lengths=cgmodel.bond_lengths, bond_force_constants=cgmodel.bond_force_constants, bond_angle_force_constants=cgmodel.bond_angle_force_constants, torsion_force_constants=cgmodel.torsion_force_constants, equil_bond_angles=cgmodel.equil_bond_angles, torsion_periodicities=cgmodel.torsion_periodicities, binary_interaction_parameters=binary_interaction_parameters, include_nonbonded_forces=cgmodel.include_nonbonded_forces, include_bond_forces=cgmodel.include_bond_forces, include_bond_angle_forces=cgmodel.include_bond_angle_forces, include_torsion_forces=cgmodel.include_torsion_forces, constrain_bonds=cgmodel.constrain_bonds, sequence=cgmodel.sequence, positions=cgmodel.positions, monomer_types=cgmodel.monomer_types, ) native_structure_file = f"{structures_path}/medoid_0.dcd" native_traj = md.load(native_structure_file, top=md.Topology.from_openmm(cgmodel.topology)) positions = native_traj.xyz[0] * unit.nanometer # Minimize energy of native structure positions, PE_start, PE_end, simulation = minimize_structure( cgmodel_new, positions, output_file=f"{structures_path}/medoid_min.dcd", ) # These should be equal to ~3 decimal places (1 Joule/mol) PE_start_kappa_off = -47.523193359375 PE_end_kappa_off = -73.21410369873047 PE_start_kappa_on = PE_start.value_in_unit(unit.kilojoule_per_mole) PE_end_kappa_on = PE_end.value_in_unit(unit.kilojoule_per_mole) assert_almost_equal(PE_start_kappa_on, PE_start_kappa_off, decimal=3) assert_almost_equal(PE_end_kappa_on, PE_end_kappa_off, decimal=3)
def get_torsion_matrix(file_list, cgmodel, frame_start, frame_stride, frame_end, backbone_torsion_type): """Internal function for reading trajectory files and computing torsions""" # Load files as {replica number: replica trajectory} rep_traj = {} for i in range(len(file_list)): if file_list[0][-3:] == 'dcd': rep_traj[i] = md.load(file_list[i], top=md.Topology.from_openmm( cgmodel.topology)) else: rep_traj[i] = md.load(file_list[i]) # Combine all trajectories, selecting specified frames if frame_end == -1: frame_end = rep_traj[0].n_frames if frame_start == -1: frame_start == frame_end traj_all = rep_traj[0][frame_start:frame_end:frame_stride] for i in range(len(file_list) - 1): traj_all = traj_all.join( rep_traj[i + 1][frame_start:frame_end:frame_stride]) # Get torsion list: torsion_list = CGModel.get_torsion_list(cgmodel) # Assign torsion types: torsion_types, torsion_array, torsion_sub_arrays, n_i, i_torsion_type, torsion_dict, inv_torsion_dict = \ assign_torsion_types(cgmodel, torsion_list) # Compute specified torsion angles over all frames: for i in range(i_torsion_type): if inv_torsion_dict[str(i + 1)] == backbone_torsion_type: # Compute all torsion values in trajectory # This returns an [nframes x n_torsions] array torsion_val_array = md.compute_dihedrals( traj_all, torsion_sub_arrays[str(i + 1)]) # Convert to degrees: torsion_val_array = (180 / np.pi) * torsion_val_array return torsion_val_array, traj_all
def assign_bond_types(cgmodel, bond_list): """Internal function for assigning bond types""" bond_types = [] bond_array = np.zeros((len(bond_list), 2)) # Relevant bond types are added to a dictionary as they are discovered bond_dict = {} # Create an inverse dictionary for getting bond string name from integer type inv_bond_dict = {} # Counter for number of bond types found: i_bond_type = 0 # Assign bond types: for i in range(len(bond_list)): bond_array[i, 0] = bond_list[i][0] bond_array[i, 1] = bond_list[i][1] particle_types = [ CGModel.get_particle_type_name(cgmodel, bond_list[i][0]), CGModel.get_particle_type_name(cgmodel, bond_list[i][1]) ] string_name = "" reverse_string_name = "" for particle in particle_types: string_name += f"{particle}_" string_name = string_name[:-1] for particle in reversed(particle_types): reverse_string_name += f"{particle}_" reverse_string_name = reverse_string_name[:-1] if (string_name in bond_dict.keys()) == False: # New bond type found, add to bond dictionary i_bond_type += 1 bond_dict[string_name] = i_bond_type bond_dict[reverse_string_name] = i_bond_type # For inverse dict we will use only the forward name based on first encounter inv_bond_dict[str(i_bond_type)] = string_name # print(f"adding new bond type {i_bond_type}: {string_name} to dictionary") # print(f"adding reverse version {i_bond_type}: {reverse_string_name} to dictionary") bond_types.append(bond_dict[string_name]) # Sort bonds by type into separate sub arrays for mdtraj compute_distances bond_sub_arrays = {} for i in range(i_bond_type): bond_sub_arrays[str(i + 1)] = np.zeros((bond_types.count(i + 1), 2)) # Counter vector for all bond types n_i = np.zeros((i_bond_type, 1), dtype=int) for i in range(len(bond_list)): bond_sub_arrays[str(bond_types[i])][n_i[bond_types[i] - 1], :] = bond_array[i, :] n_i[bond_types[i] - 1] += 1 return bond_types, bond_array, bond_sub_arrays, n_i, i_bond_type, bond_dict, inv_bond_dict
"bb_bb_bb_sc_torsion_periodicity": 1, } # Get initial positions from local file positions = PDBFile("24mer_1b1s_initial_structure.pdb").getPositions() # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, equil_torsion_angles=equil_torsion_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, sequence=sequence, positions=positions, monomer_types=[A], ) # store the cg model so that we can do various analyses. cgmodel.export("stored_cgmodel.pkl") if not os.path.exists(output_data) or overwrite_files == True: run_replica_exchange( cgmodel.topology,
def signac_run_replica_exchange(job): # Run replica exchange simulation for current job parameters # Job settings output_directory = os.path.join(job.workspace(), "output") if not os.path.exists(output_directory): os.mkdir(output_directory) overwrite_files = True # overwrite files. # Replica exchange simulation settings total_simulation_time = 0.05 * unit.nanosecond simulation_time_step = 10.0 * unit.femtosecond total_steps = int(np.floor(total_simulation_time / simulation_time_step)) output_data = os.path.join(output_directory, "output.nc") number_replicas = 36 min_temp = 100.0 * unit.kelvin max_temp = 500.0 * unit.kelvin temperature_list = get_temperature_list(min_temp, max_temp, number_replicas) exchange_frequency = 100 # Number of steps between exchange attempts collision_frequency = 5 / unit.picosecond include_bond_forces = True include_bond_angle_forces = True include_nonbonded_forces = True include_torsion_forces = True constrain_bonds = False mass = 100.0 * unit.amu # mass and charge are defaults. bb = { "particle_type_name": "bb", "sigma": job.sp.sigma_bb * unit.nanometer, "epsilon": job.sp.epsilon_bb * unit.kilojoules_per_mole, "mass": mass } sc = { "particle_type_name": "sc", "sigma": job.sp.sigma_sc * unit.nanometer, "epsilon": job.sp.epsilon_sc * unit.kilojoules_per_mole, "mass": mass } # Monomer definition A = { "monomer_name": "A", "particle_sequence": [bb, sc], "bond_list": [[0, 1]], "start": 0, "end": 0, } sequence = 24 * [A] # Bond definitions bond_lengths = { "default_bond_length": job.sp.equil_bond_length * unit.nanometer } bond_force_constants = { "default_bond_force_constant": job.sp.k_bond * unit.kilojoule_per_mole / unit.nanometer / unit.nanometer } # Bond angle definitions bond_angle_force_constants = { "default_bond_angle_force_constant": job.sp.k_angle * unit.kilojoule_per_mole / unit.radian / unit.radian } equil_bond_angles = { "default_equil_bond_angle": job.sp.equil_bond_angle * unit.degrees } # torsion angle definitions torsion_force_constants = { "default_torsion_force_constant": 0.0 * unit.kilojoule_per_mole, "bb_bb_bb_bb_torsion_force_constant": job.sp.k_torsion * unit.kilojoule_per_mole } torsion_phase_angles = { "sc_bb_bb_sc_torsion_phase_angle": 0 * unit.degrees, "bb_bb_bb_bb_torsion_phase_angle": job.sp.torsion_phase_angle * unit.degrees, "bb_bb_bb_sc_torsion_phase_angle": 0 * unit.degrees, } torsion_periodicities = { "sc_bb_bb_sc_torsion_periodicity": job.sp.torsion_periodicity, "bb_bb_bb_bb_torsion_periodicity": job.sp.torsion_periodicity, "bb_bb_bb_sc_torsion_periodicity": job.sp.torsion_periodicity, } # Get initial positions from local file pdb_path = os.path.join(proj_directory, "24mer_1b1s_initial_structure.pdb") positions = PDBFile(pdb_path).getPositions() # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, sequence=sequence, positions=positions, monomer_types=[A], ) # store the cg model so that we can do various analyses. cgmodel.export(job.fn("stored_cgmodel.pkl")) if not os.path.exists(output_data) or overwrite_files == True: run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, friction=collision_frequency, temperature_list=temperature_list, simulation_time_step=simulation_time_step, total_simulation_time=total_simulation_time, exchange_frequency=exchange_frequency, output_data=output_data, ) else: print("Replica output files exist")
def calc_torsion_distribution( cgmodel, file_list, nbins=180, frame_start=0, frame_stride=1, frame_end=-1, plot_per_page=2, temperature_list=None, plotfile="torsion_hist.pdf" ): """ Calculate and plot all torsion distributions from a CGModel object and pdb or dcd trajectory :param cgmodel: CGModel() object :type cgmodel: class :param file_list: path to pdb or dcd trajectory file(s) :type file_list: str or list(str) :param nbins: number of bins spanning the range of -180 to 180 degrees, default = 180 :type nbins: int :param frame_start: First frame in trajectory file to use for analysis. :type frame_start: int :param frame_stride: Advance by this many frames when reading trajectories. :type frame_stride: int :param frame_end: Last frame in trajectory file to use for analysis. :type frame_end: int :param plot_per_page: number of subplots to display on each page (default=2) :type plot_per_page: int :param temperature_list: list of temperatures corresponding to file_list. If None, file names will be the plot labels. :type temperature_list: list(Quantity()) :param plotfile: Base filename for saving torsion distribution pdf plots :type plotfile: str :returns: - torsion_hist_data ( dict ) """ # Convert file_list to list if a single string: if type(file_list) == str: # Single file file_list = file_list.split() # Get torsion list torsion_list = CGModel.get_torsion_list(cgmodel) # Assign torsion types torsion_types, torsion_array, torsion_sub_arrays, n_i, i_torsion_type, torsion_dict, inv_torsion_dict = \ assign_torsion_types(cgmodel, torsion_list) # Create dictionary for saving torsion histogram data: torsion_hist_data = {} # Set bin edges: torsion_bin_edges = np.linspace(-180,180,nbins+1) torsion_bin_centers = np.zeros((len(torsion_bin_edges)-1,1)) for i in range(len(torsion_bin_edges)-1): torsion_bin_centers[i] = (torsion_bin_edges[i]+torsion_bin_edges[i+1])/2 file_index = 0 for file in file_list: # Load in a trajectory file: if file[-3:] == 'dcd': traj = md.load(file,top=md.Topology.from_openmm(cgmodel.topology)) else: traj = md.load(file) # Select frames for analysis: if frame_end == -1: frame_end = traj.n_frames traj = traj[frame_start:frame_end:frame_stride] nframes = traj.n_frames # Create inner dictionary for current file: if temperature_list is not None: file_key = f"{temperature_list[file_index].value_in_unit(unit.kelvin):.2f}" else: file_key = file[:-4] torsion_hist_data[file_key] = {} for i in range(i_torsion_type): # Compute all torsion values in trajectory # This returns an [nframes x n_torsions] array torsion_val_array = md.compute_dihedrals( traj,torsion_sub_arrays[str(i+1)]) # Reshape arrays and convert to degrees: torsion_val_array = (180/np.pi)*np.reshape(torsion_val_array, (nframes*n_i[i][0],1)) # Histogram and plot results: n_out, bin_edges_out = np.histogram( torsion_val_array, bins=torsion_bin_edges,density=True) torsion_hist_data[file_key][f"{inv_torsion_dict[str(i+1)]}_density"]=n_out torsion_hist_data[file_key][f"{inv_torsion_dict[str(i+1)]}_bin_centers"]=torsion_bin_centers file_index += 1 plot_distribution( inv_torsion_dict, torsion_hist_data, xlabel="Torsion angle (degrees)", ylabel="Probability density", xlim=[-180,180], figure_title="Torsion_distributions", file_name=f"{plotfile}", plot_per_page=plot_per_page, ) return torsion_hist_data
def calc_ramachandran( cgmodel, file_list, nbin_theta=180, nbin_alpha=180, frame_start=0, frame_stride=1, frame_end=-1, plotfile="ramachandran.pdf", backbone_angle_type = "bb_bb_bb", backbone_torsion_type = "bb_bb_bb_bb", colormap="nipy_spectral", temperature_list=None, ): """ Calculate and plot ramachandran plot for backbone bond bending-angle and torsion angle, given a CGModel object and pdb or dcd trajectory. :param cgmodel: CGModel() object :type cgmodel: class :param file_list: path to pdb or dcd trajectory file(s) - can be a list or single string :type file_list: str or list(str) :param nbin_theta: number of bins for bond-bending angle (spanning from 0 to 180 degrees) :type nbin_theta: int :param nbin_alpha: number of bins for torsion angle (spanning from -180 to +180 degrees) :type nbin_alpha: :param frame_start: First frame in trajectory file to use for analysis. :type frame_start: int :param frame_stride: Advance by this many frames when reading trajectories. :type frame_stride: int :param frame_end: Last frame in trajectory file to use for analysis. :type frame_end: int :param plotfile: Filename for saving torsion distribution pdf plots :type plotfile: str :param backbone_angle_type: particle sequence of the backbone angles (default="bb_bb_bb") - for now only single sequence permitted :type backbone_angle_type: str :param backbone_torsion_type: particle sequence of the backbone angles (default="bb_bb_bb_bb") - for now only single sequence permitted :type backbone_torsion_type: str :param colormap: matplotlib pyplot colormap to use (default='nipy_spectral') :type colormap: str (case sensitive) :param temperature_list: list of temperatures corresponding to file_list. If None, no subplot labels will be used. :type temperature_list: list(Quantity()) :returns: - hist_data ( dict ) - xedges ( dict ) - yedges ( dict ) """ # Convert file_list to list if a single string: if type(file_list) == str: # Single file file_list = file_list.split() # Store angle, torsion values by filename for computing global colormap ang_val_array = {} torsion_val_array = {} for file in file_list: # Load in a trajectory file: if file[-3:] == 'dcd': traj = md.load(file,top=md.Topology.from_openmm(cgmodel.topology)) else: traj = md.load(file) # Select frames for analysis: if frame_end == -1: frame_end = traj.n_frames traj = traj[frame_start:frame_end:frame_stride] nframes = traj.n_frames # Get angle list angle_list = CGModel.get_bond_angle_list(cgmodel) # Assign angle types: ang_types, ang_array, ang_sub_arrays, n_i, i_angle_type, ang_dict, inv_ang_dict = \ assign_angle_types(cgmodel, angle_list) # Set bin edges: angle_bin_edges = np.linspace(0,180,nbin_theta+1) angle_bin_centers = np.zeros((len(angle_bin_edges)-1,1)) for i in range(len(angle_bin_edges)-1): angle_bin_centers[i] = (angle_bin_edges[i]+angle_bin_edges[i+1])/2 for i in range(i_angle_type): if inv_ang_dict[str(i+1)] == backbone_angle_type: # Compute all angle values in trajectory # This returns an [nframes x n_angles] array ang_val_array[file] = md.compute_angles(traj,ang_sub_arrays[str(i+1)]) # We will have different numbers of bond-bending angle and torsion angle. # We will set a convention of omitting the last angle value. # Convert to degrees and exclude last angle: ang_val_array[file] = (180/np.pi)*ang_val_array[file][:,:-1] # Reshape array: ang_val_array[file] = np.reshape(ang_val_array[file], (nframes*(n_i[i]-1)[0],1)) # Get torsion list torsion_list = CGModel.get_torsion_list(cgmodel) # Assign torsion types torsion_types, torsion_array, torsion_sub_arrays, n_j, i_torsion_type, torsion_dict, inv_torsion_dict = \ assign_torsion_types(cgmodel, torsion_list) # Set bin edges: torsion_bin_edges = np.linspace(-180,180,nbin_alpha+1) torsion_bin_centers = np.zeros((len(torsion_bin_edges)-1,1)) for i in range(len(torsion_bin_edges)-1): torsion_bin_centers[i] = (torsion_bin_edges[i]+torsion_bin_edges[i+1])/2 for i in range(i_torsion_type): if inv_torsion_dict[str(i+1)] == backbone_torsion_type: # Compute all torsion values in trajectory # This returns an [nframes x n_torsions] array torsion_val_array[file] = md.compute_dihedrals( traj,torsion_sub_arrays[str(i+1)]) # Convert to degrees: torsion_val_array[file] *= (180/np.pi) # Reshape array torsion_val_array[file] = np.reshape(torsion_val_array[file], (nframes*n_j[i][0],1)) # 2d histogram the data and plot: hist_data, xedges, yedges = plot_2d_distribution( file_list, torsion_val_array, ang_val_array, torsion_bin_edges, angle_bin_edges, plotfile, colormap, xlabel='Alpha (degrees)', ylabel='Theta (degrees)', temperature_list=temperature_list) return hist_data, xedges, yedges
torsion_periodicities = {"bb_bb_bb_bb_period": 1} # ,'sc_bb_bb_sc_period': 2} # Initiate cgmodel using positions from local file positions = PDBFile("init.pdb").getPositions() # Build a coarse grained model using the positions for the initial structure cgmodel = CGModel( polymer_length=polymer_length, backbone_lengths=backbone_lengths, sidechain_lengths=sidechain_lengths, sidechain_positions=sidechain_positions, masses=masses, sigmas=sigmas, epsilons=epsilons, bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, torsion_force_constants=torsion_force_constants, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, positions=positions, ) if os.path.exists(output_data): # Search for existing data, and read it if possible replica_energies, replica_positions, replica_states = read_replica_exchange_data( system=cgmodel.system, topology=cgmodel.topology,
"sigmas": sigmas, } monomer_types = [A, B] sequence = [A, A, A, B, A, A, A, B, A, A, A, B] polymer_length = len(sequence) cgmodel = CGModel( polymer_length=polymer_length, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, equil_torsion_angles=equil_torsion_angles, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, heteropolymer=True, monomer_types=monomer_types, sequence=sequence, ) if not os.path.exists(output_data): replica_energies, replica_positions, replica_states = run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, temperature_list=temperature_list, simulation_time_step=simulation_time_step,
wall_clock_time_list = [] for polymer_length in [8, 10, 20, 30]: print("Running simulations with 'polymer_length' =" + str(polymer_length)) cg_model = CGModel( polymer_length=polymer_length, backbone_lengths=backbone_lengths, sidechain_lengths=sidechain_lengths, sidechain_positions=sidechain_positions, masses=masses, sigmas=sigmas, epsilons=epsilons, bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, random_positions=True, ) output_data = str(str(output_directory) + "/" + str(polymer_length)) if not os.path.exists(output_data): os.mkdir(output_data)
for i in range(sigma_increments) ] df_ij_list = [] ddf_ij_list = [] Delta_u_list = [] dDelta_u_list = [] Delta_s_list = [] dDelta_s_list = [] C_v_list = [] dC_v_list = [] for sigma in sigma_list: output_data = str(str(top_directory) + "/rep_ex_" + str(sigma) + ".nc") sigmas = {"bb_bb_sigma": sigma, "bb_sc_sigma": sigma, "sc_sc_sigma": sigma} cgmodel = CGModel(sigmas=sigmas) if not os.path.exists(output_data): print( "Performing simulations and free energy analysis for a coarse grained model" ) print("with sigma values of " + str(sigma)) replica_energies, replica_positions, replica_states = run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, temperature_list=temperature_list, simulation_time_step=simulation_time_step, total_simulation_time=total_simulation_time, print_frequency=print_frequency, output_data=output_data, )
simulation_time_step = 5.0 * unit.femtosecond total_steps = round(total_simulation_time.__div__(simulation_time_step)) # Yank (replica exchange) simulation settings output_data = str(str(top_directory) + "/output.nc") number_replicas = 20 min_temp = 50.0 * unit.kelvin max_temp = 300.0 * unit.kelvin temperature_list = get_temperature_list(min_temp, max_temp, number_replicas) print("Using " + str(len(temperature_list)) + " replicas.") if total_steps > 10000: exchange_attempts = round(total_steps / 1000) else: exchange_attempts = 10 cgmodel = CGModel() if not os.path.exists(output_data): replica_energies, replica_positions, replica_states = run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, temperature_list=temperature_list, simulation_time_step=simulation_time_step, total_simulation_time=total_simulation_time, print_frequency=print_frequency, output_data=output_data, ) make_replica_pdb_files(cgmodel.topology, replica_positions) else: replica_energies, replica_positions, replica_states = read_replica_exchange_data(
def test_sums_periodic_torsions_5(): # Test cg_model with sums of periodic torsions - test 5 # Two periodic torsion terms, parameters input as quantities with list values # Parameters are applied to all torsion types using the default input method # Coarse grained model settings include_bond_forces = True include_bond_angle_forces = True include_nonbonded_forces = True include_torsion_forces = True constrain_bonds = False # Bond definitions bond_length = 1.5 * unit.angstrom bond_lengths = { "bb_bb_bond_length": bond_length, "bb_sc_bond_length": bond_length, "sc_sc_bond_length": bond_length, } bond_force_constant = 1000 * unit.kilojoule_per_mole / unit.nanometer / unit.nanometer bond_force_constants = { "bb_bb_bond_force_constant": bond_force_constant, "bb_sc_bond_force_constant": bond_force_constant, "sc_sc_bond_force_constant": bond_force_constant, } # Particle definitions mass = 100.0 * unit.amu r_min = 1.5 * bond_length # Lennard-Jones potential r_min # Factor of /(2.0**(1/6)) is applied to convert r_min to sigma sigma = r_min / (2.0**(1.0 / 6.0)) epsilon = 0.5 * unit.kilojoule_per_mole bb = { "particle_type_name": "bb", "sigma": sigma, "epsilon": epsilon, "mass": mass } sc = { "particle_type_name": "sc", "sigma": sigma, "epsilon": epsilon, "mass": mass } # Bond angle definitions bond_angle_force_constant = 100 * unit.kilojoule_per_mole / unit.radian / unit.radian bond_angle_force_constants = { "bb_bb_bb_bond_angle_force_constant": bond_angle_force_constant, "bb_bb_sc_bond_angle_force_constant": bond_angle_force_constant, } # OpenMM requires angle definitions in units of radians bb_bb_bb_equil_bond_angle = 120.0 * unit.degrees bb_bb_sc_equil_bond_angle = 120.0 * unit.degrees equil_bond_angles = { "bb_bb_bb_equil_bond_angle": bb_bb_bb_equil_bond_angle, "bb_bb_sc_equil_bond_angle": bb_bb_sc_equil_bond_angle, } # Torsion angle definitions torsion_force_constants = { "default_torsion_force_constant": [5, 10] * unit.kilojoule_per_mole, } torsion_phase_angles = { "default_torsion_phase_angle": [0, 180] * unit.degrees, } torsion_periodicities = { "default_torsion_periodicity": [1, 3], } # Monomer definitions A = { "monomer_name": "A", "particle_sequence": [bb, sc], "bond_list": [[0, 1]], "start": 0, "end": 0, } sequence = 24 * [A] pdb_path = os.path.join(data_path, "24mer_1b1s_initial_structure.pdb") positions = PDBFile(pdb_path).getPositions() # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, positions=positions, sequence=sequence, monomer_types=[A], ) # Check the number of periodic torsions terms: n_torsion_forces = cgmodel.system.getForces()[3].getNumTorsions() assert n_torsion_forces == 176
def create_cgmodel(): # Coarse grained model settings include_bond_forces = True include_bond_angle_forces = True include_nonbonded_forces = True include_torsion_forces = True constrain_bonds = False # Bond definitions bond_length = 1.5 * unit.angstrom bond_lengths = { "bb_bb_bond_length": bond_length, "bb_sc_bond_length": bond_length, "sc_sc_bond_length": bond_length, } bond_force_constant = 1000 * unit.kilojoule_per_mole / unit.nanometer / unit.nanometer bond_force_constants = { "bb_bb_bond_force_constant": bond_force_constant, "bb_sc_bond_force_constant": bond_force_constant, "sc_sc_bond_force_constant": bond_force_constant, } # Particle definitions mass = 100.0 * unit.amu r_min = 1.5 * bond_length # Lennard-Jones potential r_min # Factor of /(2.0**(1/6)) is applied to convert r_min to sigma sigma = r_min / (2.0**(1.0 / 6.0)) epsilon = 0.5 * unit.kilojoule_per_mole bb = { "particle_type_name": "bb", "sigma": sigma, "epsilon": epsilon, "mass": mass } sc = { "particle_type_name": "sc", "sigma": sigma, "epsilon": epsilon, "mass": mass } # Bond angle definitions bond_angle_force_constant = 100 * unit.kilojoule_per_mole / unit.radian / unit.radian bond_angle_force_constants = { "bb_bb_bb_bond_angle_force_constant": bond_angle_force_constant, "bb_bb_sc_bond_angle_force_constant": bond_angle_force_constant, } # OpenMM requires angle definitions in units of radians bb_bb_bb_equil_bond_angle = 120.0 * unit.degrees bb_bb_sc_equil_bond_angle = 120.0 * unit.degrees equil_bond_angles = { "bb_bb_bb_equil_bond_angle": bb_bb_bb_equil_bond_angle, "bb_bb_sc_equil_bond_angle": bb_bb_sc_equil_bond_angle, } # Torsion angle definitions torsion_force_constant = 20.0 * unit.kilojoule_per_mole torsion_force_constants = { "bb_bb_bb_bb_torsion_force_constant": torsion_force_constant, "bb_bb_bb_sc_torsion_force_constant": torsion_force_constant } bb_bb_bb_bb_torsion_phase_angle = 75.0 * unit.degrees bb_bb_bb_sc_torsion_phase_angle = 75.0 * unit.degrees torsion_phase_angles = { "bb_bb_bb_bb_torsion_phase_angle": bb_bb_bb_bb_torsion_phase_angle, "bb_bb_bb_sc_torsion_phase_angle": bb_bb_bb_sc_torsion_phase_angle } torsion_periodicities = { "bb_bb_bb_bb_torsion_periodicity": 3, "bb_bb_bb_sc_torsion_periodicity": 3 } # Monomer definitions A = { "monomer_name": "A", "particle_sequence": [bb, sc], "bond_list": [[0, 1]], "start": 0, "end": 0, } sequence = 24 * [A] pdb_path = os.path.join(data_path, "24mer_1b1s_initial_structure.pdb") positions = PDBFile(pdb_path).getPositions() # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, positions=positions, sequence=sequence, monomer_types=[A], ) return cgmodel
def calc_bond_length_distribution(cgmodel, file_list, nbins=90, frame_start=0, frame_stride=1, frame_end=-1, plot_per_page=2, temperature_list=None, plotfile="bond_hist.pdf"): """ Calculate and plot all bond length distributions from a CGModel object and trajectory :param cgmodel: CGModel() object :type cgmodel: class :param file_list: path to pdb or dcd trajectory file(s) :type file_list: str or list(str) :param nbins: number of histogram bins :type nbins: int :param frame_start: First frame in trajectory file to use for analysis. :type frame_start: int :param frame_stride: Advance by this many frames when reading trajectories. :type frame_stride: int :param frame_end: Last frame in trajectory file to use for analysis. :type frame_end: int :param plot_per_page: number of subplots to display on each page (default=2) :type plot_per_page: int :param temperature_list: list of temperatures corresponding to file_list. If None, file names will be the plot labels. :type temperature_list: list(Quantity()) :param plotfile: filename for saving bond length distribution pdf plots :type plotfile: str :returns: - bond_hist_data ( dict ) """ # Convert file_list to list if a single string: if type(file_list) == str: # Single file file_list = file_list.split() # Create dictionary for saving bond histogram data: bond_hist_data = {} # Get bond list bond_list = CGModel.get_bond_list(cgmodel) # Assign bond types: bond_types, bond_array, bond_sub_arrays, n_i, i_bond_type, bond_dict, inv_bond_dict = \ assign_bond_types(cgmodel, bond_list) file_index = 0 for file in file_list: # Load in a trajectory file: if file[-3:] == 'dcd': traj = md.load(file, top=md.Topology.from_openmm(cgmodel.topology)) else: traj = md.load(file) # Select frames for analysis: if frame_end == -1: frame_end = traj.n_frames traj = traj[frame_start:frame_end:frame_stride] nframes = traj.n_frames # Create inner dictionary for current file: if temperature_list is not None: file_key = f"{temperature_list[file_index].value_in_unit(unit.kelvin):.2f}" else: file_key = file[:-4] bond_hist_data[file_key] = {} for i in range(i_bond_type): # Compute all bond distances in trajectory # This returns an [nframes x n_bonds] array bond_val_array = md.compute_distances(traj, bond_sub_arrays[str(i + 1)]) # Reshape arrays: bond_val_array = np.reshape(bond_val_array, (nframes * n_i[i][0], 1)) # Histogram and plot results: n_out, bin_edges_out = np.histogram(bond_val_array, bins=nbins, density=True) bond_bin_centers = np.zeros((len(bin_edges_out) - 1, 1)) for j in range(len(bin_edges_out) - 1): bond_bin_centers[j] = (bin_edges_out[j] + bin_edges_out[j + 1]) / 2 bond_hist_data[file_key][ f"{inv_bond_dict[str(i+1)]}_density"] = n_out bond_hist_data[file_key][ f"{inv_bond_dict[str(i+1)]}_bin_centers"] = bond_bin_centers file_index += 1 plot_distribution( inv_bond_dict, bond_hist_data, xlabel="Bond length (nm)", ylabel="Probability density", figure_title="Bond distributions", file_name=f"{plotfile}", plot_per_page=plot_per_page, ) return bond_hist_data
"start": 0, "end": 1 } sequence = 12 * [A] # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, sequence=sequence, constrain_bonds=constrain_bonds, random_positions=random_positions, monomer_types=[A], ) file_name = "12mer_2b1s_initial_structure.pdb" # file_name = "12mer_1b1s_initial_structure.pdb" # file_name = "24mer_1b1s_initial_structure.pdb" # file_name = "12mer_1b2s_initial_structure.pdb" # this one looks a bit off, and takes a long time # file_name = "12mer_2b1s_initial_structure.pdb" # file_name = "12mer_2b2s_initial_structure.pdb"
def test_random_builder(tmpdir): """See if the random builder can build a simple 1b1s model""" # Coarse grained model settings include_bond_forces = True include_bond_angle_forces = True include_nonbonded_forces = True include_torsion_forces = False constrain_bonds = False random_positions = True # Bond definitions bond_length = 1.5 * unit.angstrom bond_lengths = { "bb_bb_bond_length": bond_length, "bb_sc_bond_length": bond_length, "sc_sc_bond_length": bond_length, } bond_force_constant = 1000 * unit.kilojoule_per_mole / unit.nanometer / unit.nanometer bond_force_constants = { "bb_bb_bond_force_constant": bond_force_constant, "bb_sc_bond_force_constant": bond_force_constant, "sc_sc_bond_force_constant": bond_force_constant, } # Particle definitions mass = 100.0 * unit.amu r_min = 1.5 * bond_length # Lennard-Jones potential r_min # Factor of /(2.0**(1/6)) is applied to convert r_min to sigma sigma = r_min / (2.0 ** (1.0 / 6.0)) epsilon = 0.5 * unit.kilojoule_per_mole bb = {"particle_type_name": "bb", "sigma": sigma, "epsilon": epsilon, "mass": mass} sc = {"particle_type_name": "sc", "sigma": sigma, "epsilon": epsilon, "mass": mass} # Bond angle definitions bond_angle_force_constant = 100 * unit.kilojoule_per_mole / unit.radian / unit.radian bond_angle_force_constants = { "bb_bb_bb_bond_angle_force_constant": bond_angle_force_constant, "bb_bb_sc_bond_angle_force_constant": bond_angle_force_constant, } # OpenMM requires angle definitions in units of radians bb_bb_bb_equil_bond_angle = 120.0 * unit.degrees bb_bb_sc_equil_bond_angle = 120.0 * unit.degrees equil_bond_angles = { "bb_bb_bb_equil_bond_angle": bb_bb_bb_equil_bond_angle, "bb_bb_sc_equil_bond_angle": bb_bb_sc_equil_bond_angle, } # Torsion angle definitions torsion_force_constant = 20.0 * unit.kilojoule_per_mole torsion_force_constants = { "bb_bb_bb_bb_torsion_force_constant": torsion_force_constant, "bb_bb_bb_sc_torsion_force_constant": torsion_force_constant } bb_bb_bb_bb_torsion_phase_angle = 75.0 * unit.degrees bb_bb_bb_sc_torsion_phase_angle = 75.0 * unit.degrees torsion_phase_angles = { "bb_bb_bb_bb_torsion_phase_angle": bb_bb_bb_bb_torsion_phase_angle, "bb_bb_bb_sc_torsion_phase_angle": bb_bb_bb_sc_torsion_phase_angle } torsion_periodicities = { "bb_bb_bb_bb_torsion_periodicity": 3, "bb_bb_bb_sc_torsion_periodicity": 3} # Monomer definitions A = { "monomer_name": "A", "particle_sequence": [bb, sc], "bond_list": [[0, 1]], "start": 0, "end": 0, } sequence = 5 * [A] # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb,sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, sequence=sequence, constrain_bonds=constrain_bonds, random_positions=random_positions, monomer_types=[A], ) output_directory = tmpdir.mkdir("output") filename = f"{output_directory}/5mer_1b1s_builder_test.pdb" write_pdbfile_without_topology(cgmodel, filename) positions = PDBFile(filename).getPositions() assert len(positions)==10
def signac_run_CEI_replica_exchange(job): # Run replica exchange simulation for current job parameters print(f'job_parameters:') print(job.sp) rep_exch_begin = time.perf_counter() # Job settings output_directory = os.path.join(job.workspace(),"output_CEI") if not os.path.exists(output_directory): os.mkdir(output_directory) overwrite_files = True # overwrite files. global_context_cache.platform = openmm.Platform.getPlatformByName("CUDA") # Replica exchange simulation settings total_simulation_time = 200.0 * unit.nanosecond simulation_time_step = 5.0 * unit.femtosecond total_steps = int(np.floor(total_simulation_time / simulation_time_step)) output_data = os.path.join(output_directory, "output.nc") number_replicas = job.sp.n_replica min_temp = 200.0 * unit.kelvin max_temp = 600.0 * unit.kelvin # Load in CEI temperature list: temperature_list = pickle.load(open(job.fn("opt_T_spacing.pkl"),"rb")) exchange_frequency = job.sp.exch_freq # Number of steps between exchange attempts collision_frequency = job.sp.coll_freq/unit.picosecond include_bond_forces = True include_bond_angle_forces = True include_nonbonded_forces = True include_torsion_forces = True constrain_bonds = False mass = 100.0 * unit.amu # mass and charge are defaults. bb = { "particle_type_name": "bb", "sigma": job.sp.sigma_bb * unit.angstrom, "epsilon": job.sp.epsilon_bb * unit.kilojoules_per_mole, "mass": mass } sc = { "particle_type_name": "sc", "sigma": job.sp.sigma_sc * unit.angstrom, "epsilon": job.sp.epsilon_sc * unit.kilojoules_per_mole, "mass": mass } # Monomer definition A = { "monomer_name": "A", "particle_sequence": [bb, sc], "bond_list": [[0, 1]], "start": 0, "end": 0, } sequence = 24 * [A] # Bond definitions bond_lengths = {"default_bond_length": job.sp.equil_bond_length * unit.nanometer} bond_force_constants = { "default_bond_force_constant": job.sp.k_bond * unit.kilojoule_per_mole / unit.nanometer / unit.nanometer } # Bond angle definitions bond_angle_force_constants = { "default_bond_angle_force_constant": job.sp.k_angle * unit.kilojoule_per_mole / unit.radian / unit.radian } equil_bond_angles = { "default_equil_bond_angle": job.sp.equil_bond_angle_bb_bb_sc * unit.degrees, "bb_bb_bb_equil_bond_angle": job.sp.equil_bond_angle_bb_bb_bb * unit.degrees} # torsion angle definitions torsion_force_constants = { "default_torsion_force_constant": 0.0 * unit.kilojoule_per_mole, "bb_bb_bb_bb_torsion_force_constant": job.sp.k_torsion * unit.kilojoule_per_mole} # Need to substract 180 degrees from specified torsion for mdtraj consistency torsion_phase_angles = { "sc_bb_bb_sc_torsion_phase_angle": 0 * unit.degrees, "bb_bb_bb_bb_torsion_phase_angle": (job.sp.equil_torsion_angle_bb_bb_bb_bb-180) * unit.degrees, "bb_bb_bb_sc_torsion_phase_angle": 0 * unit.degrees, } torsion_periodicities = { "sc_bb_bb_sc_torsion_periodicity": job.sp.torsion_periodicity, "bb_bb_bb_bb_torsion_periodicity": job.sp.torsion_periodicity, "bb_bb_bb_sc_torsion_periodicity": job.sp.torsion_periodicity, } # Get initial positions from local file pdb_path = os.path.join(proj_directory, f"initial_structure_trial_{job.sp.trial}.pdb") positions = PDBFile(pdb_path).getPositions() # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, positions=positions, sequence=sequence, monomer_types=[A], ) # store the cg model so that we can do various analyses. cgmodel.export(job.fn("stored_cgmodel.pkl")) if not os.path.exists(output_data) or overwrite_files == True: run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, friction=collision_frequency, temperature_list=temperature_list, simulation_time_step=simulation_time_step, total_simulation_time=total_simulation_time, exchange_frequency=exchange_frequency, output_data=output_data, ) else: print("Replica output files exist") rep_exch_end = time.perf_counter() print(f'replica exchange run time: {rep_exch_end-rep_exch_begin}')
def calc_2d_distribution( cgmodel, file_list, nbin_xvar=180, nbin_yvar=180, frame_start=0, frame_stride=1, frame_end=-1, plotfile="2d_hist.pdf", xvar_name = "bb_bb_bb", yvar_name = "bb_bb_bb_bb", colormap="nipy_spectral", temperature_list=None, ): """ Calculate and plot 2d histogram for any 2 bonded variables, given a CGModel object and pdb or dcd trajectory. :param cgmodel: CGModel() object :type cgmodel: class :param file_list: path to pdb or dcd trajectory file(s) - can be a list or single string :type file_list: str or list(str) :param nbin_xvar: number of bins for x bonded variable :type nbin_xvar: int :param nbin_yvar: number of bins for y bonded variable :type nbin_yvar: :param frame_start: First frame in trajectory file to use for analysis. :type frame_start: int :param frame_stride: Advance by this many frames when reading trajectories. :type frame_stride: int :param frame_end: Last frame in trajectory file to use for analysis. :type frame_end: int :param plotfile: Filename for saving torsion distribution pdf plots :type plotfile: str :param xvar_name: particle sequence of the x bonded parameter (default="bb_bb_bb") :type xvar_name: str :param yvar_name: particle sequence of the y bonded parameter (default="bb_bb_bb_bb") :type yvar_name: str :param colormap: matplotlib pyplot colormap to use (default='nipy_spectral') :type colormap: str (case sensitive) :param temperature_list: list of temperatures corresponding to file_list. If None, no subplot labels will be used. :type temperature_list: list(Quantity()) :returns: - hist_data ( dict ) - xedges ( dict ) - yedges ( dict ) """ # Convert file_list to list if a single string: if type(file_list) == str: # Single file file_list = file_list.split() # Store angle, torsion values by filename for computing global colormap xvar_val_array = {} yvar_val_array = {} # Store the reverse name of the bonded type (need to check both) # x variable particle_list = [] particle = "" for c in xvar_name: if c == '_': particle_list.append(particle) particle = "" else: particle += c particle_list.append(particle) particle_list_reverse = particle_list[::-1] xvar_name_reverse = "" for par in particle_list_reverse: xvar_name_reverse += par xvar_name_reverse += "_" xvar_name_reverse = xvar_name_reverse[:-1] # y variable particle_list = [] particle = "" for c in yvar_name: if c == '_': particle_list.append(particle) particle = "" else: particle += c particle_list.append(particle) particle_list_reverse = particle_list[::-1] yvar_name_reverse = "" for par in particle_list_reverse: yvar_name_reverse += par yvar_name_reverse += "_" yvar_name_reverse = yvar_name_reverse[:-1] for file in file_list: # Load in a trajectory file: if file[-3:] == 'dcd': traj = md.load(file,top=md.Topology.from_openmm(cgmodel.topology)) else: traj = md.load(file) # Select frames for analysis: if frame_end == -1: frame_end = traj.n_frames traj = traj[frame_start:frame_end:frame_stride] nframes = traj.n_frames # x variable # Determine parameter type of xvar: n_particle_x = xvar_name.count('_')+1 if n_particle_x == 2: # Bond # Get bond list bond_list = CGModel.get_bond_list(cgmodel) # Assign bond types: bond_types, bond_array, bond_sub_arrays, n_i, i_bond_type, bond_dict, inv_bond_dict = \ assign_bond_types(cgmodel, bond_list) for i in range(i_bond_type): if inv_bond_dict[str(i+1)] == xvar_name or inv_bond_dict[str(i+1)] == xvar_name_reverse: # Compute all bond length values in trajectory # This returns an [nframes x n_bonds] array xvar_val_array[file] = md.compute_distances(traj,bond_sub_arrays[str(i+1)]) # Get equilibrium value: b_eq = cgmodel.get_bond_length(bond_sub_arrays[str(i+1)][0]) # Set bin edges: # This should be the same across all files - use heuristic from equilibrium bond length b_min = 0.5*b_eq.value_in_unit(unit.nanometer) b_max = 1.5*b_eq.value_in_unit(unit.nanometer) xvar_bin_edges = np.linspace(b_min,b_max,nbin_xvar+1) xvar_bin_centers = np.zeros((len(xvar_bin_edges)-1,1)) for i in range(len(xvar_bin_edges)-1): xvar_bin_centers[i] = (xvar_bin_edges[i]+xvar_bin_edges[i+1])/2 xlabel = f'{xvar_name} distance ({unit.nanometer})' elif n_particle_x == 3: # Angle # Get angle list angle_list = CGModel.get_bond_angle_list(cgmodel) # Assign angle types: ang_types, ang_array, ang_sub_arrays, n_i, i_angle_type, ang_dict, inv_ang_dict = \ assign_angle_types(cgmodel, angle_list) # Set bin edges: xvar_bin_edges = np.linspace(0,180,nbin_xvar+1) xvar_bin_centers = np.zeros((len(xvar_bin_edges)-1,1)) for i in range(len(xvar_bin_edges)-1): xvar_bin_centers[i] = (xvar_bin_edges[i]+xvar_bin_edges[i+1])/2 for i in range(i_angle_type): if inv_ang_dict[str(i+1)] == xvar_name or inv_ang_dict[str(i+1)] == xvar_name_reverse: # Compute all angle values in trajectory # This returns an [nframes x n_angles] array xvar_val_array[file] = md.compute_angles(traj,ang_sub_arrays[str(i+1)]) # Convert to degrees: xvar_val_array[file] *= (180/np.pi) xlabel = f'{xvar_name} angle (degrees)' elif n_particle_x == 4: # Torsion # Get torsion list torsion_list = CGModel.get_torsion_list(cgmodel) # Assign torsion types torsion_types, torsion_array, torsion_sub_arrays, n_j, i_torsion_type, torsion_dict, inv_torsion_dict = \ assign_torsion_types(cgmodel, torsion_list) # Set bin edges: xvar_bin_edges = np.linspace(-180,180,nbin_xvar+1) xvar_bin_centers = np.zeros((len(xvar_bin_edges)-1,1)) for i in range(len(xvar_bin_edges)-1): xvar_bin_centers[i] = (xvar_bin_edges[i]+xvar_bin_edges[i+1])/2 for i in range(i_torsion_type): if inv_torsion_dict[str(i+1)] == xvar_name or inv_torsion_dict[str(i+1)] == xvar_name_reverse: # Compute all torsion values in trajectory # This returns an [nframes x n_torsions] array xvar_val_array[file] = md.compute_dihedrals( traj,torsion_sub_arrays[str(i+1)]) # Convert to degrees: xvar_val_array[file] *= (180/np.pi) xlabel = f'{xvar_name} angle (degrees)' # y variable # Determine parameter type of yvar: n_particle_y = yvar_name.count('_')+1 if n_particle_y == 2: # Bond # Get bond list bond_list = CGModel.get_bond_list(cgmodel) # Assign bond types: bond_types, bond_array, bond_sub_arrays, n_i, i_bond_type, bond_dict, inv_bond_dict = \ assign_bond_types(cgmodel, bond_list) for i in range(i_bond_type): if inv_bond_dict[str(i+1)] == yvar_name or inv_bond_dict[str(i+1)] == yvar_name_reverse: # Compute all bond length values in trajectory # This returns an [nframes x n_bonds] array yvar_val_array[file] = md.compute_distances(traj,bond_sub_arrays[str(i+1)]) # Get equilibrium value: b_eq = cgmodel.get_bond_length(bond_sub_arrays[str(i+1)][0]) # Set bin edges: # This should be the same across all files - use heuristic from equilibrium bond length b_min = 0.5*b_eq.value_in_unit(unit.nanometer) b_max = 1.5*b_eq.value_in_unit(unit.nanometer) yvar_bin_edges = np.linspace(b_min,b_max,nbin_yvar+1) yvar_bin_centers = np.zeros((len(yvar_bin_edges)-1,1)) for i in range(len(yvar_bin_edges)-1): yvar_bin_centers[i] = (yvar_bin_edges[i]+yvar_bin_edges[i+1])/2 ylabel = f'{yvar_name} distance ({unit.nanometer})' elif n_particle_y == 3: # Angle # Get angle list angle_list = CGModel.get_bond_angle_list(cgmodel) # Assign angle types: ang_types, ang_array, ang_sub_arrays, n_i, i_angle_type, ang_dict, inv_ang_dict = \ assign_angle_types(cgmodel, angle_list) # Set bin edges: yvar_bin_edges = np.linspace(0,180,nbin_yvar+1) yvar_bin_centers = np.zeros((len(yvar_bin_edges)-1,1)) for i in range(len(yvar_bin_edges)-1): yvar_bin_centers[i] = (yvar_bin_edges[i]+yvar_bin_edges[i+1])/2 for i in range(i_angle_type): if inv_ang_dict[str(i+1)] == yvar_name or inv_ang_dict[str(i+1)] == yvar_name_reverse: # Compute all angle values in trajectory # This returns an [nframes x n_angles] array yvar_val_array[file] = md.compute_angles(traj,ang_sub_arrays[str(i+1)]) # Convert to degrees: yvar_val_array[file] *= (180/np.pi) ylabel = f'{yvar_name} angle (degrees)' elif n_particle_y == 4: # Torsion # Get torsion list torsion_list = CGModel.get_torsion_list(cgmodel) # Assign torsion types torsion_types, torsion_array, torsion_sub_arrays, n_j, i_torsion_type, torsion_dict, inv_torsion_dict = \ assign_torsion_types(cgmodel, torsion_list) # Set bin edges: yvar_bin_edges = np.linspace(-180,180,nbin_yvar+1) yvar_bin_centers = np.zeros((len(yvar_bin_edges)-1,1)) for i in range(len(yvar_bin_edges)-1): yvar_bin_centers[i] = (yvar_bin_edges[i]+yvar_bin_edges[i+1])/2 for i in range(i_torsion_type): if inv_torsion_dict[str(i+1)] == yvar_name or inv_torsion_dict[str(i+1)] == yvar_name_reverse: # Compute all torsion values in trajectory # This returns an [nframes x n_torsions] array yvar_val_array[file] = md.compute_dihedrals( traj,torsion_sub_arrays[str(i+1)]) # Convert to degrees: yvar_val_array[file] *= (180/np.pi) ylabel = f'{yvar_name} angle (degrees)' # Since the bonded variables may have different numbers of observables, we can use all # combinations of the 2 parameter observables to create the histograms. xvar_val_array_combo = {} yvar_val_array_combo = {} # Each array of single observables is [n_frames x n_occurances] # x value arrays should be [xval0_y0, xval1_y0, ...xvaln_y0, ... xval0_yn, xval1_yn, xvaln_yn] # y value arrays should be [yval0_x0, yval0_x1, ...yval0_xn, ... yvaln_x0, yvaln_x1, yvaln_xn] for file in file_list: n_occ_x = xvar_val_array[file].shape[1] n_occ_y = yvar_val_array[file].shape[1] xvar_val_array_combo[file] = np.zeros((nframes,n_occ_x*n_occ_y)) yvar_val_array_combo[file] = np.zeros_like(xvar_val_array_combo[file]) for iy in range(n_occ_y): xvar_val_array_combo[file][:,(iy*n_occ_x):((iy+1)*n_occ_x)] = xvar_val_array[file] for ix in range(n_occ_x): yvar_val_array_combo[file][:,ix+iy*n_occ_x] = yvar_val_array[file][:,iy] # Reshape arrays for histogramming: xvar_val_array_combo[file] = np.reshape(xvar_val_array_combo[file], (nframes*n_occ_x*n_occ_y,1)) yvar_val_array_combo[file] = np.reshape(yvar_val_array_combo[file], (nframes*n_occ_x*n_occ_y,1)) # 2d histogram the data and plot: hist_data, xedges, yedges = plot_2d_distribution( file_list, xvar_val_array_combo, yvar_val_array_combo, xvar_bin_edges, yvar_bin_edges, plotfile, colormap, xlabel, ylabel, temperature_list=temperature_list) return hist_data, xedges, yedges
# Initiate cgmodel using positions from local file positions = PDBFile("init.pdb").getPositions() native_structure = positions cgmodel = CGModel( polymer_length=polymer_length, backbone_lengths=backbone_lengths, sidechain_lengths=sidechain_lengths, sidechain_positions=sidechain_positions, masses=masses, sigmas=sigmas, epsilons=epsilons, bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, torsion_force_constants=torsion_force_constants, equil_torsion_angles=equil_torsion_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, positions=positions, ) # Set parameters for definition/evaluation of native contacts native_structure_contact_distance_cutoff = 1.05 * cgmodel.get_sigma( 0 ) # This distance cutoff determines which nonbonded interactions are considered 'native' contacts native_contact_cutoff_ratio = 1.1 # The distance ratio (in comparison with the distance of a contact in the native structure) below which a nonbonded interaction is considered 'native'
def assign_torsion_types(cgmodel, torsion_list): """Internal function for assigning torsion types""" torsion_types = [] # List of torsion types for each torsion in torsion_list torsion_array = np.zeros((len(torsion_list),4)) # Relevant torsion types are added to a dictionary as they are discovered torsion_dict = {} # Create an inverse dictionary for getting torsion string name from integer type inv_torsion_dict = {} # Counter for number of torsion types found: i_torsion_type = 0 for i in range(len(torsion_list)): torsion_array[i,0] = torsion_list[i][0] torsion_array[i,1] = torsion_list[i][1] torsion_array[i,2] = torsion_list[i][2] torsion_array[i,3] = torsion_list[i][3] particle_types = [ CGModel.get_particle_type_name(cgmodel,torsion_list[i][0]), CGModel.get_particle_type_name(cgmodel,torsion_list[i][1]), CGModel.get_particle_type_name(cgmodel,torsion_list[i][2]), CGModel.get_particle_type_name(cgmodel,torsion_list[i][3]) ] string_name = "" reverse_string_name = "" for particle in particle_types: string_name += f"{particle}_" string_name = string_name[:-1] for particle in reversed(particle_types): reverse_string_name += f"{particle}_" reverse_string_name = reverse_string_name[:-1] if (string_name in torsion_dict.keys()) == False: # New torsion type found, add to torsion dictionary i_torsion_type += 1 torsion_dict[string_name] = i_torsion_type torsion_dict[reverse_string_name] = i_torsion_type # For inverse dict we will use only the forward name based on first encounter inv_torsion_dict[str(i_torsion_type)] = string_name # print(f"adding new torsion type {i_torsion_type}: {string_name} to dictionary") # print(f"adding reverse version {i_torsion_type}: {reverse_string_name} to dictionary") torsion_types.append(torsion_dict[string_name]) # Sort torsions by type into separate sub arrays for mdtraj compute_dihedrals torsion_sub_arrays = {} for i in range(i_torsion_type): torsion_sub_arrays[str(i+1)] = np.zeros((torsion_types.count(i+1),4)) # Counter vector for all angle types n_i = np.zeros((i_torsion_type,1), dtype=int) for i in range(len(torsion_list)): torsion_sub_arrays[str(torsion_types[i])][n_i[torsion_types[i]-1],:] = torsion_array[i,:] n_i[torsion_types[i]-1] += 1 return torsion_types, torsion_array, torsion_sub_arrays, n_i, i_torsion_type, torsion_dict, inv_torsion_dict
def test_run_simulation(tmpdir): """Run a short MD simulation of a 24mer 1b1s model""" # Set output directory # In pytest we need to use a temp directory # tmpdir is a fixture - hence we need to pass it into test function, not import it output_directory = tmpdir.mkdir("output") # OpenMM simulation settings print_frequency = 10 # Number of steps to skip when printing output total_simulation_time = 1.0 * unit.picosecond simulation_time_step = 5.0 * unit.femtosecond total_steps = int(np.floor(total_simulation_time / simulation_time_step)) temperature = 200 * unit.kelvin friction = 1.0 / unit.picosecond # Coarse grained model settings include_bond_forces = True include_bond_angle_forces = True include_nonbonded_forces = True include_torsion_forces = True constrain_bonds = False # Bond definitions bond_length = 1.5 * unit.angstrom bond_lengths = { "bb_bb_bond_length": bond_length, "bb_sc_bond_length": bond_length, "sc_sc_bond_length": bond_length, } bond_force_constant = 1000 * unit.kilojoule_per_mole / unit.nanometer / unit.nanometer bond_force_constants = { "bb_bb_bond_force_constant": bond_force_constant, "bb_sc_bond_force_constant": bond_force_constant, "sc_sc_bond_force_constant": bond_force_constant, } # Particle definitions mass = 100.0 * unit.amu r_min = 1.5 * bond_length # Lennard-Jones potential r_min # Factor of /(2.0**(1/6)) is applied to convert r_min to sigma sigma = r_min / (2.0**(1.0 / 6.0)) epsilon = 0.5 * unit.kilojoule_per_mole bb = { "particle_type_name": "bb", "sigma": sigma, "epsilon": epsilon, "mass": mass } sc = { "particle_type_name": "sc", "sigma": sigma, "epsilon": epsilon, "mass": mass } # Bond angle definitions bond_angle_force_constant = 100 * unit.kilojoule_per_mole / unit.radian / unit.radian bond_angle_force_constants = { "bb_bb_bb_bond_angle_force_constant": bond_angle_force_constant, "bb_bb_sc_bond_angle_force_constant": bond_angle_force_constant, } # OpenMM requires angle definitions in units of radians bb_bb_bb_equil_bond_angle = 120.0 * unit.degrees bb_bb_sc_equil_bond_angle = 120.0 * unit.degrees equil_bond_angles = { "bb_bb_bb_equil_bond_angle": bb_bb_bb_equil_bond_angle, "bb_bb_sc_equil_bond_angle": bb_bb_sc_equil_bond_angle, } # Torsion angle definitions torsion_force_constant = 20.0 * unit.kilojoule_per_mole torsion_force_constants = { "bb_bb_bb_bb_torsion_force_constant": torsion_force_constant, "bb_bb_bb_sc_torsion_force_constant": torsion_force_constant } bb_bb_bb_bb_torsion_phase_angle = 75.0 * unit.degrees bb_bb_bb_sc_torsion_phase_angle = 75.0 * unit.degrees torsion_phase_angles = { "bb_bb_bb_bb_torsion_phase_angle": bb_bb_bb_bb_torsion_phase_angle, "bb_bb_bb_sc_torsion_phase_angle": bb_bb_bb_sc_torsion_phase_angle } torsion_periodicities = { "bb_bb_bb_bb_torsion_periodicity": 3, "bb_bb_bb_sc_torsion_periodicity": 3 } # Monomer definitions A = { "monomer_name": "A", "particle_sequence": [bb, sc], "bond_list": [[0, 1]], "start": 0, "end": 0, } sequence = 24 * [A] pdb_path = os.path.join(structures_path, "24mer_1b1s_initial_structure.pdb") positions = PDBFile(pdb_path).getPositions() # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, positions=positions, sequence=sequence, monomer_types=[A], ) run_simulation( cgmodel, total_simulation_time, simulation_time_step, temperature, friction=friction, print_frequency=print_frequency, output_directory=output_directory, ) assert os.path.isfile(f"{output_directory}/simulation.dat") assert os.path.isfile(f"{output_directory}/simulation.pdb")
os.mkdir(top_directory) # OpenMM simulation settings print_frequency = 20 # Number of steps to skip when printing output total_simulation_time = 1.0 * unit.nanosecond # Units = picoseconds simulation_time_step = 5.0 * unit.femtosecond total_steps = round(total_simulation_time.__div__(simulation_time_step)) # Yank (replica exchange) simulation settings number_replicas = 10 min_temp = 10.0 * unit.kelvin max_temp = 50.0 * unit.kelvin temperature_list = get_temperature_list(min_temp, max_temp, number_replicas) print("Using " + str(len(temperature_list)) + " replicas.") cgmodel = CGModel() output_data = str(str(top_directory) + "/output.nc") if not os.path.exists(output_data): replica_energies, replica_positions, replica_states = run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, temperature_list=temperature_list, simulation_time_step=simulation_time_step, total_simulation_time=total_simulation_time, print_frequency=print_frequency, output_data=output_data, ) else: replica_energies, replica_positions, replica_states = read_replica_exchange_data(
def test_run_replica_exchange(tmpdir): """ Run a short replica exchange MD simulation of a 24mer 1b1s model Test replica exchange processing (write pdb files) Test heat capacity analysis code Test physical validation code """ global_context_cache.platform = openmm.Platform.getPlatformByName("CPU") # Set output directory # In pytest we need to use a temp directory # tmpdir is a fixture - hence we need to pass it into test function, not import it output_directory = tmpdir.mkdir("output") # Replica exchange simulation settings total_simulation_time = 1.0 * unit.picosecond simulation_time_step = 5.0 * unit.femtosecond total_steps = int(np.floor(total_simulation_time / simulation_time_step)) output_data = os.path.join(output_directory, "output.nc") number_replicas = 4 min_temp = 200.0 * unit.kelvin max_temp = 300.0 * unit.kelvin temperature_list = get_temperature_list(min_temp, max_temp, number_replicas) exchange_frequency = 10 # Number of steps between exchange attempts # Coarse grained model settings include_bond_forces = True include_bond_angle_forces = True include_nonbonded_forces = True include_torsion_forces = True constrain_bonds = False # Bond definitions bond_length = 1.5 * unit.angstrom bond_lengths = { "bb_bb_bond_length": bond_length, "bb_sc_bond_length": bond_length, "sc_sc_bond_length": bond_length, } bond_force_constant = 1000 * unit.kilojoule_per_mole / unit.nanometer / unit.nanometer bond_force_constants = { "bb_bb_bond_force_constant": bond_force_constant, "bb_sc_bond_force_constant": bond_force_constant, "sc_sc_bond_force_constant": bond_force_constant, } # Particle definitions mass = 100.0 * unit.amu r_min = 1.5 * bond_length # Lennard-Jones potential r_min # Factor of /(2.0**(1/6)) is applied to convert r_min to sigma sigma = r_min / (2.0**(1.0 / 6.0)) epsilon = 0.5 * unit.kilojoule_per_mole bb = { "particle_type_name": "bb", "sigma": sigma, "epsilon": epsilon, "mass": mass } sc = { "particle_type_name": "sc", "sigma": sigma, "epsilon": epsilon, "mass": mass } # Bond angle definitions bond_angle_force_constant = 100 * unit.kilojoule_per_mole / unit.radian / unit.radian bond_angle_force_constants = { "bb_bb_bb_bond_angle_force_constant": bond_angle_force_constant, "bb_bb_sc_bond_angle_force_constant": bond_angle_force_constant, } # OpenMM requires angle definitions in units of radians bb_bb_bb_equil_bond_angle = 120.0 * unit.degrees bb_bb_sc_equil_bond_angle = 120.0 * unit.degrees equil_bond_angles = { "bb_bb_bb_equil_bond_angle": bb_bb_bb_equil_bond_angle, "bb_bb_sc_equil_bond_angle": bb_bb_sc_equil_bond_angle, } # Torsion angle definitions torsion_force_constant = 20.0 * unit.kilojoule_per_mole torsion_force_constants = { "bb_bb_bb_bb_torsion_force_constant": torsion_force_constant, "bb_bb_bb_sc_torsion_force_constant": torsion_force_constant } bb_bb_bb_bb_torsion_phase_angle = 75.0 * unit.degrees bb_bb_bb_sc_torsion_phase_angle = 75.0 * unit.degrees torsion_phase_angles = { "bb_bb_bb_bb_torsion_phase_angle": bb_bb_bb_bb_torsion_phase_angle, "bb_bb_bb_sc_torsion_phase_angle": bb_bb_bb_sc_torsion_phase_angle } torsion_periodicities = { "bb_bb_bb_bb_torsion_periodicity": 3, "bb_bb_bb_sc_torsion_periodicity": 3 } # Monomer definitions A = { "monomer_name": "A", "particle_sequence": [bb, sc], "bond_list": [[0, 1]], "start": 0, "end": 0, } sequence = 24 * [A] pdb_path = os.path.join(structures_path, "24mer_1b1s_initial_structure.pdb") positions = PDBFile(pdb_path).getPositions() # Build a coarse grained model cgmodel = CGModel( particle_type_list=[bb, sc], bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, torsion_force_constants=torsion_force_constants, equil_bond_angles=equil_bond_angles, torsion_phase_angles=torsion_phase_angles, torsion_periodicities=torsion_periodicities, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, positions=positions, sequence=sequence, monomer_types=[A], ) run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, temperature_list=temperature_list, simulation_time_step=simulation_time_step, total_simulation_time=total_simulation_time, exchange_frequency=exchange_frequency, output_data=output_data, ) assert os.path.isfile(f"{output_directory}/output.nc") # Process replica exchange output # 1) With plot production only and print_timing: replica_energies, replica_states, production_start, sample_spacing, n_transit, mixing_stats = process_replica_exchange_data( output_data=output_data, output_directory=output_directory, plot_production_only=True, print_timing=True, ) # 2) With non-default equil_nskip replica_energies, replica_states, production_start, sample_spacing, n_transit, mixing_stats = process_replica_exchange_data( output_data=output_data, output_directory=output_directory, plot_production_only=True, equil_nskip=2, ) # 3) With frame_begin used to circumvent detectEquilibration replica_energies, replica_states, production_start, sample_spacing, n_transit, mixing_stats = process_replica_exchange_data( output_data=output_data, output_directory=output_directory, frame_begin=5, ) # 4) With frame end specified to analyze only the beginning of a trajectory replica_energies, replica_states, production_start, sample_spacing, n_transit, mixing_stats = process_replica_exchange_data( output_data=output_data, output_directory=output_directory, frame_end=25, ) # 5) Without writing .dat file: replica_energies, replica_states, production_start, sample_spacing, n_transit, mixing_stats = process_replica_exchange_data( output_data=output_data, output_directory=output_directory, write_data_file=False, ) # Test pdb writer: make_replica_pdb_files( cgmodel.topology, output_dir=output_directory, ) make_state_pdb_files(cgmodel.topology, output_dir=output_directory) assert os.path.isfile(f"{output_directory}/replica_4.pdb") assert os.path.isfile(f"{output_directory}/state_4.pdb") # With non-default frame_begin, stride, no centering: make_replica_pdb_files(cgmodel.topology, frame_begin=10, frame_stride=2, output_dir=output_directory) make_state_pdb_files(cgmodel.topology, frame_begin=10, frame_stride=2, output_dir=output_directory, center=False) # Test dcd writer: make_replica_dcd_files(cgmodel.topology, timestep=simulation_time_step, time_interval=exchange_frequency, output_dir=output_directory) make_state_dcd_files(cgmodel.topology, timestep=simulation_time_step, time_interval=exchange_frequency, output_dir=output_directory) assert os.path.isfile(f"{output_directory}/replica_4.dcd") assert os.path.isfile(f"{output_directory}/state_4.dcd") # With non-default frame_begin, stride, no centering: make_replica_dcd_files(cgmodel.topology, timestep=simulation_time_step, time_interval=exchange_frequency, frame_begin=10, frame_stride=2, output_dir=output_directory) make_state_dcd_files(cgmodel.topology, timestep=simulation_time_step, time_interval=exchange_frequency, frame_begin=10, frame_stride=2, output_dir=output_directory, center=False)
epsilon_list = [ unit.Quantity((0.25 + i * 0.25), unit.kilocalorie_per_mole) for i in range(grid_size) ] for epsilon in epsilon_list: print( "Performing simulations and heat capacity analysis for a coarse grained model" ) print("with epsilon values of " + str(epsilon)) epsilons = { "bb_bb_eps": epsilon, "bb_sc_eps": epsilon, "sc_sc_eps": epsilon } cgmodel = CGModel(epsilons=epsilons) output_data = str( str(top_directory) + "/eps_" + str(epsilon._value) + ".nc") if not os.path.exists(output_data): replica_energies, replica_positions, replica_states = run_replica_exchange( cgmodel.topology, cgmodel.system, cgmodel.positions, temperature_list=temperature_list, simulation_time_step=simulation_time_step, total_simulation_time=total_simulation_time, print_frequency=print_frequency, output_data=output_data, ) else:
} C = { "monomer_name": "C", "particle_sequence": [cbb, cbb, cbb, csc], "bond_list": [[0, 1], [1, 2], [1, 3]], # sidechain is on 2nd bead "start": 0, "end": 2 } sequence = 4 * [A, B, C] # Build a coarse grained model cgmodel = CGModel( particle_type_list=particle_type_list, bond_lengths=bond_lengths, bond_force_constants=bond_force_constants, bond_angle_force_constants=bond_angle_force_constants, equil_bond_angles=equil_bond_angles, include_nonbonded_forces=include_nonbonded_forces, include_bond_forces=include_bond_forces, include_bond_angle_forces=include_bond_angle_forces, include_torsion_forces=include_torsion_forces, constrain_bonds=constrain_bonds, random_positions=random_positions, sequence=sequence, monomer_types=[A, B, C], ) file_name = "12mer_ABC4s_initial_structure.pdb" write_pdbfile_without_topology(cgmodel, file_name)