### 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) ``` 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. ```python 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.: ```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) ``` 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 ```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, 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 ```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, 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. ```python 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: ```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. ````{Attention} When using rotated crystal frames, it is required to specify the corresponding orientation matrix in the `state` dictionary before running a simulation, ```python state["Rorient"] = Rorient ``` so that the code can internally retrieve Burgers vectors and plane normal vectors in the crystal frame when needed. ````