Code architecture and data structures¶
Overview¶
ExaDiS is implemented based on a modular design in which each functionality can be viewed as an independent module. This design is essential to make the code extensible and enable its coupling with various other modules within the OpenDiS framework.
ExaDiS core backend is written in modern C++ and built based on the Kokkos framework. This allows to maintain a unique version of the source code that is largely agnostic of the target hardware: the same source code can be compiled for a wide variety of systems and hardwares, e.g. for serial CPU machines, using OpenMP, or for GPU machines using cuda or hip architectures, for instance.
ExaDiS classes and functions are implemented with high-performance and high-parallelism kernel execution in mind. Yet, the code is designed to abstract away as much complexity as possible, so as to lower the entry barrier for developing new functionalities. For prototyping or first implementation pass, ExaDiS also provides convenient helper functions and data structures that are easier to manipulate than Kokkos-based implementations. An illustration of this is the Topology class implementation, where class TopologySerial implements the split-multi-node procedure in a serial fashion on the host (CPU), while TopologyParallel is an implementation of the same split-multi-node procedure but executed with highly-parallel kernels on the device (GPU).
Project structure¶
Here is a brief description of the directory structure of the ExaDiS code:
cmake/: cmake related files, including pre-defined build system optionsexamples/: examples of scripts and simulationseach example is numbered and placed in a dedicated subfolder
kokkos/: directory containing the Kokkos submodulepython/: files related to the python binding implementationpybind11/directory containing the pybind11 submoduleexadis_pybind.cpp: implementation of the C++ classes / functions bindingpyexadis_base.py: interface enabling the use of pyexadis within OpenDiSpyexadis_utils.py: pyexadis utility functions
src/: C++ source and header files (*.cpp,*.h)base class files and common functions are at the root of the
srcdirectoryindividual module implementations are placed in subfolders
collision_types/: implementation of collision modulesforce_types/: implementation of force modulesintegrator_types/: implementation of integrator modulesmobility_types/: implementation of mobility modulesneighbor_types/: implementation of neighbor modulestopology_types/: implementation of topology modules
tests/: files for testing and debuggingbenchmark/: benchmark tests to evaluate the performance of the codedebug.h: debug utility functionstest_exadis.cpp: simple ExaDiS test simulations
Dislocation network and data structures¶
Dislocation networks in ExaDiS are stored using two major classes, SerialDisNet and DeviceDisNet. Class DisNetManager is used as a container to synchronize dislocation networks between the two classes for use in the different execution spaces (host CPU / device GPU).
SerialDisNet class¶
SerialDisNet is a STL-based class that allows to easily create, manipulate, and modify dislocation networks on the host: a SerialDisNet instance is marked for a Kokkos::Serial execution space associated with a Kokkos::HostSpace memory space. The SerialDisNet class implements all low-level topological operations, e.g. split_seg(), split_node(), and merge_nodes() methods. It is designed to create initial dislocation networks and perform topological operations on existing networks.
Constructors¶
SerialDisNet(): instantiate an empty dislocation networkSerialDisNet(double Lbox): instantiate an empty dislocation network within a cubic cell of sideLboxSerialDisNet(const Cell& cell): instantiate an empty dislocation network within a cellCell
Properties¶
Cell cell: dislocation network cellstd::vector<DisNode> nodes: array of nodesstd::vector<DisSeg> segs: array of segmentsstd::vector<Conn> conn: array of node connectivityint Nnodes_local: number of local nodesint Nsegs_local: number of local segments
Methods¶
int number_of_nodes(): returns the number of nodes in the networkint number_of_segs(): returns the number of segments in the networkNodeTag get_new_tag(): returns a new available node tagvoid free_tag(NodeTag& tag): release an existing node tagvoid update_ptr(): updates pointers to the nodes, segments and connectivity data as STL arrays may be reallocated under-the-hood, e.g. due to resize during topology changesDisNode* get_nodes(): returns a pointer to the nodes array dataDisSeg* get_segs()returns a pointer to the segments array dataConn* get_conn()returns a pointer to the connectivity array datavoid add_node(const Vec3& pos): add a node by specifying its positionposvoid add_node(const Vec3& pos, int constraint): add a node by specifying its positionposand its constraintconstraintvoid add_node(const NodeTag& tag, const Vec3& pos): add a node by specifying its tagtagand positionposvoid add_node(const NodeTag& tag, const Vec3& pos, int constraint): add a node by specifying its tagtag, positionpos, and its constraintconstraintvoid add_seg(int n1, int n2, const Vec3& b): add a segment with Burgers vectorbconnecting node at indexn1with node at indexn2void add_seg(int n1, int n2, const Vec3& b, const Vec3& p): add a segment with Burgers vectorband plane nornalpconnecting node at indexn1with node at indexn2int find_connection(int n1, int n2): finds in the node connectivity array ofn1if there exists a connection (segment) to node at indexn2void generate_connectivity(): generate the connectivity array for all nodesdouble seg_length(int i): returns the length of segmentibool constrained_node(int i): returns if nodeiis a constrained nodebool discretization_node(int i): returns if nodeiis a discretization nodevoid update_node_plastic_strain(int i, const Vec3& pold, const Vec3& pnew, Mat33& dEp): update the plastic strain tensordEpwith the increment of moving nodeifrom positionpoldto positionpnewint split_seg(int i, const Vec3& pos, bool update_conn=true): splits segmentiby inserting a new node at positionposand returns the index of the new nodeint split_node(int i, std::vector<int> arms): splits nodeiinto a new node that contains the subset of arms indicesarms, and returns the index of the new nodebool merge_nodes(int n1, int n2, Mat33& dEp): merges nodesn1andn2into noden1, updates the corresponding plastic strain tensordEp, and returns whether the merge succeededbool merge_nodes_position(int n1, int n2, const Vec3 &pos, Mat33& dEp): merges nodesn1andn2into noden1at positionpos, updates the corresponding plastic strain tensordEp, and returns whether the merge succeededvoid remove_segs(std::vector<int> seglist): removes all segments with indices specified inseglistfrom the networkvoid remove_nodes(std::vector<int> nodelist): removes all nodes with indices specified innodelistfrom the networkvoid purge_network(): purge the network from unconnected nodes and zero Burgers vectors segmentsstd::vector<std::vector<int> > physical_links(): decompose the network into a set of dislocation links connecting physical network nodesdouble dislocation_density(double burgmag): computes the dislocation density in units of 1/m^2 given the Burgers vector scaling factorburgmagvoid write_data(std::string filename): write the dislocation network into a file using the legacy ParaDiS.dataformatSaveNode save_node(int i): save a dislocation node and all its connectionsvoid restore_node(SaveNode& saved_node): restore a dislocation node and all its connections from a save nodesaved_node
DeviceDisNet class¶
DeviceDisNet is a class that uses Kokkos views to store and access dislocation nodes, segments and connections for device execution (GPU): a DeviceDisNet instance is marked for a Kokkos::DefaultExecutionSpace execution space associated with corresponding default memory space. These will default to the highest available execution/memory spaces available at compile time (e.g. device spaces when compiling for GPU). As of now, the DeviceDisNet class does not implement topological operations.
Constructor¶
DeviceDisNet(const Cell& cell): instantiates an empty dislocation network within a cellCell
Properties¶
Cell cell: dislocation network cellT_nodes nodes: Kokkos view of nodesT_segs segs: Kokkos view of segmentsT_conn conn: Kokkos view of node connectivityint Nnodes_local: number of local nodesint Nsegs_local: number of local segments
Methods¶
void update_ptr(): updates network pointers (dummy forDeviceDisNet)T_nodes::pointer_type get_nodes(): returns a pointer to the nodes view dataT_segs::pointer_type get_segs()returns a pointer to the segments view dataT_conn::pointer_type get_conn()returns a pointer to the connectivity view data
DisNetManager class¶
Class DisNetManager is used as a container to synchronize dislocation networks between the two classes SerialDisNet and DeviceDisNet for use in the different execution spaces. A DisNetManager object is instantiated by providing a SerialDisNet (resp. DeviceDisNet) object, and a mirror DeviceDisNet (resp. SerialDisNet) object is automatically created. A given instance of a network type is requested by using functions get_serial_network() and get_device_network(). When calling these functions, a memory copy is triggered only if the requested network type instance is not marked as active to minimize memory transfers between host and device memory spaces. DisNetManager is used as the fundamental network class associated with the ExaDiS System object that is being propagated through the different ExaDiS modules.
Constructors¶
DisNetManager(SerialDisNet* n): instantiates aDisNetManagerobject from aSerialDisNetdislocation networknDisNetManager(DeviceDisNet* d): instantiates aDisNetManagerobject from aDeviceDisNetdislocation networkd
Methods¶
SerialDisNet* get_serial_network(): returns a pointer to aSerialDisNetinstance of the dislocation networkDeviceDisNet* get_device_network(): returns a pointer to aDeviceDisNetinstance of the dislocation networkint get_active(): returns the currently active network type (SERIAL_ACTIVEorDEVICE_ACTIVE)void set_active(int a): sets as active the network typea = SERIAL_ACTIVEora = DEVICE_ACTIVEint Nnodes_local(): returns the number of local nodes in the networkint Nsegs_local(): returns the number of local segments in the network
System class¶
System is the base class in ExaDiS that contains all information about the simulated dislocation system, including the parameters, the crystal instance, and the dislocation network object. A System object is the fundamental data structure that is being propagated from modules to modules.
Important
A System object must be allocated using the exadis_new() or the make_system() helper functions to ensure it is placed on a memory space accessible to all execution spaces.
Constructors¶
System(): instantiates an emptySystemobjectSystem* make_system(SerialDisNet* net, Crystal crystal, Params params): creates aSystemobject from an initial dislocation network, crystal instance, and parameters object.
Properties¶
DisNetManager* net_mngr: dislocation network manager of the systemdouble neighbor_cutoff: neighbor cutoff for the simulated systemParams params: parameters of the simulated systemCrystal crystal: crystal object of the simulated systemT_x xold: Kokkos view to store old nodal positionsMat33 extstress: external/applied stress tensordouble realdt: current global time step size of the simulationMat33 dEp: current increment of plastic strain for the time stepMat33 dWp: current increment of plastic spin for the time stepdouble density: current dislocation density in the systemSystemTimer timer[]: array of timers of the systemint numdevtimer: number of development timers of the systemSystemTimer devtimer[]: array of development timers of the system
Methods¶
void initialize(Params _params, Crystal _crystal, SerialDisNet* network): initialize aSystemobject with a parameters object, a crystal instance, and an initial dislocation networkvoid register_neighbor_cutoff(double cutoff): register a minimum cutoff distance to be used in the simulated systemSerialDisNet* get_serial_network(): returns a pointer to aSerialDisNetinstance of the dislocation network of the systemDeviceDisNet* get_device_network(): returns a pointer to aDeviceDisNetinstance of the dislocation network of the systemint Nnodes_local(): returns the local number of dislocation nodes in the systemint Nsegs_local(): returns the local number of dislocation segments in the systemint Nnodes_total(): returns the total number of dislocation nodes in the systemint Nsegs_total(): returns the total number of dislocation segments in the systemvoid plastic_strain(): computes the plastic strain resulting from the motion of all dislocation lines between the nodal positions stored inxoldand the current positionsvoid reset_glide_planes(): reset and select appropriate glide planes for all segments in the networkvoid write_config(std::string filename): writes the network configuration into a file (in legacy ParaDiS format for now)void print_timers(bool dev=false): prints system timers