forked from lijiext/lammps
435 lines
16 KiB
C++
435 lines
16 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2010, Alexander Stukowski
|
|
// All rights reserved. See README.txt for more information.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef __DXA_DISLOCATION_EXTRACTOR_H
|
|
#define __DXA_DISLOCATION_EXTRACTOR_H
|
|
|
|
// Include the LAMMPS headers that we need.
|
|
#include "atom.h"
|
|
#include "comm.h"
|
|
#include "error.h"
|
|
#include "domain.h"
|
|
#include "update.h"
|
|
#include "neigh_list.h"
|
|
using namespace LAMMPS_NS;
|
|
|
|
// This is must be defined when we build the LAMMPS module (and not the DXA standalone tool).
|
|
#define DXA_LAMMPS_MODULE
|
|
|
|
// Enables additional sanity checks.
|
|
#define DEBUG_DISLOCATIONS
|
|
|
|
// In LAMMPS, we always use double precision floating-point numbers.
|
|
#define USE_DOUBLE_PRECISION_FP
|
|
|
|
#include "../src/dxa/DXATracing.h"
|
|
#include "../src/util/FILEStream.h"
|
|
|
|
/**
|
|
*
|
|
*/
|
|
class DXADislocationExtractor : private DXATracing, Pointers
|
|
{
|
|
public:
|
|
|
|
/// Constructor.
|
|
///
|
|
/// Expects a pointer to the global LAMMPS object.
|
|
///
|
|
/// Exact mode flag:
|
|
///
|
|
/// Enables serial processing, i.e. the master processor performs the complete analysis.
|
|
/// This allows for proper handling of periodic boundary conditions.
|
|
DXADislocationExtractor(LAMMPS* lmp, bool _exactMode = false) : DXATracing(msgLoggerStream, verboseLoggerStream), Pointers(lmp)
|
|
{
|
|
exactMode = _exactMode;
|
|
|
|
// Connect internal logging streams to global LAMMPS streams.
|
|
if(comm->me == 0) {
|
|
msgLoggerStream.rdbuf()->setFiles(screen, logfile);
|
|
verboseLoggerStream.rdbuf()->setFiles(screen, logfile);
|
|
}
|
|
this->processor = comm->me;
|
|
}
|
|
|
|
/// Extracts all dislocations from the current simulation state.
|
|
///
|
|
/// First copies atoms from LAMMPS array to internal memory.
|
|
/// Also adopts the nearest neighbor lists from LAMMPS.
|
|
///
|
|
/// Parameters:
|
|
///
|
|
/// neighborList A full neighbor list provided by LAMMPS, which contains neighbors of ghost atoms.
|
|
/// cnaCutoff Cutoff radius to be used for the common neighbor analysis.
|
|
///
|
|
void extractDislocations(NeighList* neighborList, double cnaCutoff, int lineSmoothingLevel = DEFAULT_LINE_SMOOTHING_LEVEL, int lineCoarseningLevel = DEFAULT_LINE_COARSENING_LEVEL)
|
|
{
|
|
// Check parameters.
|
|
if(cnaCutoff <= 0.0) lammps->error->all(FLERR,"Common neighbor analysis cutoff radius must be positive.");
|
|
|
|
// Release any memory allocated during last call.
|
|
cleanup();
|
|
|
|
// Setup simulation cell.
|
|
if(exactMode) {
|
|
pbc[0] = domain->periodicity[0] != 0;
|
|
pbc[1] = domain->periodicity[1] != 0;
|
|
pbc[2] = domain->periodicity[2] != 0;
|
|
}
|
|
else {
|
|
pbc[0] = pbc[1] = pbc[2] = false;
|
|
}
|
|
simulationCell(0,0) = domain->h[0];
|
|
simulationCell(1,1) = domain->h[1];
|
|
simulationCell(2,2) = domain->h[2];
|
|
simulationCell(1,2) = domain->h[3];
|
|
simulationCell(0,2) = domain->h[4];
|
|
simulationCell(0,1) = domain->h[5];
|
|
simulationCell(1,0) = simulationCell(2,0) = simulationCell(2,1) = 0;
|
|
simulationCellOrigin.X = domain->boxlo[0];
|
|
simulationCellOrigin.Y = domain->boxlo[1];
|
|
simulationCellOrigin.Z = domain->boxlo[2];
|
|
setCNACutoff(cnaCutoff);
|
|
setupSimulationCell(cnaCutoff);
|
|
timestep = update->ntimestep;
|
|
|
|
// Transfer LAMMPS atoms to internal array.
|
|
if(exactMode)
|
|
transferAllLAMMPSAtoms();
|
|
else
|
|
transferLocalLAMMPSAtoms(neighborList);
|
|
|
|
if(exactMode == false || processor == 0) {
|
|
|
|
// Perform common neighbor analysis to identify crystalline atoms.
|
|
performCNA();
|
|
|
|
// Order the neighbor lists of crystalline atoms.
|
|
orderCrystallineAtoms();
|
|
|
|
// Cluster crystalline atoms.
|
|
clusterAtoms();
|
|
|
|
#ifdef PROCESSED_ATOMS
|
|
// Enable this code block to get the processed atoms.
|
|
{
|
|
ostringstream ss;
|
|
ss << "proc_" << comm->me << ".atoms.dump";
|
|
ofstream stream(ss.str().c_str());
|
|
writeAtomsDumpFile(stream);
|
|
}
|
|
#endif
|
|
|
|
// Create the nodes of the interface mesh.
|
|
createInterfaceMeshNodes();
|
|
|
|
// Create the facets of the interface mesh.
|
|
createInterfaceMeshFacets();
|
|
|
|
#ifdef DEBUG_DISLOCATIONS
|
|
// Check the generated mesh.
|
|
validateInterfaceMesh();
|
|
#endif
|
|
|
|
// Generate and advance Burgers circuits on the interface mesh.
|
|
traceDislocationSegments();
|
|
|
|
// Smooth dislocation lines.
|
|
smoothDislocationSegments(lineSmoothingLevel, lineCoarseningLevel);
|
|
}
|
|
|
|
if(exactMode) {
|
|
// Wrap segments at simulation cell boundaries.
|
|
wrapDislocationSegments();
|
|
// Distribute extracted dislocation segments to back all processors.
|
|
broadcastDislocations();
|
|
}
|
|
|
|
// Clip dislocation lines at processor domain.
|
|
Point3 subOrigin;
|
|
Matrix3 subCell;
|
|
if(domain->triclinic) {
|
|
subOrigin = simulationCellOrigin + simulationCell * Vector3(domain->sublo_lamda);
|
|
subCell.setColumn(0, simulationCell.column(0) * (domain->subhi_lamda[0] - domain->sublo_lamda[0]));
|
|
subCell.setColumn(1, simulationCell.column(1) * (domain->subhi_lamda[1] - domain->sublo_lamda[1]));
|
|
subCell.setColumn(2, simulationCell.column(2) * (domain->subhi_lamda[2] - domain->sublo_lamda[2]));
|
|
} else {
|
|
subOrigin = Point3(domain->sublo);
|
|
subCell.setColumn(0, Vector3(domain->subhi[0] - domain->sublo[0], 0, 0));
|
|
subCell.setColumn(1, Vector3(0, domain->subhi[1] - domain->sublo[1], 0));
|
|
subCell.setColumn(2, Vector3(0, 0, domain->subhi[2] - domain->sublo[2]));
|
|
}
|
|
clipDislocationLines(subOrigin, subCell);
|
|
|
|
#ifdef OUTPUT_SEGMENTS
|
|
// Enable this code block to see the extracted dislocation segments on each processor.
|
|
{
|
|
ostringstream ss;
|
|
ss << "proc_" << comm->me << ".dislocations.vtk";
|
|
ofstream stream(ss.str().c_str());
|
|
writeDislocationsVTKFile(stream);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// Returns the list of extracted dislocation segments.
|
|
const vector<DislocationSegment*>& getSegments() const { return this->segments; }
|
|
|
|
protected:
|
|
|
|
/// Copies local atoms and neighbor lists from LAMMPS to internal array.
|
|
void transferLocalLAMMPSAtoms(NeighList* neighborList)
|
|
{
|
|
// Allocate internal atoms array.
|
|
int numTotalAtoms = atom->nlocal + atom->nghost;
|
|
inputAtoms.resize(numTotalAtoms);
|
|
numLocalInputAtoms = numTotalAtoms;
|
|
|
|
// Transfer local and ghost atoms to internal array.
|
|
for(int i = 0; i < numTotalAtoms; i++) {
|
|
InputAtom& a = inputAtoms[i];
|
|
a.tag = i + 1;
|
|
a.pos.X = atom->x[i][0];
|
|
a.pos.Y = atom->x[i][1];
|
|
a.pos.Z = atom->x[i][2];
|
|
a.flags = 0;
|
|
a.cluster = NULL;
|
|
a.numNeighbors = 0;
|
|
a.setFlag(ATOM_IS_LOCAL_ATOM);
|
|
}
|
|
|
|
// Adopt nearest neighbor lists from LAMMPS.
|
|
if(neighborList->ghostflag) {
|
|
DISLOCATIONS_ASSERT(neighborList->inum + neighborList->gnum == numTotalAtoms);
|
|
int tt = 0;
|
|
for(int ii = 0; ii < numTotalAtoms; ii++) {
|
|
int i = neighborList->ilist[ii];
|
|
DISLOCATIONS_ASSERT(i < numTotalAtoms);
|
|
InputAtom& ai = inputAtoms[i];
|
|
int* jlist = neighborList->firstneigh[i];
|
|
int jnum = neighborList->numneigh[i];
|
|
if(ii >= neighborList->inum)
|
|
tt += jnum;
|
|
for(int jj = 0; jj < jnum; jj++) {
|
|
int j = jlist[jj];
|
|
j &= NEIGHMASK;
|
|
DISLOCATIONS_ASSERT(j >= 0 && j < numTotalAtoms);
|
|
if(j < i) continue;
|
|
InputAtom& aj = inputAtoms[j];
|
|
if(DistanceSquared(aj.pos, ai.pos) >= cnaCutoff * cnaCutoff) continue;
|
|
if(ai.numNeighbors == MAX_ATOM_NEIGHBORS)
|
|
raiseError("Maximum number of nearest neighbors exceeded. Atom %i has more than %i nearest neighbors (built-in maximum number).", ai.tag, MAX_ATOM_NEIGHBORS);
|
|
ai.addNeighbor(&aj);
|
|
if(aj.numNeighbors == MAX_ATOM_NEIGHBORS)
|
|
raiseError("Maximum number of nearest neighbors exceeded. Atom %i has more than %i nearest neighbors (built-in maximum number).", aj.tag, MAX_ATOM_NEIGHBORS);
|
|
aj.addNeighbor(&ai);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for(int ii = 0; ii < neighborList->inum; ii++) {
|
|
int i = neighborList->ilist[ii];
|
|
InputAtom& ai = inputAtoms[i];
|
|
int* jlist = neighborList->firstneigh[i];
|
|
int jnum = neighborList->numneigh[i];
|
|
for(int jj = 0; jj < jnum; jj++) {
|
|
int j = jlist[jj];
|
|
j &= NEIGHMASK;
|
|
DISLOCATIONS_ASSERT(j >= 0 && j < numTotalAtoms);
|
|
if(j < i) continue;
|
|
InputAtom& aj = inputAtoms[j];
|
|
if(DistanceSquared(aj.pos, ai.pos) < cnaCutoff * cnaCutoff) {
|
|
if(ai.numNeighbors == MAX_ATOM_NEIGHBORS)
|
|
raiseError("Maximum number of nearest neighbors exceeded. Atom %i has more than %i nearest neighbors (built-in maximum number).", ai.tag, MAX_ATOM_NEIGHBORS);
|
|
ai.addNeighbor(&aj);
|
|
if(aj.numNeighbors == MAX_ATOM_NEIGHBORS)
|
|
raiseError("Maximum number of nearest neighbors exceeded. Atom %i has more than %i nearest neighbors (built-in maximum number).", aj.tag, MAX_ATOM_NEIGHBORS);
|
|
aj.addNeighbor(&ai);
|
|
}
|
|
if(j >= atom->nlocal) {
|
|
for(int kk = 0; kk < jnum; kk++) {
|
|
int k = jlist[kk];
|
|
k &= NEIGHMASK;
|
|
DISLOCATIONS_ASSERT(k < (int)inputAtoms.size());
|
|
if(k == j) continue;
|
|
if(k < atom->nlocal) continue;
|
|
InputAtom& ak = inputAtoms[k];
|
|
if(DistanceSquared(aj.pos, ak.pos) >= cnaCutoff * cnaCutoff) continue;
|
|
if(aj.hasNeighbor(&ak)) continue;
|
|
if(aj.numNeighbors == MAX_ATOM_NEIGHBORS)
|
|
raiseError("Maximum number of nearest neighbors exceeded. Atom %i has more than %i nearest neighbors (built-in maximum number).", aj.tag, MAX_ATOM_NEIGHBORS);
|
|
aj.addNeighbor(&ak);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Copies atoms from LAMMPS to internal array and gathers all atoms on the master processor.
|
|
void transferAllLAMMPSAtoms()
|
|
{
|
|
// Determine maximum number of local atoms a single processor has.
|
|
int nlocalatoms_max;
|
|
MPI_Reduce(&atom->nlocal, &nlocalatoms_max, 1, MPI_INT, MPI_MAX, 0, world);
|
|
|
|
if(processor == 0) {
|
|
// Allocate internal atoms array.
|
|
inputAtoms.resize(atom->natoms);
|
|
numLocalInputAtoms = atom->natoms;
|
|
vector<InputAtom>::iterator currentAtom = inputAtoms.begin();
|
|
|
|
// Copy local atoms of master processor into internal array.
|
|
for(int i = 0; i < atom->nlocal; i++, ++currentAtom) {
|
|
currentAtom->tag = currentAtom - inputAtoms.begin() + 1;
|
|
currentAtom->pos.X = atom->x[i][0];
|
|
currentAtom->pos.Y = atom->x[i][1];
|
|
currentAtom->pos.Z = atom->x[i][2];
|
|
currentAtom->flags = 0;
|
|
currentAtom->cluster = NULL;
|
|
currentAtom->numNeighbors = 0;
|
|
currentAtom->setFlag(ATOM_IS_LOCAL_ATOM);
|
|
}
|
|
|
|
if(comm->nprocs > 1) {
|
|
// Allocate receive buffer.
|
|
vector<double> buffer(nlocalatoms_max * 3);
|
|
|
|
// Receive atoms from other processors.
|
|
for(int iproc = 1; iproc < comm->nprocs; iproc++) {
|
|
MPI_Status status;
|
|
MPI_Recv(buffer.empty() ? NULL : &buffer.front(), nlocalatoms_max * 3, MPI_DOUBLE, iproc, 0, world, &status);
|
|
int ndoubles;
|
|
MPI_Get_count(&status, MPI_DOUBLE, &ndoubles);
|
|
int nReceived = ndoubles / 3;
|
|
|
|
vector<double>::const_iterator data = buffer.begin();
|
|
for(int i = 0; i < nReceived; i++, ++currentAtom) {
|
|
currentAtom->tag = currentAtom - inputAtoms.begin() + 1;
|
|
currentAtom->pos.X = *data++;
|
|
currentAtom->pos.Y = *data++;
|
|
currentAtom->pos.Z = *data++;
|
|
currentAtom->flags = 0;
|
|
currentAtom->cluster = NULL;
|
|
currentAtom->numNeighbors = 0;
|
|
currentAtom->setFlag(ATOM_IS_LOCAL_ATOM);
|
|
}
|
|
}
|
|
}
|
|
DISLOCATIONS_ASSERT(currentAtom == inputAtoms.end());
|
|
}
|
|
else {
|
|
// Allocate and fill send buffer.
|
|
vector<double> buffer(atom->nlocal * 3);
|
|
vector<double>::iterator data = buffer.begin();
|
|
for(int i = 0; i < atom->nlocal; i++) {
|
|
*data++ = atom->x[i][0];
|
|
*data++ = atom->x[i][1];
|
|
*data++ = atom->x[i][2];
|
|
}
|
|
// Send local atom coordinates to master proc.
|
|
MPI_Send(buffer.empty() ? NULL : &buffer.front(), buffer.size(), MPI_DOUBLE, 0, 0, world);
|
|
}
|
|
|
|
// Make sure all input atoms are wrapped at periodic boundary conditions.
|
|
wrapInputAtoms(NULL_VECTOR);
|
|
|
|
// Build nearest neighbor lists.
|
|
buildNearestNeighborLists();
|
|
}
|
|
|
|
/// This structure is used to communicate dislocation segments between processors.
|
|
struct DislocationSegmentComm {
|
|
LatticeVector burgersVector;
|
|
Vector3 burgersVectorWorld;
|
|
int numPoints;
|
|
};
|
|
|
|
/// After the master processor has extracted all dislocation segments, this function broadcasts the dislocations
|
|
/// back to all other processor.
|
|
void broadcastDislocations()
|
|
{
|
|
if(comm->nprocs == 1) return; // Nothing to do in serial mode.
|
|
|
|
// Broadcast number of segments.
|
|
int numSegments = segments.size();
|
|
MPI_Bcast(&numSegments, 1, MPI_INT, 0, world);
|
|
|
|
// Allocate send/receive buffer for dislocation segments.
|
|
vector<DislocationSegmentComm> segmentBuffer(numSegments);
|
|
|
|
// The total number of line points (sum of all segments).
|
|
int numLinePoints = 0;
|
|
|
|
if(processor == 0) {
|
|
// Fill segment send buffer.
|
|
vector<DislocationSegmentComm>::iterator sendItem = segmentBuffer.begin();
|
|
for(vector<DislocationSegment*>::const_iterator segment = segments.begin(); segment != segments.end(); ++segment, ++sendItem) {
|
|
sendItem->burgersVector = (*segment)->burgersVector;
|
|
sendItem->burgersVectorWorld = (*segment)->burgersVectorWorld;
|
|
sendItem->numPoints = (*segment)->line.size();
|
|
numLinePoints += (*segment)->line.size();
|
|
}
|
|
}
|
|
// Broadcast segments.
|
|
MPI_Bcast(segmentBuffer.empty() ? NULL : &segmentBuffer.front(), segmentBuffer.size() * sizeof(segmentBuffer[0]), MPI_CHAR, 0, world);
|
|
|
|
if(processor != 0) {
|
|
// Extract segments from receive buffer.
|
|
segments.reserve(segmentBuffer.size());
|
|
for(vector<DislocationSegmentComm>::const_iterator receiveItem = segmentBuffer.begin(); receiveItem != segmentBuffer.end(); ++receiveItem) {
|
|
DislocationSegment* newSegment = segmentPool.construct(receiveItem->burgersVector, receiveItem->burgersVectorWorld);
|
|
newSegment->index = segments.size() + 1;
|
|
segments.push_back(newSegment);
|
|
numLinePoints += receiveItem->numPoints;
|
|
}
|
|
}
|
|
|
|
// Allocate send/receive buffer for dislocation points.
|
|
vector<Point3> pointBuffer(numLinePoints);
|
|
|
|
if(processor == 0) {
|
|
// Fill point send buffer.
|
|
vector<Point3>::iterator sendItem = pointBuffer.begin();
|
|
for(vector<DislocationSegment*>::const_iterator segment = segments.begin(); segment != segments.end(); ++segment) {
|
|
for(deque<Point3>::const_iterator p = (*segment)->line.begin(); p != (*segment)->line.end(); ++p, ++sendItem)
|
|
*sendItem = *p;
|
|
}
|
|
DISLOCATIONS_ASSERT(sendItem == pointBuffer.end());
|
|
}
|
|
// Broadcast segments.
|
|
MPI_Bcast(pointBuffer.empty() ? NULL : &pointBuffer.front(), pointBuffer.size() * sizeof(pointBuffer[0]), MPI_CHAR, 0, world);
|
|
|
|
if(processor != 0) {
|
|
// Extract points from receive buffer.
|
|
vector<DislocationSegment*>::const_iterator segment = segments.begin();
|
|
vector<Point3>::const_iterator p = pointBuffer.begin();
|
|
for(vector<DislocationSegmentComm>::const_iterator receiveItem = segmentBuffer.begin(); receiveItem != segmentBuffer.end(); ++receiveItem, ++segment) {
|
|
(*segment)->line.assign(p, p + receiveItem->numPoints);
|
|
p += receiveItem->numPoints;
|
|
}
|
|
DISLOCATIONS_ASSERT(p == pointBuffer.end());
|
|
}
|
|
}
|
|
|
|
protected:
|
|
|
|
/// Pointer to the main LAMMPS object.
|
|
LAMMPS* lammps;
|
|
|
|
/// Enables serial processing, which provides exact handling of periodic boundary conditions.
|
|
bool exactMode;
|
|
|
|
/// The output stream to which log messages are sent.
|
|
stdio_osyncstream msgLoggerStream;
|
|
|
|
/// The output stream to which verbose log messages are sent.
|
|
stdio_osyncstream verboseLoggerStream;;
|
|
};
|
|
|
|
#endif // __DXA_DISLOCATION_EXTRACTOR_H
|