def energy(s, all=False, np=None, **kwargs): """pysimm.lmps.energy Convenience function to calculate energy of a given :class:`~pysimm.system.System` object. Args: s: system to calculate energy all: returns decomposition of energy if True (default: False) np: number of threads to use for simulation Returns: total energy or disctionary of energy components """ sim = Simulation(s, log='pysimm_calc.tmp.log', **kwargs) sim.add( OutputSettings( thermo={ 'freq': 1, 'style': 'custom step etotal epair emol evdwl ecoul ebond eangle edihed eimp' })) sim.add_md(length=0, **kwargs) sim.run(np) log = LogFile('pysimm_calc.tmp.log') try: os.remove('pysimm_calc.tmp.log') except: error_print('error likely occurred during simulation') if all: return log.data.loc[0] else: return log.data.loc[0].TotEng
def add_qeq(self, template=None, **kwargs): """pysimm.lmps.Simulation.add_qeq Add :class:`~pysimm.lmps.Qeq` template to simulation Args: template: :class:`~pysimm.lmps.Qeq` object reference **kwargs: if template is None these are passed to :class:`~pysimm.lmps.Qeq` constructor to create new template """ if template is None: self.sim.append(Qeq(**kwargs)) elif isinstance(template, Qeq): self.sim.append(template) else: error_print('you must add an object of type Qeq to Simulation')
def add_min(self, template=None, **kwargs): """pysimm.lmps.Simulation.add_min Add :class:`~pysimm.lmps.Minimization` template to simulation Args: template: :class:`~pysimm.lmps.Minimization` object reference **kwargs: if template is None these are passed to :class:`~pysimm.lmps.Minimization` constructor to create new template """ if template is None: self.sim.append(Minimization(**kwargs)) elif isinstance(template, Minimization): self.sim.append(template) else: error_print('you must add an object of type Minimization to Simulation')
def add_md(self, template=None, **kwargs): """pysimm.lmps.Simulation.add_md Add :class:`~pysimm.lmps.MolecularDyanmics` template to simulation Args: template: :class:`~pysimm.lmps.MolecularDynamics` object reference **kwargs: if template is None these are passed to :class:`~pysimm.lmps.MolecularDynamics` constructor to create new template """ if template is None: self.sim.append(MolecularDynamics(**kwargs)) elif isinstance(template, MolecularDynamics): self.sim.append(template) else: error_print('you must add an object of type MolecularDynamics to Simulation')
def redo_monomer_insertion(s, m, i): """pysimm.apps.random_walk.redo_monomer_insertion This function is called by random_walk_tacticity if the latest capped monomer insertion resulted in hardcore overlaps. 1) The hardcore overlap is resolved by shrinking the last monomer by a factor of 0.8, iteratively, until there are no more hardcore overlaps. 2) Then the shrunken last monomer is frozen while the rest of the polymer chain is optimized, and the last monomer is scaled in size by 1.05 3) Cycles of contrainedOptimization and regrowth are alternated until a reasonable structure is obtained Args: s_: :class:`~pysimm.system.System` is a polymer chain in which the last monomer insertion has generated a hardcore overlap m: reference monomer :class:`~pysimm.system.System`. Must be a capped monomer, with headCap and tail_cap as the first and last atoms in the .mol file. i: number of the offending monomer, used for labelling diagnostic .xyz output files Returns: nothing; all changes to the polymer chain are written to the argument s_ """ for p in s.particles[-1 * m.particles.count:]: if p.linker == 'tail': tail = p scale_min = 0.1 s.unwrap() s.set_box(padding=10) s.wrap() # shrink last monomer for p in s.particles[-1 * m.particles.count:]: p.x, p.y, p.z = scale_monomer(p, tail, scale_min) # now, reexpand the monomer and relax polymer, step-wise scale = 1 while scale_min * scale * 1.05 < 0.91: print("Scaling up from %s to %s" % (str(scale_min * scale), str(scale * scale_min * 1.05))) scale = scale * 1.05 for p in s.particles[-1 * m.particles.count:]: p.x, p.y, p.z = scale_monomer(p, tail, 1.05) # simulation with fixed latest monomer constrained_opt( s, m, "nearby") # system-wide constrained optimization is too slow s.unwrap() s.write_xyz('bad_insertion_' + str(i) + '.xyz', append=True) s.wrap() if s.quality(tolerance=0.2) > 0: error_print("system is broken upon monomer reexpansion") # now relax the last monomer constrained_opt(s, m, "monomer")
def get_forcefield_types(s, types='gaff', f=None): """pysimm.amber.get_forcefield_types Uses antechamber to determine atom types. Defaults to GAFF atom types. Retrieves :class:`~pysimm.system.ParticleType` objects from force field is provided Args: s: :class:`~pysimm.system.System` for which to type types: name of atom types to use (default: gaff) f: forcefield object to retrieve :class:`~pysimm.system.ParticleType` objects from if not present in s (default: None) Returns: None """ s.write_pdb('pysimm.tmp.pdb') cl = '{} -fi pdb -i pysimm.tmp.pdb -fo ac -o pysimm.tmp.ac -at {}'.format( ANTECHAMBER_EXEC, types) p = Popen(cl.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) p.communicate() with file('pysimm.tmp.ac') as fr: fr.next() fr.next() line = fr.next() while line.split()[0] == 'ATOM': tag = int(line.split()[1]) type_name = line.split()[-1] if s.particle_types.get(type_name): s.particles[tag].type = s.particle_types.get(type_name)[0] elif f: pt = f.particle_types.get(type_name) if pt: s.particles[tag].type = s.particle_types.add(pt[0].copy()) else: error_print( 'cannot find type {} in system or forcefield'.format( type_name)) line = fr.next()
def copolymer(m, nmon, s_=None, **kwargs): """pysimm.apps.random_walk.copolymer Builds copolymer using random walk methodology using pattern Args: m: list of reference monomer :class:`~pysimm.system.System`s nmon: total number of monomers to add to chain s_: :class:`~pysimm.system.System` in which to build polymer chain (None) settings: dictionary of simulation settings density: density at which to build polymer (0.3) forcefield: :class:`~pysimm.forcefield.Forcefield` object to acquire new force field parameters capped: True/False if monomers are capped unwrap: True to unwrap final system traj: True to build xyz trajectory of polymer growth (True) pattern: list of pattern for monomer repeat units, should match length of m ([1 for _ in range(len(m))]) limit: during MD, limit atomic displacement by this max value (LAMMPS ONLY) sim: :class:`~pysimm.lmps.Simulation` object for relaxation between polymer growth Returns: new copolymer :class:`~pysimm.system.System` """ m = [x.copy() for x in m] settings = kwargs.get('settings', {}) density = kwargs.get('density', 0.3) f = kwargs.get('forcefield') capped = kwargs.get('capped') unwrap = kwargs.get('unwrap') traj = kwargs.get('traj', True) pattern = kwargs.get('pattern', [1 for _ in range(len(m))]) limit = kwargs.get('limit', 0.1) sim = kwargs.get('sim') for m_ in m: m_.add_particle_bonding() for p in m_.particles: if p.type.name.find('@') >= 0 and p.type.name.split('@')[0].find('H'): p.linker = 'head' elif p.type.name.find('@') >= 0 and p.type.name.split('@')[0].find('T'): p.linker = 'tail' m_.remove_linker_types() if s_ is None: s = system.replicate(m[0], 1, density=density/nmon) else: s = system.replicate(m[0], 1, s_=s_, density=density/nmon) print('%s: %s/%s monomers added' % (strftime('%H:%M:%S'), 1, nmon)) for p in s.particles: if p.linker == 'head': last_head = p elif p.linker == 'tail': last_tail = p for m_ in m: if capped: m_.particles.remove(1) m_.remove_spare_bonding() m_.add_particle_bonding() s.add_particle_bonding() if traj: s.write_xyz('random_walk.xyz') temp_nmon = 1 while True: m_ = m.pop(0) m.append(m_) p_ = pattern.pop(0) pattern.append(p_) if temp_nmon == 1 and p_ == 1: m_ = m.pop(0) m.append(m_) p_ = pattern.pop(0) pattern.append(p_) elif temp_nmon == 1: p_ -= 1 for insert in range(p_): head = None tail = None backbone_vector = np.array([last_head.x - last_tail.x, last_head.y - last_tail.y, last_head.z - last_tail.z]) ref_head = None ref_tail = None for p in m_.particles: if p.linker == 'head': ref_head = p elif p.linker == 'tail': ref_tail = p if ref_head and ref_tail: ref_backbone_vector = np.array([ref_head.x - ref_tail.x, ref_head.y - ref_tail.y, ref_head.z - ref_tail.z]) rot_matrix = calc.find_rotation(ref_backbone_vector, backbone_vector) m_.rotate(around=ref_tail, rot_matrix=rot_matrix) translation_vector = [last_tail.x - ref_tail.x, last_tail.y - ref_tail.y, last_tail.z - ref_tail.z] for p in m_.particles: p.x = p.x + translation_vector[0] + 3*backbone_vector[0] p.y = p.y + translation_vector[1] + 3*backbone_vector[1] p.z = p.z + translation_vector[2] + 3*backbone_vector[2] else: print('reference molecule has no head or tail') n = m_.copy() if capped: s.particles.remove(s.particles.count) s.remove_spare_bonding() s.add_particle_bonding() s.add(n, change_dim=False) s.add_particle_bonding() head = last_head for p in s.particles[-1*n.particles.count:]: if p.linker == 'tail': tail = p s.make_new_bonds(head, tail, f) temp_nmon += 1 print('%s: %s/%s monomers added' % (strftime('%H:%M:%S'), temp_nmon, nmon)) if unwrap: s.unwrap() if sim is None: sim = lmps.Simulation(s, name='relax_%03d' % (temp_nmon), log='relax.log', **settings) sim.add_md(ensemble='nve', limit=limit, **settings) sim.add_min(**settings) if isinstance(sim, lmps.Simulation): sim.system = s sim.name = 'relax_%03d' % (temp_nmon) sim.run(np=settings.get('np')) if unwrap: s.unwrap() if unwrap: s.wrap() for p in s.particles[-1*n.particles.count:]: if p.linker == 'head': last_head = p elif p.linker == 'tail': last_tail = p if temp_nmon >= nmon: break if unwrap: if not s.unwrap(): error_print('something went wrong') return s if traj: s.write_xyz('random_walk.xyz', append=True) if unwrap: s.wrap() for p in s.particles: if p not in s.molecules[p.molecule.tag].particles: s.molecules[p.molecule.tag].particles.add(p) s.write_lammps('polymer.lmps') s.unwrap() s.write_xyz('polymer.xyz') return s
def random_walk(m, nmon, s_=None, **kwargs): """pysimm.apps.random_walk.random_walk Builds homopolymer using random walk methodology Args: m: reference monomer system nmon: total number of monomers to add to chain s_: system in which to build polymer chain (None) extra_bonds: EXPERMINTAL, True if making ladder backbone polymer settings: dictionary of simulation settings density: density at which to build polymer (0.3) forcefield: pysimm.forcefield.Forcefield object to acquire new force field parameters capped: True/False if monomers are capped unwrap: True to unwrap final system traj: True to build xyz trajectory of polymer growth (True) limit: during MD, limit atomic displacement by this max value (LAMMPS ONLY) sim: pysimm.lmps.Simulation object for relaxation between polymer growth Returns: new polymer pysimm.system.System """ m = m.copy() extra_bonds = kwargs.get('extra_bonds') if kwargs.get('extra_bonds') is not None else False settings = kwargs.get('settings') if kwargs.get('settings') is not None else {} density = kwargs.get('density') or 0.3 f = kwargs.get('forcefield') capped = kwargs.get('capped') unwrap = kwargs.get('unwrap') traj = kwargs.get('traj') if kwargs.get('traj') is not None else True limit = kwargs.get('limit') if kwargs.get('limit') is not None else 0.1 sim = kwargs.get('sim') m.add_particle_bonding() for p in m.particles: if p.type.name.find('@') >= 0 and p.type.name.split('@')[0].find('H'): p.linker = 'head' elif p.type.name.find('@') >= 0 and p.type.name.split('@')[0].find('T'): p.linker = 'tail' m.remove_linker_types() if s_ is None: s = system.replicate(m, 1, density=density/nmon) else: s = system.replicate(m, 1, s_=s_, density=None) print('%s: %s/%s monomers added' % (strftime('%H:%M:%S'), 1, nmon)) if traj: s.write_xyz('random_walk.xyz') if capped: m.particles.remove(1) m.remove_spare_bonding() m.add_particle_bonding() for insertion in range(nmon - 1): head = None tail = None backbone_vector = np.array(find_last_backbone_vector(s, m)) for p, p_ in izip(s.particles[-1*m.particles.count:], m.particles): p_.x = p.x + 3*backbone_vector[0] p_.y = p.y + 3*backbone_vector[1] p_.z = p.z + 3*backbone_vector[2] n = m.copy() if capped: s.particles.remove(s.particles.count) s.remove_spare_bonding() s.add_particle_bonding() if extra_bonds: heads = [] for p in s.particles[-1*n.particles.count:]: if p.linker == 'head': heads.append(p) else: for p in s.particles[-1*n.particles.count:]: if p.linker == 'head': head = p s.add(n, change_dim=False) s.add_particle_bonding() if extra_bonds: tails = [] for p in s.particles[-1*n.particles.count:]: if p.linker == 'tail': tails.append(p) else: for p in s.particles[-1*n.particles.count:]: if p.linker == 'tail': tail = p for p in s.particles: if not p.bonded_to: print(p.tag) if head and tail: add_bonds(s, head, tail, f) print('%s: %s/%s monomers added' % (strftime('%H:%M:%S'), insertion+2, nmon)) elif extra_bonds and len(heads) == len(tails): for h, t in izip(heads, tails): add_bonds(s, h, t, f) print('%s: %s/%s monomers added' % (strftime('%H:%M:%S'), insertion+2, nmon)) else: print('cannot find head and tail') if sim is None: sim = lmps.Simulation(s, name='relax_%03d' % (insertion+2), log='relax.log', **settings) sim.add_md(ensemble='nve', limit=limit, **settings) sim.add_min(**settings) if isinstance(sim, lmps.Simulation): sim.system = s sim.name = 'relax_%03d' % (insertion+2) sim.run(np=settings.get('np')) if unwrap: if not s.unwrap(): error_print('something went wrong') return s if traj: s.write_xyz('random_walk.xyz', append=True) if unwrap: s.wrap() s.write_lammps('polymer.lmps') s.unwrap() s.write_xyz('polymer.xyz') return s
def tacticity(s, a_tag=None, b_tag=None, c_tag=None, d_tag=None, offset=None, return_angles=True, unwrap=True, rewrap=True, skip_first=False): """pysimm.calc.tacticity Determines tacticity for polymer chain. Iterates through groups of four patricles given by X_tags, using offset. This assumes equivalent atoms in each group of four are perfectly offset. Args: s: :class:`~pysimm.system.System` a_tag: tag of first a particle b_tag: tag of first b particle c_tag: tag of first c particle d_tag: tag of first d particle offset: offset of particle tags (monomer repeat atomic count) return_angles: if True return chiral angles of all monomers unwrap: True to perform unwrap before calculation (REQUIRED before calculation, but not required in this function) rewrap: True to rewrap system after calculation skip_first: True to skip first monomer (sometime chirality is poorly defined for thsi monomer) Returns: tacticity or tacticity, [chiral_angles] """ if not np: raise PysimmError('pysimm.calc.tacticity function requires numpy') if a_tag is None or b_tag is None or c_tag is None or d_tag is None: error_print('particle tags for chiral center are required') error_print('a: chiral center, b-d: 3 side groups') if offset is None: error_print('offset for tags in each monomer is required, i.e. - number of particles in each monomer') if unwrap: s.unwrap() a_ = [s.particles[i] for i in range(a_tag, s.particles.count, offset)] b_ = [s.particles[i] for i in range(b_tag, s.particles.count, offset)] c_ = [s.particles[i] for i in range(c_tag, s.particles.count, offset)] d_ = [s.particles[i] for i in range(d_tag, s.particles.count, offset)] stereochem_angles = [] if skip_first: for a, b, c, d in izip(a_[1:], b_[1:], c_[1:], d_[1:]): stereochem_angles.append(chiral_angle(a, b, c, d)) else: for a, b, c, d in izip(a_, b_, c_, d_): stereochem_angles.append(chiral_angle(a, b, c, d)) last = None iso_diads = 0 syn_diads = 0 for a in stereochem_angles: if last is not None: if (a < 90 and last < 90) or (a > 90 and last > 90): iso_diads += 1 else: syn_diads += 1 last = a if iso_diads == (len(stereochem_angles) - 1): t = 'isotactic' elif syn_diads == (len(stereochem_angles) - 1): t = 'syndiotactic' else: t = 'atactic' if rewrap: s.wrap() print('{:.1f}% syn diads'.format(100*float(syn_diads)/(syn_diads+iso_diads))) print('{:.1f}% iso diads'.format(100*float(iso_diads)/(syn_diads+iso_diads))) if return_angles: return t, stereochem_angles else: return t
def set_charges(s, maxiter=100, tol=1e-6): global gasteiger_parameters for p in s.particles: if (p.type and p.type.name and p.type.name.find('@') > 0 and s.particle_types.get(p.type.name.split('@')[-1])): p.nbonds = len(p.bonds) + 1 if p.type.name[0] == 'H': p.linker = 'head' elif p.type.name[0] == 'T': p.linker = 'tail' else: p.linker = True type_ = s.particle_types.get(p.type.name.split('@')[-1]) if type_: p.type = type_[0] else: error_print('found linker type %s but did not find regular ' 'type %s' % (p.type.name, p.type.name.split('@')[-1])) return else: p.nbonds = len(p.bonds) gast_type = gasteiger_parameters.get(p.type.name) if gast_type: gast_type = gast_type[0] p.gast_conv = False p.qn = 0.0 p.charge = 0.0 p.gast_a = gast_type.a p.chi = p.gast_a p.gast_b = gast_type.b p.gast_c = gast_type.c continue else: if not p.type.elem and p.type.mass: elem = element_names_by_mass.get(int(round(p.type.mass))) if elem: p.type.elem = elem if not p.type.elem or p.type.elem not in ['H', 'N', 'C', 'O', 'F', 'Cl', 'Br', 'I', 'S']: error_print('cannot find gastieger paramater for particle %s' % p.tag) else: if p.type.elem == 'H': gast_type = gasteiger_parameters.get('h')[0] elif p.type.elem == 'C': if p.nbonds == 4: gast_type = gasteiger_parameters.get('c_sp3')[0] elif p.nbonds == 3: gast_type = gasteiger_parameters.get('c_sp2')[0] elif p.nbonds == 2: gast_type = gasteiger_parameters.get('c_sp1')[0] elif p.type.elem == 'N': if p.nbonds >= 3: gast_type = gasteiger_parameters.get('n_sp3')[0] elif p.nbonds == 2: gast_type = gasteiger_parameters.get('c_sp2')[0] elif p.nbonds == 1: gast_type = gasteiger_parameters.get('c_sp1')[0] elif p.type.elem == 'O': if p.nbonds == 2: gast_type = gasteiger_parameters.get('o_sp3')[0] elif p.nbonds == 1: gast_type = gasteiger_parameters.get('o_sp2')[0] elif p.type.elem == 'F': gast_type = gasteiger_parameters.get('f')[0] elif p.type.elem == 'Cl': gast_type = gasteiger_parameters.get('cl')[0] elif p.type.elem == 'Br': gast_type = gasteiger_parameters.get('br')[0] elif p.type.elem == 'I': gast_type = gasteiger_parameters.get('i')[0] elif p.type.elem == 'S': gast_type = gasteiger_parameters.get('s')[0] gast_type = gast_type p.gast_conv = False p.qn = 0.0 p.charge = 0.0 p.gast_a = gast_type.a p.chi = p.gast_a p.gast_b = gast_type.b p.gast_c = gast_type.c continue for n in range(1, maxiter+1): for b in s.bonds: if b.b.chi > b.a.chi: b.b.qn += ((b.a.chi - b.b.chi) / (b.a.gast_a + b.a.gast_b + b.a.gast_c)) b.a.qn += ((b.b.chi - b.a.chi) / (b.a.gast_a + b.a.gast_b + b.a.gast_c)) else: b.b.qn += ((b.a.chi - b.b.chi) / (b.b.gast_a + b.b.gast_b + b.b.gast_c)) b.a.qn += ((b.b.chi - b.a.chi) / (b.b.gast_a + b.b.gast_b + b.b.gast_c)) convergence = True for p in s.particles: p.qn *= pow(0.5, n) p.charge += p.qn p.chi = p.gast_a + p.gast_b*p.charge + p.gast_c*p.charge*p.charge if abs(p.qn) < tol: p.gast_conv = True else: convergence = False p.qn = 0.0 if convergence: print('charges converged after %s iterations' % n) break if not convergence: print('charges not converged after %s iterations' % n)
def write_init(l, **kwargs): """pysimm.lmps.write_init Create initialization LAMMPS input based on pysimm.system.System data Args: l: pysimm.system.System object reference kwargs: atom_style: LAMMPS atom_style default=full kspace_style: LAMMPS kspace style default='pppm 1e-4' units: LAMMPS set of units to use default=real special_bonds: LAMMPS special bonds input nonbond_mixing: type of mixing rule for nonbonded interactions default=arithmetic nb_cut: cutoff for nonbonded interactions default=12 """ atom_style = kwargs.get('atom_style') or 'full' kspace_style = kwargs.get('kspace_style') or 'pppm 1e-4' units = kwargs.get('units') or 'real' nb_cut = kwargs.get('nb_cut') or 12.0 special_bonds = kwargs.get('special_bonds') nonbond_mixing = kwargs.get('nonbond_mixing') or 'arithmetic' output = '' if type(l) == str and os.path.isfile(l): l = read_lammps(l, quiet=True) elif type(l) == str: return 'init_system failed to read %s' % l output += 'units %s\n' % units output += 'atom_style %s\n' % atom_style pair_style = None charge = False if l.charge is None: for p in l.particles: if p.charge != 0: charge = True break else: if l.charge != 0: charge = True if not l.pair_style: if l.particle_types[1].sigma and l.particle_types[1].epsilon: if charge: if l.ff_class == '2': pair_style = 'lj/class2/coul/long' else: pair_style = 'lj/cut/coul/long' else: if l.ff_class == '2': pair_style = 'lj/class2' else: pair_style = 'lj/cut' elif (l.particle_types[1].a and l.particle_types[1].rho and l.particle_types[1].c): if charge: pair_style = 'buck/coul/long' else: pair_style = 'buck' else: if l.pair_style.startswith('lj') or l.pair_style.startswith('class2'): if charge: if l.ff_class == '2': pair_style = 'lj/class2/coul/long' else: pair_style = 'lj/cut/coul/long' else: if l.ff_class == '2': pair_style = 'lj/class2' else: pair_style = 'lj/cut' elif l.pair_style.startswith('buck'): if charge: pair_style = 'buck/coul/long' else: pair_style = 'buck' if pair_style: output += 'pair_style %s %s\n' % (pair_style, nb_cut) else: error_print('pair style probably not supported') if charge: output += 'kspace_style %s\n' % kspace_style if not pair_style.startswith('buck'): if nonbond_mixing == 'arithmetic': output += 'pair_modify shift yes mix arithmetic\n' elif nonbond_mixing == 'geometric': output += 'pair_modify shift yes mix geometric\n' else: output += 'pair_modify shift yes mix arithmetic\n' print('%s mixing rule not supported; defaulting to arithmetic' % nonbond_mixing) if l.bond_style: output += 'bond_style %s\n' % l.bond_style else: if l.ff_class == '1': output += 'bond_style harmonic\n' elif l.ff_class == '2': output += 'bond_style class2\n' if l.angles.count > 0: if l.angle_style: output += 'angle_style %s\n' % l.angle_style else: if l.ff_class == '1': output += 'angle_style harmonic\n' elif l.ff_class == '2': output += 'angle_style class2\n' if l.dihedrals.count > 0: if l.dihedral_style: output += 'dihedral_style %s\n' % l.dihedral_style else: if l.ff_class == '1': output += 'dihedral_style harmonic\n' elif l.ff_class == '2': output += 'dihedral_style class2\n' if l.impropers.count > 0: if l.improper_style: output += 'improper_style %s\n' % l.improper_style else: if l.ff_class == '1': output += 'improper_style harmonic\n' elif l.ff_class == '2': output += 'improper_style class2\n' if special_bonds: output += 'special_bonds %s\n' % special_bonds else: output += 'special_bonds amber\n' l.write_lammps('temp.lmps') output += 'read_data temp.lmps\n' if pair_style.startswith('buck'): for pt1 in l.particle_types: for pt2 in l.particle_types: if pt1.tag <= pt2.tag: a = pow(pt1.a*pt2.a, 0.5) c = pow(pt1.c*pt2.c, 0.5) rho = 0.5*(pt1.rho+pt2.rho) output += 'pair_coeff %s %s %s %s %s\n' % (pt1.tag, pt2.tag, a, rho, c) return output