Creating initial dislocation configurations

Initial dislocation configurations for OpenDiS simulations can be created in various ways.

Important

Independently on how it was created, any dislocation network must be wrapped into a DisNetManager object before it can be used within OpenDiS modules.

Reading from a file

A dislocation network object can be read from a legacy ParaDiS format file .data using the ExaDisNet built-in method read_paradis(), e.g.:

from framework.disnet_manager import DisNetManager
import pyexadis
from pyexadis_base import ExaDisNet
pyexadis.initialize()

G = ExaDisNet()
G.read_paradis('config.data')
N = DisNetManager(G)

Using utility functions

Some utility functions are provided in class ExaDisNet and in file core/exadis/python/pyexadis_utils.py to generate basic dislocation configurations (Frank-Read source, infinite lines, etc.).

Ensemble of infinite lines or dipoles

Utility method generate_line_config() can be used to generate a collection of randomly positioned infinite lines or dipoles, e.g.

from framework.disnet_manager import DisNetManager
import pyexadis
from pyexadis_base import ExaDisNet
pyexadis.initialize()

G = ExaDisNet()
G.generate_line_config(crystal, Lbox, num_lines, theta, maxseg, Rorient, seed, verbose)
N = DisNetManager(G)

with

  • crystal: crystal structure, e.g. 'fcc' or 'bcc'

  • Lbox: simulation cell object or the cell size

  • num_lines: number of lines to generate

  • theta (optional): list of character angles in degrees

  • maxseg (optional): maximum segment size to discretize the dislocation lines

  • Rorient (optional): crystal orientation matrix

  • seed (optional): seed number for the random generation

  • verbose (optional): print out lines information

The dislocations are generated by cycling through the list of signed (+/- Burgers vectors) slip systems (1/2<110>{111} systems in fcc and 1/2<111>{110} systems in bcc). To create dipoles (neutral Burgers charge), num_lines must be a multiple of 24 (=12*2). If a list of character angles theta (optional) is provided, each dislocation will be randomly assigned one of the character angles from the list. If not provided, the character angles will be chosen such that the dislocation density is roughly equal between all slip systems.

Ensemble of prismatic loops

Utility method generate_prismatic_config() can be used to generate a collection of randomly positioned prismatic (i.e. with all arms of edge character) dislocation loops, e.g.

from framework.disnet_manager import DisNetManager
import pyexadis
from pyexadis_base import ExaDisNet
pyexadis.initialize()

G = ExaDisNet()
G.generate_prismatic_config(crystal, Lbox, num_loops, radius, maxseg, Rorient, seed, uniform)
N = DisNetManager(G)

with

  • crystal: crystal structure, e.g. 'fcc' or 'bcc'

  • Lbox: simulation cell object or the cell size

  • num_loops: number of loops to generate

  • radius: radius of the loops, or radius range [min_radius, max_radius]

  • maxseg (optional): maximum segment size to discretize the loops

  • Rorient (optional): crystal orientation matrix

  • seed (optional): seed number for the random generation

  • uniform (optional): make the spatial loop distribution close to uniform

The dislocation loops are generated by cycling through the list of native Burgers vectors for the crystal structure (6 1/2<110> Burgers vectors for fcc and 4 1/2<111> Burgers vectors for bcc).

Individual Frank-Read source

Utility function insert_frank_read_src() from pyexadis_utils can be used to insert a Frank-Read source, e.g.:

from framework.disnet_manager import DisNetManager
import pyexadis
from pyexadis_base import ExaDisNet
from pyexadis_utils import insert_frank_read_src
pyexadis.initialize()

# Define box
Lbox = 1000.0
cell = pyexadis.Cell(Lbox)
nodes, segs = [], []

# Insert Frank-Read source
burg = 1.0/np.sqrt(2.0)*np.array([1.,1.,0.])
plane = np.array([-1.,1.,1.])
length = 0.5*Lbox
center = cell.center()
nodes, segs = insert_frank_read_src(cell, nodes, segs, burg, plane,
                                    length, center, theta=90.0, numnodes=10)
G = ExaDisNet(cell, nodes, segs)
N = DisNetManager(G)

To insert multiple Frank-Read sources into the same configuration, one can call insert_frank_read_src() repeatedly, e.g.

...
# Frank-Read source 1
nodes, segs = insert_frank_read_src(cell, nodes, segs, burg1, plane1,
                                    length1, center1, theta1, numnodes1)
# Frank-Read source 2
nodes, segs = insert_frank_read_src(cell, nodes, segs, burg2, plane2,
                                    length2, center2, theta2, numnodes2)
...

Individual infinite line

Utility function insert_infinite_line() from pyexadis_utils can be used to insert an infinite, straight dislocation line, e.g.:

from framework.disnet_manager import DisNetManager
import pyexadis
from pyexadis_base import ExaDisNet
from pyexadis_utils import insert_infinite_line
pyexadis.initialize()

nodes, segs = [], []

nodes, segs = insert_infinite_line(cell, nodes, segs, burg, plane, origin,
                                   theta=0.0, linedir=None, maxseg=-1, trial=False)
G = ExaDisNet(cell, nodes, segs)
N = DisNetManager(G)

with

  • cell: network cell object or cell size

  • nodes: list of nodes to which the line will be appended

  • segs: list of segments to which the line will be appended

  • burg: Burgers vector of the line

  • plane: habit plane normal of the line

  • origin: position of the origin of the line

  • theta: character angle of the line in degrees

  • linedir: line direction vector

  • maxseg (optional): maximum discretization size of the line

  • trial (optional): flag to specify a trial insertion only

The character angle is specified with either theta the character angle of the line in degrees, or linedir the line direction vector. Parameter trial (optional) is a flag to do a trial insertion only to test if insertion is possible (depending on the cell size and crystal orientation, insertion of an infinite line may result in an unacceptably long line under PBC). When trial=True, the function returns the length of the trial line (if insertion is successful) or -1 (if insertion failed), instead of returning the nodes and segs lists.

Manual generation

One can create a dislocation configuration manually by specifying the lists of nodes and segments properties. Configurations can be created using both pydis or pyexadis data structures.

As an example, let’s assume one wants to insert a 1/2<110> screw dislocation line of length 100b lying on the (-1,1,1) plane and discretized into 3 nodes.

Using pydis

To create the example configuration using pydis, one can do

from framework.disnet_manager import DisNetManager
from pydis import DisNode, DisNet, Cell

Ldis = 100.0 # dislocation length
Lbox = 2*Ldis # simulation box size
burg = 1.0/np.sqrt(2.0)*np.array([1.,1.,0.]) # Burgers vector
plane = np.array([-1.,1.,1.]) # plane normal

# Simulation cell object
cell = Cell(h=Lbox*np.eye(3), is_periodic=[True,True,True])

# List of nodes, nodes = [x,y,z,constraint]
linevec = Ldis*burg # line vector
rn = np.array([[*(-0.5*linevec), DisNode.Constraints.PINNED_NODE],
               [*( 0.0*linevec), DisNode.Constraints.UNCONSTRAINED],
               [*( 0.5*linevec), DisNode.Constraints.PINNED_NODE]])
rn[:,0:3] += cell.center() # translate line to the center of the box

# List of segments, links = [node1,node2,burg,plane]
links = np.array([[0, 1, *burg, *plane],
                  [1, 2, *burg, *plane]])

G = DisNet(cell=cell, rn=rn, links=links)
N = DisNetManager(G)

Using pyexadis

To create the example configuration using pyexadis, one can do

from framework.disnet_manager import DisNetManager
import pyexadis
from pyexadis_base import ExaDisNet, NodeConstraints
pyexadis.initialize()

Ldis = 100.0 # dislocation length
Lbox = 2*Ldis # simulation box size
burg = 1.0/np.sqrt(2.0)*np.array([1.,1.,0.]) # Burgers vector
plane = np.array([-1.,1.,1.]) # plane normal

# Simulation cell object
cell = pyexadis.Cell(h=Lbox*np.eye(3), is_periodic=[True,True,True])

# List of nodes, nodes = [x,y,z,constraint]
linevec = Ldis*burg # line vector
nodes = np.array([[*(-0.5*linevec), NodeConstraints.PINNED_NODE],
                  [*( 0.0*linevec), NodeConstraints.UNCONSTRAINED],
                  [*( 0.5*linevec), NodeConstraints.PINNED_NODE]])
nodes[:,0:3] += cell.center() # translate line to the center of the box

# List of segments, segs = [node1,node2,burg,plane]
segs = np.array([[0, 1, *burg, *plane],
                 [1, 2, *burg, *plane]])

G = ExaDisNet(cell, nodes, segs)
N = DisNetManager(G)

Combining several networks

Dislocation networks coming from different sources can be combined together into a single network using utility function combine_networks() from file pyexadis_utils. This can be useful to easily assemble complex initial configurations, e.g.

from framework.disnet_manager import DisNetManager
from pydis import DisNet
import pyexadis
from pyexadis_base import ExaDisNet
from pyexadis_utils import combine_networks
pyexadis.initialize()

# Configuration created with pydis
G1 = DisNet(...)
N1 = DisNetManager(G1)

# Configuration created with pyexadis
G2 = ExaDisNet(...)
N2 = DisNetManager(G2)

N = combine_networks([N1, N2])

Warning

Simulation cells must be identical for all input networks in order for them to be successfully combined into a single network. The user must also ensure that the input networks are defined in the same reference frame.

Rotated crystal frames

It can be convenient or desirable to create a dislocation configuration in a user-defined rotated crystal frame, e.g. such as to align the line direction or slip plane of a given dislocation with the simulation box axes.

As an example, let’s assume one wants to insert an infinite dislocation line with line direction along the y-axis and glide plane normal along the z-axis into a FCC crystal. Since FCC plane normals are along the {111} crystallographic directions, this requires to rotate the crystal frame. This can be done as in the following example:

from framework.disnet_manager import DisNetManager
import pyexadis
from pyexadis_base import ExaDisNet
from pyexadis_utils import insert_infinite_line
pyexadis.initialize()

Lbox = 1000.0 # simulation box size
maxseg = 0.05*Lbox # line discretization
burg = 1.0/np.sqrt(2.0)*np.array([1.,1.,0.]) # Burgers vector
plane = np.array([-1.,1.,1.]) # plane normal
theta = 90.0 # character angle in degrees

# Crystal orientation
plane = plane / np.linalg.norm(plane)
b = burg / np.linalg.norm(burg)
y = np.cross(plane, b)
y = y / np.linalg.norm(y)
linedir = np.cos(theta*np.pi/180.0)*b+np.sin(theta*np.pi/180.0)*y
x = np.cross(linedir, plane)
x = x / np.linalg.norm(x)
Rorient = np.array([x, linedir, plane])

burg = np.matmul(Rorient, burg)
plane = np.matmul(Rorient, plane)
linedir = -np.matmul(Rorient, linedir)

# Create the cell and insert the line
cell = pyexadis.Cell(Lbox)
nodes, segs = [], []
origin = np.array(cell.origin) + 0.5*np.sum(np.array(cell.h), axis=0)
nodes, segs = insert_infinite_line(cell, nodes, segs, burg, plane, origin,
                                   linedir=linedir, maxseg=maxseg)
    
N = DisNetManager(ExaDisNet(cell, nodes, segs))

As done in the above, rotation of the burg and plane vectors are necessary as these vectors must always be expressed in the global frame in the segs array.

Attention

When using rotated crystal frames, it is required to specify the corresponding orientation matrix in the state dictionary before running a simulation,

state["Rorient"] = Rorient

so that the code can internally retrieve Burgers vectors and plane normal vectors in the crystal frame when needed.