### 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`](../code_structure/data_structure/disnetmanager_class.md) 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`](../core_libraries/exadis_documentation/user_guide/python_modules.md#exadisnet) built-in method `read_paradis()`, e.g.: ```python 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`](../core_libraries/exadis_documentation/user_guide/python_modules.md#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. ```python 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) ``` where `crystal` is the crystal structure, `Lbox` is a simulation cell object or the cell size, `num_lines` the number of lines to generate, `maxseg` (optional) the maximum segment size to discretize the dislocations, `Rorient` (optional) the crystal orientation matrix, `seed` (optional) the seed number for the random generation. 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. ```python from framework.disnet_manager import DisNetManager import pyexadis from pyexadis_base import ExaDisNet pyexadis.initialize() G = ExaDisNet() G.generate_prismatic_config(crystal, Lbox, numsources, radius, maxseg, Rorient, seed) N = DisNetManager(G) ``` where `crystal` is the crystal structure, `Lbox` is a simulation cell object or the cell size, `numsources` the number of loops to generate, `maxseg` (optional) the maximum segment size to discretize the dislocations, `Rorient` (optional) the crystal orientation matrix, `seed` (optional) the seed number for the random generation. The dislocation loops are generated by cycling through the list of native Burgers vectors for the crystal structure. ##### Individual Frank-Read source Utility function `insert_frank_read_src()` from `pyexadis_utils` can be used to insert a Frank-Read source, e.g.: ```python 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. ```python ... # 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.: ```python 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) ``` where `cell` is the network cell object or cell size, `nodes` and `segs` are the list of nodes and segments to which the line will be appended, `burg` the Burgers vector of the line, `plane` the habit plane normal of the line, `origin` the origin position of the line. The character angle can be specified either by specifying `theta` the character angle of the line in degrees, or `linedir` the line direction vector. `maxseg` (optional) is the maximum discretization length of the line. 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 ```python 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, rn = [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 ```python 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, rn = [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, links = [node1,node2,burg,plane] segs = np.array([[0, 1, *burg, *plane], [1, 2, *burg, *plane]]) G = ExaDisNet(cell, nodes, segs) N = DisNetManager(G) ``` #### 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: ```python 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. As a consequence, when using rotated crystal frames, it is also important to specify the corresponding orientation matrix in the `state` dictionary before running a simulation: ```python state["Rorient"] = Rorient ```