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)
import sys import mdi use_mpi4py = False try: from mpi4py import MPI use_mpi4py = True except: pass # Initialize the MDI Library mdi.MDI_Init(sys.argv[2]) # Connect to the engine comm = mdi.MDI_Accept_communicator() # Determine the name of the engine mdi.MDI_Send_Command("<NAME", comm) name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, comm) print("Engine name: " + str(name)) # Send the "EXIT" command to the engine mdi.MDI_Send_Command("EXIT", comm)
import mdi as mdi niterations = 10 # initialize the socket mdi.MDI_Init(sys.argv[2], None) # connect to the production codes ncodes = 2 for icode in range(ncodes): comm = mdi.MDI_Accept_Communicator() # get the name of the code mdi.MDI_Send_Command("<NAME", comm) name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, comm) print('Received connection: ' + str(name)) if name.strip() == 'QM': qm_comm = comm elif name.strip() == 'MM': mm_comm = comm else: raise ValueError('Production code name not recognized') # 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)
# LAMMPS engines are stand-alone codes # world = MPI communicator for just this driver # invoke perform_tasks() directly if not plugin: mdi.MDI_Init(mdiarg) world = mdi.MDI_MPI_get_world_comm() # connect to 2 engines, determine which is MM vs QM mdicomm1 = mdi.MDI_Accept_Communicator() mdicomm2 = mdi.MDI_Accept_Communicator() mdi.MDI_Send_command("<NAME", mdicomm1) name1 = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, mdicomm1) name1 = world.bcast(name1, root=0) mdi.MDI_Send_command("<NAME", mdicomm2) name2 = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, mdicomm2) name2 = world.bcast(name2, root=0) if name1 == "MM" and name2 == "QM": mm_comm = mdicomm1 qm_comm = mdicomm2 elif name1 == "QM" and name2 == "MM": mm_comm = mdicomm2 qm_comm = mdicomm1 else: error("Two engines have invalid names") perform_aimd(world, mm_comm, qm_comm)
import mdi as mdi niterations = 10 # initialize the socket mdi.MDI_Init(sys.argv[2],None) # connect to the production codes ncodes = 1 for icode in range(ncodes): comm = mdi.MDI_Accept_Communicator() # get the name of the code mdi.MDI_Send_Command("<NAME", comm) name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, comm) print('Received connection: ' + str(name)) if name.strip() == 'MM': mm_comm = comm else: raise ValueError('Production code name not recognized') # have the MD code initialize a new MD simulation mdi.MDI_Send_Command("MD_INIT", mm_comm) for iiter in range(niterations): # get the MM energy
# Set the type of elements to receive if len(sys.argv) <= iarg+1: raise Exception("Argument to -stype option not found") stype = sys.argv[iarg+1] iarg += 1 else: raise Exception("Unrecognized argument") iarg += 1 # Connect to the engine comm = mdi.MDI_Accept_Communicator() # Get the name of the engine, which will be checked and verified at the end mdi.MDI_Send_Command("<NAME", comm) initial_name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, comm) recv_type = None if nreceive is not None: # Get the number of elements to receive nreceive_split = re.split("\+|\-|\*\*|\*|\/\/|\/|\%| ",nreceive) print("NRECV: " + str(nreceive_split)) for word in nreceive_split: if len(word) > 0 and word[0] == '<': print("Found command in nreceive_split") # This is a command, so send it to the engine # Assume that the command receives a single integer mdi.MDI_Send_Command(word, comm) value = mdi.MDI_Recv(1, mdi.MDI_INT, comm) print("count: " + str(value))
# Initialize MDI if len(sys.argv) <= iarg + 1: raise Exception("Argument to -mdi option not found") mdi.MDI_Init(sys.argv[iarg + 1], None) iarg += 1 else: raise Exception("Unrecognized argument") iarg += 1 # Connect to the engine comm = mdi.MDI_Accept_Communicator() # Get the name of the engine, which will be checked and verified at the end mdi.MDI_Send_Command("<NAME", comm) initial_name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, comm) ############## print("AAA") #mdi.MDI_Send_Command("@INIT_OPTG", comm) print("BBB") # 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 mdi.MDI_Send_Command("@INIT_MD", comm) for i in range(100): mdi.MDI_Send_Command("@", comm)
world_comm = mdi.MDI_MPI_get_world_comm() # Get the MPI rank of this process if world_comm is not None: my_rank = world_comm.Get_rank() else: my_rank = 0 # Accept a connection from an external driver mdi_comm = mdi.MDI_Accept_Communicator() # This is the part where you can use MDI if my_rank == 0: # <NAME mdi.MDI_Send_Command("<NAME", mdi_comm) engine_name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, mdi_comm) print("ENGINE NAME: " + str(engine_name)) # <ENERGY mdi.MDI_Send_Command("<ENERGY", mdi_comm) energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mdi_comm) print("ENERGY: " + str(energy)) # <KE mdi.MDI_Send_Command("<KE", mdi_comm) energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mdi_comm) print("KE: " + str(energy)) # <KE_ELEC mdi.MDI_Send_Command("<KE_ELEC", mdi_comm) energy = mdi.MDI_Recv(1, mdi.MDI_DOUBLE, mdi_comm)
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
niterations = 10 for iiteration in range(niterations): # Create and connect to a library instance that spans the MPI task communicator MDIEngine("-role ENGINE -name MM -method LIBRARY -driver_name driver", mpi_task_comm) comm = mdi.MDI_Accept_Communicator() # Create and connect to a library instance that spans MPI_COMM_WORLD MDIEngine("-role ENGINE -name unsplit -method LIBRARY -driver_name driver", mpi_world) comm_unsplit = mdi.MDI_Accept_Communicator() # Communicate with the library instance that spans the MPI task communicator mdi.MDI_Send_Command("<NATOMS", comm) natoms = mdi.MDI_Recv(1, mdi.MDI_INT, comm) if world_rank == 0: print("NATOMS: " + str(natoms)) mdi.MDI_Send_Command("EXIT", comm) # Communicate with the library instance that spans MPI_COMM_WORLD mdi.MDI_Send_Command("<NATOMS", comm_unsplit) natoms = mdi.MDI_Recv(1, mdi.MDI_INT, comm_unsplit) if world_rank == 0: print("NATOMS: " + str(natoms)) mdi.MDI_Send_Command("EXIT", comm_unsplit) if use_mpi4py: mpi_world.Barrier()
import mdi as mdi niterations = 1000 # initialize the socket mdi.MDI_Init(sys.argv[2], None) # connect to the production codes ncodes = 1 for icode in range(ncodes): comm = mdi.MDI_Accept_Communicator() # get the name of the code mdi.MDI_Send_Command("<NAME", comm) name = mdi.MDI_Recv(mdi.MDI_NAME_LENGTH, mdi.MDI_CHAR, comm) print('Received connection: ' + str(name)) if name.strip() == 'MM': mm_comm = comm else: raise ValueError('Production code name not recognized') # receive the number of atoms mdi.MDI_Send_Command("<NATOMS", mm_comm) natoms = mdi.MDI_Recv(1, mdi.MDI_INT, mm_comm) # receive the number of atom types mdi.MDI_Send_Command("<NTYPES", mm_comm) ntypes = mdi.MDI_Recv(1, mdi.MDI_INT, mm_comm)