def execute_command(self, command, comm): global exit_flag if command == "EXIT": exit_flag = True elif command == "<NATOMS": mdi.MDI_Send(self.natoms, 1, mdi.MDI_INT, comm) else: raise Exception( "Error in engine_py.py: MDI command not recognized") return 0
def perform_tasks(world, mdicomm, dummy): me = world.Get_rank() nprocs = world.Get_size() # allocate vectors for per-atom types, coords, vels, forces natoms = nx * ny * nz atypes = np.zeros(natoms, dtype=np.int) coords = np.zeros(3 * natoms, dtype=np.float64) vels = np.zeros(3 * natoms, dtype=np.float64) forces = np.zeros(3 * natoms, dtype=np.float64) atypes[:] = 1 # initialize RN generator random.seed(seed) # loop over sequence of calculations for icalc in range(ncalc): # define simulation box onerho = rho + (random.random() - 0.5) * rhodelta sigma = pow(1.0 / onerho, 1.0 / 3.0) xlo = ylo = zlo = 0.0 xhi = nx * sigma yhi = ny * sigma zhi = nz * sigma # send simulation box to engine vec = [xhi - xlo, 0.0, 0.0] + [0.0, yhi - ylo, 0.0 ] + [0.0, 0.0, zhi - zlo] mdi.MDI_Send_command(">CELL", mdicomm) mdi.MDI_Send(vec, 9, mdi.MDI_DOUBLE, mdicomm) # create atoms on perfect lattice m = 0 for k in range(nz): for j in range(ny): for i in range(nx): coords[m] = i * sigma coords[m + 1] = j * sigma coords[m + 2] = k * sigma m += 3 # perturb lattice for m in range(3 * natoms): coords[m] += 2.0 * random.random() * delta - delta # define initial velocities for m in range(3 * natoms): vels[m] = random.random() - 0.5 tcurrent = 0.0 for m in range(3 * natoms): tcurrent += vels[m] * vels[m] tcurrent /= 3 * (natoms - 1) factor = math.sqrt(tinitial / tcurrent) for m in range(3 * natoms): vels[m] *= factor # send atoms and their properties to engine mdi.MDI_Send_command(">NATOMS", mdicomm) mdi.MDI_Send(natoms, 1, mdi.MDI_INT, mdicomm) mdi.MDI_Send_command(">TYPES", mdicomm) mdi.MDI_Send(atypes, natoms, mdi.MDI_INT, mdicomm) mdi.MDI_Send_command(">COORDS", mdicomm) mdi.MDI_Send(coords, 3 * natoms, mdi.MDI_DOUBLE, mdicomm) mdi.MDI_Send_command(">VELOCITIES", mdicomm) mdi.MDI_Send(vels, 3 * natoms, mdi.MDI_DOUBLE, mdicomm) # eval or run or minimize if mode == "eval": pass elif mode == "run": mdi.MDI_Send_command(">NSTEPS", mdicomm) mdi.MDI_Send(nsteps, 1, mdi.MDI_INT, mdicomm) mdi.MDI_Send_command("MD", mdicomm) elif mode == "min": mdi.MDI_Send_command(">TOLERANCE", mdicomm) params = [tol, tol, 1000.0, 1000.0] mdi.MDI_Send(params, 4, mdi.MDI_DOUBLE, mdicomm) mdi.MDI_Send_command("OPTG", mdicomm) # request potential energy mdi.MDI_Send_command("<PE", mdicomm) pe = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mdicomm) pe = world.bcast(pe, root=0) # request virial tensor mdi.MDI_Send_command("<STRESS", mdicomm) virial = mdi.MDI_Recv(9, mdi.MDI_DOUBLE, mdicomm) virial = world.bcast(virial, root=0) # request forces mdi.MDI_Send_command("<FORCES", mdicomm) mdi.MDI_Recv(3 * natoms, mdi.MDI_DOUBLE, mdicomm, buf=forces) world.Bcast(forces, root=0) # final output from each calculation # pressure = trace of virial tensor, no kinetic component aveeng = pe / natoms pressure = (virial[0] + virial[4] + virial[8]) / 3.0 m = 0 fx = fy = fz = 0.0 for i in range(natoms): fx += forces[m] fy += forces[m + 1] fz += forces[m + 2] m += 3 fx /= natoms fy /= natoms fz /= natoms line = "Calc %d: eng %7.5g pressure %7.5g aveForce %7.5g %7.5g %7.5g" % \ (icalc+1,aveeng,pressure,fx,fy,fz) if me == 0: print(line) # send EXIT command to engine # in plugin mode, removes the plugin library mdi.MDI_Send_command("EXIT", mdicomm) # return needed for plugin mode return 0
def perform_aimd(world, mm_comm, qm_comm): me = world.Get_rank() # receive number of atoms from the MM engine mdi.MDI_Send_command("<NATOMS", mm_comm) natoms = mdi.MDI_Recv(1, mdi.MDI_INT, mm_comm) natoms = world.bcast(natoms, root=0) # allocate arrays for coordinates and forces coords = np.zeros(3 * natoms, dtype=np.float64) forces = np.zeros(3 * natoms, dtype=np.float64) # MM engine initializes a new MD simulation mdi.MDI_Send_command("@INIT_MD", mm_comm) # ----------------- # compute initial forces for Verlet timestepping # and initial energy for output on step 0 # ----------------- # MM engine proceeds to @FORCES node in setup() mdi.MDI_Send_command("@FORCES", mm_comm) # get coords from MM engine mdi.MDI_Send_command("<COORDS", mm_comm) mdi.MDI_Recv(3 * natoms, mdi.MDI_DOUBLE, mm_comm, buf=coords) world.Bcast(coords, root=0) # send coords to QM engine mdi.MDI_Send_command(">COORDS", qm_comm) mdi.MDI_Send(coords, 3 * natoms, mdi.MDI_DOUBLE, qm_comm) # get QM potential energy mdi.MDI_Send_command("<PE", qm_comm) qm_pe = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, qm_comm) qm_pe = world.bcast(qm_pe, root=0) # get forces from QM engine mdi.MDI_Send_command("<FORCES", qm_comm) mdi.MDI_Recv(3 * natoms, mdi.MDI_DOUBLE, qm_comm, buf=forces) world.Bcast(forces, root=0) # send forces to MM engine mdi.MDI_Send_command(">FORCES", mm_comm) mdi.MDI_Send(forces, 3 * natoms, mdi.MDI_DOUBLE, mm_comm) # get MM kinetic energy mdi.MDI_Send_command("<KE", mm_comm) mm_ke = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mm_comm) mm_ke = world.bcast(mm_ke, root=0) # output by driver # normalize energies by atom count if me == 0: print("Step %d: MM energy %g, QM energy %g, Total energy %g" % \ (0,mm_ke/natoms,qm_pe/natoms,(mm_ke+qm_pe)/natoms)) # ----------------- # timestepping loop # ----------------- for istep in range(nsteps): # MM engine proceeds to @FORCES node mdi.MDI_Send_command("@FORCES", mm_comm) # get coords from MM engine mdi.MDI_Send_command("<COORDS", mm_comm) mdi.MDI_Recv(3 * natoms, mdi.MDI_DOUBLE, mm_comm, buf=coords) world.Bcast(coords, root=0) # send coords to QM engine mdi.MDI_Send_command(">COORDS", qm_comm) mdi.MDI_Send(coords, 3 * natoms, mdi.MDI_DOUBLE, qm_comm) # get QM potential energy mdi.MDI_Send_command("<PE", qm_comm) qm_pe = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, qm_comm) qm_pe = world.bcast(qm_pe, root=0) # get forces from QM engine mdi.MDI_Send_command("<FORCES", qm_comm) mdi.MDI_Recv(3 * natoms, mdi.MDI_DOUBLE, qm_comm, buf=forces) world.Bcast(forces, root=0) # send forces to MM engine mdi.MDI_Send_command(">FORCES", mm_comm) mdi.MDI_Send(forces, 3 * natoms, mdi.MDI_DOUBLE, mm_comm) # MM engine proceeds to @ENDSTEP node # so that KE will be for fully updated velocity mdi.MDI_Send_command("@ENDSTEP", mm_comm) # get MM kinetic energy mdi.MDI_Send_command("<KE", mm_comm) mm_ke = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mm_comm) mm_ke = world.bcast(mm_ke, root=0) # output by driver # normalize energies by atom count if me == 0: print("Step %d: MM energy %g, QM energy %g, Total energy %g" % \ (istep+1,mm_ke/natoms,qm_pe/natoms,(mm_ke+qm_pe)/natoms)) # send EXIT to each engine mdi.MDI_Send_command("EXIT", mm_comm) mdi.MDI_Send_command("EXIT", qm_comm)
# receive the number of atoms from the MM code mdi.MDI_Send_Command("<NATOMS", mm_comm) natom = mdi.MDI_Recv(1, mdi.MDI_INT, mm_comm) # have the MD code initialize a new MD simulation mdi.MDI_Send_Command("MD_INIT", mm_comm) for iiter in range(niterations): # receive the coordinates from the MM code mdi.MDI_Send_Command("<COORDS", mm_comm) coords = mdi.MDI_Recv(3 * natom, mdi.MDI_DOUBLE, mm_comm) # send the coordinates to the QM code mdi.MDI_Send_Command(">COORDS", qm_comm) mdi.MDI_Send(coords, 3 * natom, mdi.MDI_DOUBLE, qm_comm) # run an SCF calculation mdi.MDI_Send_Command("SCF", qm_comm) # get the QM energy mdi.MDI_Send_Command("<ENERGY", qm_comm) qm_energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, qm_comm) # get the MM energy mdi.MDI_Send_Command("<ENERGY", mm_comm) mm_energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mm_comm) # get the QM forces mdi.MDI_Send_Command("<FORCES", qm_comm) forces = mdi.MDI_Recv(3 * natom, mdi.MDI_DOUBLE, qm_comm)
# Write the received data to a file f = open("min_driver.dat", "w") f.write(str(recv_data)) f.close() if nsend is not None: if send_type == mdi.MDI_INT: data = [ 0 for i in range(send_num) ] elif send_type == mdi.MDI_DOUBLE: data = [ 0.0 for i in range(send_num) ] elif send_type == mdi.MDI_CHAR: data = "" for i in range(send_num): data += " " else: raise Exception("Invalid send type") mdi.MDI_Send(data, send_num, send_type, comm) # Verify that the engine is still responsive mdi.MDI_Send_Command("<NAME", comm) final_name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, comm) assert initial_name == final_name # Some nodes might not support the "EXIT" command, so write a file indicating success now # Write the received data to a file f = open("min_driver.err", "w") f.write("0") f.close() mdi.MDI_Send_Command("EXIT", comm) print(" Completed testing command")
print("DIMENSIONS: " + str(dimensions)) # <NATOMS mdi.MDI_Send_Command("<NATOMS", mdi_comm) natoms = mdi.MDI_Recv(1, mdi.MDI_INT, mdi_comm) print("NATOMS: " + str(natoms)) # <COORDS mdi.MDI_Send_Command("<COORDS", mdi_comm) coords = mdi.MDI_Recv(3 * natoms, mdi.MDI_DOUBLE, mdi_comm) print("COORDS: " + str(coords)) # >COORDS coords[0] += 0.1 mdi.MDI_Send_Command(">COORDS", mdi_comm) mdi.MDI_Send(coords, 3 * natoms, mdi.MDI_DOUBLE, mdi_comm) mdi.MDI_Send_Command("<ENERGY", mdi_comm) energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mdi_comm) print("ENERGY: " + str(energy)) # <CHARGES mdi.MDI_Send_Command("<CHARGES", mdi_comm) charges = mdi.MDI_Recv(natoms, mdi.MDI_DOUBLE, mdi_comm) print("CHARGES: " + str(charges)) # <ELEMENTS mdi.MDI_Send_Command("<ELEMENTS", mdi_comm) elements = mdi.MDI_Recv(natoms, mdi.MDI_INT, mdi_comm) print("ELEMENTS: " + str(elements)) # <FORCES
def callback(self, mpi_comm, mdi_comm): my_rank = mpi_comm.Get_rank() print("PRE MDI SEND <NAME") mdi.MDI_Send_Command("<NAME", mdi_comm) print("POST MDI SEND <NAME") name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, mdi_comm) if my_rank == 0: print("Engine name: " + str(name)) # Send the cell dimensions to the plugin mdi.MDI_Send_Command(">CELL", mdi_comm) mdi.MDI_Send(self.cell, 9, mdi.MDI_DOUBLE, mdi_comm) # Find out if the plugin supports the >NATOMS command natoms_supported = 0 natoms_supported = mdi.MDI_Check_command_exists("@DEFAULT", ">NATOMS", mdi_comm) natoms_supported = mpi_comm.bcast(natoms_supported, root=0) if natoms_supported: # Send the number of atoms to the plugin mdi.MDI_Send_Command(">NATOMS", mdi_comm) mdi.MDI_Send(self.natoms, 1, mdi.MDI_INT, mdi_comm) else: # We will assume the plugin has read the correct number # of atoms from an input file. pass # Find out if the plugin supports the >ELEMENTS command elem_supported = 0 elem_supported = mdi.MDI_Check_command_exists("@DEFAULT", ">ELEMENTS", mdi_comm) elem_supported = mpi_comm.bcast(elem_supported, root=0) if elem_supported: # Send the number of elements to the plugin mdi.MDI_Send_Command(">ELEMENTS", mdi_comm) mdi.MDI_Send(self.elements, self.natoms, mdi.MDI_INT, mdi_comm) else: # We will assume the plugin has read the correct element # of each atom from an input file. pass # Send the nuclear coordinates to the plugin mdi.MDI_Send_Command(">COORDS", mdi_comm) mdi.MDI_Send(self.coords, 3*self.natoms, mdi.MDI_DOUBLE, mdi_comm) # Receive the energy of the system from the plugin mdi.MDI_Send_Command("<ENERGY", mdi_comm) energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mdi_comm) if my_rank == 0: print("ENERGY: " + str(energy)) # Receive the nuclear forces from the plugin mdi.MDI_Send_Command("<FORCES", mdi_comm) forces = mdi.MDI_Recv(3*self.natoms, mdi.MDI_DOUBLE, mdi_comm) if my_rank == 0: print("FORCES: " + str(forces)) # Send the "EXIT" command to the plugin mdi.MDI_Send_Command("EXIT", mdi_comm) return 0
# receive the coordinates mdi.MDI_Send_Command("<COORDS", mm_comm) coords = mdi.MDI_Recv(3 * natoms, mdi.MDI_DOUBLE, mm_comm) # receive the charges mdi.MDI_Send_Command("<CHARGES", mm_comm) charges = mdi.MDI_Recv(natoms, mdi.MDI_DOUBLE, mm_comm) # receive the charges mdi.MDI_Send_Command("<MASSES", mm_comm) masses = mdi.MDI_Recv(ntypes + 1, mdi.MDI_DOUBLE, mm_comm) # get the MM energy mdi.MDI_Send_Command("<ENERGY", mm_comm) mm_energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mm_comm) # send a set of updated forces mdi.MDI_Send_Command("+PRE-FORCES", mm_comm) mdi.MDI_Send(update_forces, 3 * natoms, mdi.MDI_DOUBLE, mm_comm) # do an MD timestep mdi.MDI_Send_Command("TIMESTEP", mm_comm) print("-------------------------------------") print("timestep: " + str(iiter)) print(" MM Energy: " + str(mm_energy)) # close the production codes mdi.MDI_Send_Command("EXIT", mm_comm)