{ "cells": [ { "cell_type": "markdown", "id": "9d209664-e22b-4b1e-8218-92e8c27c2620", "metadata": {}, "source": [ "# Material Phases " ] }, { "cell_type": "markdown", "id": "a42fc2d6-d322-479a-b73d-2a716b6385aa", "metadata": {}, "source": [ "The physical and mechanical properties of a polycrystalline materials depend not only on its chemical composition but also on the phases from which it is composed. This tutorial will review the objects and tools available in *Pymicro* to store, load and process the informations related to the various constitutive phases of a material." ] }, { "cell_type": "markdown", "id": "305a749d-095a-4f42-a756-c08ebe8867fa", "metadata": {}, "source": [ "In *Pymicro*, a phase is defined by a set of crystallographic and physical properties. The `CrystallinePhase` class of the `pymicro.crystal.lattice` module allows to store and manipulate phase data. In addition, the *data model* of [the Microstructure class](./Microstructure_class.ipynb) includes a specific *Group* to store phase data in datasets.\n", "\n", "To begin with, we will look at the phase data stored in one of Pymicro's example datasets. This file is zipped in the package to reduce its size. If you are reading through this tutorial as a Notebook, you will first have to unzip the file: " ] }, { "cell_type": "code", "execution_count": null, "id": "1c6e6dca-2962-4c21-be64-b131f81ebcd3", "metadata": {}, "outputs": [], "source": [ "from pymicro import get_examples_data_dir # import file directory path\n", "PYMICRO_EXAMPLES_DATA_DIR = get_examples_data_dir() # get the file directory path\n", "import os \n", "dataset_file = os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'example_microstructure') # test dataset desired file path\n", "tar_file = os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'example_microstructure.tar.gz') # dataset zipped archive path\n", "\n", "# Save current directory\n", "cwd = os.getcwd()\n", "# move to example data directory\n", "os.chdir(PYMICRO_EXAMPLES_DATA_DIR)\n", "# unarchive the dataset\n", "os.system(f'tar -xvf {tar_file}')\n", "# get back to UserGuide directory\n", "os.chdir(cwd)" ] }, { "cell_type": "markdown", "id": "a3edde71-1f51-4bcb-8132-cf9b7fc194a4", "metadata": {}, "source": [ "## Phase Groups" ] }, { "cell_type": "markdown", "id": "28c5d1d7-4b28-4037-9277-7111fff96168", "metadata": {}, "source": [ "Let us now open the dataset and display its content, using the `Microstructure` class:" ] }, { "cell_type": "code", "execution_count": null, "id": "6196e122-e027-4629-a58f-57ce45a44db4", "metadata": {}, "outputs": [], "source": [ "# import SampleData class\n", "from pymicro.crystal.microstructure import Microstructure \n", "# Open Microstructure dataset\n", "micro = Microstructure(filename=dataset_file)" ] }, { "cell_type": "code", "execution_count": null, "id": "31a2f7cf-feef-408e-9749-6676f78e172a", "metadata": {}, "outputs": [], "source": [ "# display content of dataset\n", "micro.print_dataset_content(max_depth=2, short=True)" ] }, { "cell_type": "markdown", "id": "97f984fd-324a-4215-9e8d-f4fcc703d3fc", "metadata": {}, "source": [ "The dataset contains a `PhaseData` group, that is used to store all phase data relative to the sample associated to the dataset. It has one `Group` children for each phase in the dataset, that stores all relevant information for this phase. \n", "\n", "Here there only one phase in the microstructure, whose data is stored into the `phase_01` group. The content of this group can be easily printed:" ] }, { "cell_type": "code", "execution_count": null, "id": "2e380ebd-91c4-4124-8e9b-e7902ae0b525", "metadata": {}, "outputs": [], "source": [ "micro.print_node_info('phase_01')" ] }, { "cell_type": "markdown", "id": "bb0f6ce0-c594-464a-90fc-3f768131ab9c", "metadata": {}, "source": [ "As shown by the print, this group contains only metadata (*node attributes*, see [here](./Data_Items.ipynb)) providing information on the phase crystallographic (symmetry, lattice parameters) and physical (elasticity) properties, but also some identification metadata (phase name, formula, Id number). \n", "\n", "The `CrystallinePhase` class of the `pymicro.crystal.lattice` module is the object that allows to manipulate interactively all the data stored into the `PhaseData` group. A `CrystallinePhase` can be directly retrieved from a phase description group with the `get_phase` method:" ] }, { "cell_type": "code", "execution_count": null, "id": "5f77fe02-810b-4090-8cd6-3e4d74777db0", "metadata": {}, "outputs": [], "source": [ "phase_01 = micro.get_phase(phase_id=1)\n", "print(phase_01)" ] }, { "cell_type": "markdown", "id": "c90e6368-f4f8-4436-b5ae-96c842d857eb", "metadata": {}, "source": [ "This object contains a `Lattice` object, defining the crystal lattice of the phase. It also stores the elastic constants of the phase. Currently, those are the only information that are handled by the `CrystallinePhase` object. As many attributes as desired can be stored in a `phase_XX` group, but only those will be loaded into the `CrystallinePhase` object with the `get_phase` method.\n", "\n", "The data associated to the `CrystallinePhase` class are used by other tools of the *Pymicro* package, related to X-ray diffration or mechanical behavior simulations. \n", "\n", "We will now see how to create *phase objects*, and store them into *microstructure datasets*." ] }, { "cell_type": "code", "execution_count": null, "id": "0ad48041-135b-47cd-adba-f952729d24fc", "metadata": {}, "outputs": [], "source": [ "# close the opened dataset and remove the phase object\n", "del phase_01\n", "del micro" ] }, { "cell_type": "markdown", "id": "b2e6f8c7-18ed-4544-8703-f01961c6f70f", "metadata": {}, "source": [ "## Phase objects" ] }, { "cell_type": "markdown", "id": "03239775-4d37-4e95-9414-f1e993688e89", "metadata": {}, "source": [ "As shown above, *Phase objects* are instances of the class `CrystallinePhase`. To create one, the class constructor can be used. Three arguments can be passed to the constructor:\n", "* a phase id number\n", "* a name for the phase\n", "* a crystal lattice object\n", "\n", "A *lattice object* must hence be created to be associated to the *phase object*." ] }, { "cell_type": "markdown", "id": "69f1d37d-6195-4caa-8d67-dd55380479d5", "metadata": {}, "source": [ "### Lattice objects" ] }, { "cell_type": "markdown", "id": "36db836f-1669-4392-8f10-727c66b1b7e9", "metadata": {}, "source": [ "The `Lattice` class of the `pymicro.crystal.lattice` module allows to create and manipulate *lattice objects*. A `Lattice` is defined by a by a symmetry group and three vectors. \n", "\n", "Most lattice systems encountered in real materials have a lot of symetries. In those cases, the complete description of the lattice vectors is redundant. Specific lattice constructors for each type of the 7 Bravais lattice systems are available in *Pymicro*, and allow to simplify the declaration of *lattice objects*.\n", "\n", "For instance, you can create a cubic lattice (completely defined by one lattice parameter) as follows: " ] }, { "cell_type": "code", "execution_count": null, "id": "e0447b9e-dcef-4ab2-9eb6-24148017f1a3", "metadata": {}, "outputs": [], "source": [ "from pymicro.crystal.lattice import Lattice\n", "\n", "a = 0.352 # lattice parameter for FCC Nickel (nm)\n", "l = Lattice.face_centered_cubic(a)\n", "print(l)" ] }, { "cell_type": "markdown", "id": "34fb98d3-7ed0-4cd7-9b4f-9586ddb1d0dd", "metadata": {}, "source": [ "
\n", "\n", "**Note** \n", " \n", "The unit for lattice parameters in *Pymicro* is the nanometer (nm).\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "cb7ffa70-df18-4e1e-afb4-934321102899", "metadata": {}, "source": [ "The class allows to get usefull properties of the crystal lattice, such as:\n", "* the director vectors of the lattice and the reciprocal lattice\n", "* the metric tensor and the volume of the lattice\n", "* crystallographic planes of the lattice\n", "* slip systems of the lattice" ] }, { "cell_type": "code", "execution_count": null, "id": "201ca8e9-bdf3-4cd8-8d11-0e01f4c7957f", "metadata": {}, "outputs": [], "source": [ "# get lattice directors : each column of the lattice matrix is a lattice director vector\n", "print(f' Crystal lattice directors. \\n - D1 : {l.matrix.round(decimals=3)[0,:]} '\n", " f'\\n - D2 : {l.matrix.round(decimals=3)[1,:]} '\n", " f'\\n - D3 : {l.matrix.round(decimals=3)[2,:]} \\n')\n", "\n", "# same for reciprocal lattice\n", "Rmat = l.reciprocal_lattice()\n", "print(f' Crystal reciprocal lattice directors. \\n - D1 : {Rmat[0].round(decimals=3)} '\n", " f'\\n - D2 : {Rmat[1].round(decimals=3)} '\n", " f'\\n - D3 : {Rmat[2].round(decimals=3)} \\n')\n", "\n", "# get lattice volume\n", "print(f'Volume of the crystal lattice: {l.volume()} \\n')\n", "\n", "# get lattice metric tensor\n", "g = l.metric_tensor()\n", "print(f'Lattice Metric tensor: \\n {g} \\n')\n", "\n", "\n", "# get 111 planes \n", "print(f'1,1,1 planes : \\n {l.get_hkl_family([1,1,1])} \\n')\n", "\n", "# slip systems \n", "slip_list = l.get_slip_systems(slip_type='111')\n", "print(f'List of octahedral slip systems: \\n {slip_list}')" ] }, { "cell_type": "markdown", "id": "188220e7-d8a3-41f6-bcda-f6b7a609269e", "metadata": {}, "source": [ "### Phase object creation" ] }, { "cell_type": "markdown", "id": "1693b135-72f1-4696-8bb3-8ae7eacff428", "metadata": {}, "source": [ "Now that the *lattice object* has been created, it is time to create a phase object, using the `CrystallinePhase` class constructor:" ] }, { "cell_type": "code", "execution_count": null, "id": "96f66e24-1c9d-4f54-b3fd-d47223df6f37", "metadata": {}, "outputs": [], "source": [ "# import CrystallinePhase class\n", "from pymicro.crystal.lattice import CrystallinePhase\n", "\n", "# create empty phase object \n", "phase = CrystallinePhase(phase_id=1)\n", "\n", "print(phase)" ] }, { "cell_type": "markdown", "id": "d626c453-1d37-44a0-ad57-c14f72aa751e", "metadata": {}, "source": [ "The default lattice set to created *Phase objects* is a cubic lattice with a unitary lattice parameter. To change that and set the *lattice* object that we created above to our phase, the `set_lattice` method must be used: " ] }, { "cell_type": "code", "execution_count": null, "id": "6a2d5537-4a49-4a25-b506-b1d5fb1684b1", "metadata": {}, "outputs": [], "source": [ "phase.set_lattice(l)\n", "\n", "print(phase)" ] }, { "cell_type": "markdown", "id": "632c6e64-13da-4895-8bb9-56ee886ce60c", "metadata": {}, "source": [ "The name of the phase, by default \"*unkown*\", can be easily set: " ] }, { "cell_type": "code", "execution_count": null, "id": "a3db3525-f723-443d-b168-35afd7f7bcc6", "metadata": {}, "outputs": [], "source": [ "phase.set_name('FCC Ni-16Cr')\n", "print(phase)" ] }, { "cell_type": "markdown", "id": "d3a49f62-5aa1-4721-ad3c-50fda02e8373", "metadata": {}, "source": [ "### Elastic constants" ] }, { "cell_type": "markdown", "id": "a8ff74a9-d3ed-47b0-9be9-1a97f9b32c8f", "metadata": {}, "source": [ "The definition of elastic constants in *Pymicro* complies with the classical expression of the generalized **Hooke's law** with **Voigt's notation**. In this framework, the elastic constants are the coefficients of the symmetric stiffness matrix $C$ which linearly links stress to strain: \n", "$$\n", " \\sigma = C \\cdot \\varepsilon \\qquad \\sigma_I = C_{IJ} \\cdot \\varepsilon_{J} \n", "$$\n", "\n", "The single indices used in **Voigt's notation**, that define the $C_{IJ}$ coefficients in second expression above, correspond to the following convention:\n", "$$\n", " \\sigma_I = [ \\sigma_{1}, \\sigma_{2}, \\sigma_{3}, \\sigma_{4}, \\sigma_{5}, \\sigma_{6}] = \\sigma_{ij} = [ \\sigma_{11}, \\sigma_{22}, \\sigma_{33}, \\sigma_{23}, \\sigma_{13}, \\sigma_{12}] \\\\\n", " \\varepsilon_J = [ \\varepsilon_{1}, \\varepsilon_{2}, \\varepsilon_{3}, 2 \\varepsilon_{4}, 2 \\varepsilon_{5}, 2 \\varepsilon_{6}] = \\varepsilon_{ij} = [\\varepsilon_{11}, \\varepsilon_{22}, \\varepsilon_{33}, 2 \\varepsilon_{23}, 2 \\varepsilon_{13}, 2 \\varepsilon_{12}]\n", "$$\n", "\n", "Depending on the symmetry of the crystal lattice, the number of independent coefficients of the matrix can vary from 3 (cubic symmetry) to 21 (triclinic symmetry). The `set_elastic_constants` method allows to set this constants for the phase, from the list of independent coefficients. The list of symmetries implemented in *Pymicro* with the associated list of coefficients to use as input is provided hereafter:\n", "\n", "* **cubic** : 3 independant constants $[C_{11}, C_{12}, C_{44}]$ \n", " $$\\begin{bmatrix} C_{11} & C_{12} & C_{12} & 0 & 0 & 0 \\\\ \n", " C_{12} & C_{11} & C_{12} & 0 & 0 & 0 \\\\ \n", " C_{12} & C_{12} & C_{11} & 0 & 0 & 0 \\\\ \n", " 0 & 0 & 0 & C_{44} & 0 & 0 \\\\ \n", " 0 & 0 & 0 & C_{44} & 0 & 0 \\\\ \n", " 0 & 0 & 0 & C_{44} & 0 & 0 \\end{bmatrix}$$\n", " \n", " \n", "* **hexagonal** : 5 independant constants $[C_{11}, C_{12}, C_{13}, C_{33}, C_{44}]$:\n", " $$\\begin{bmatrix} C_{11} & C_{12} & C_{13} & 0 & 0 & 0 \\\\ \n", " C_{12} & C_{11} & C_{13} & 0 & 0 & 0 \\\\ \n", " C_{13} & C_{13} & C_{33} & 0 & 0 & 0 \\\\ \n", " 0 & 0 & 0 & C_{44} & 0 & 0 \\\\ \n", " 0 & 0 & 0 & 0 & C_{44} & 0 \\\\ \n", " 0 & 0 & 0 & 0 & 0 & \\frac{C_{11} - C_{12}}{2} \\end{bmatrix}$$\n", " \n", " \n", "* **tetragonal** : 6 independant constants $[C_{11}, C_{12}, C_{13}, C_{33}, C_{44}, C_{66}]$:\n", " $$\\begin{bmatrix} C_{11} & C_{12} & C_{13} & 0 & 0 & 0 \\\\ \n", " C_{12} & C_{11} & C_{13} & 0 & 0 & 0 \\\\ \n", " C_{13} & C_{13} & C_{33} & 0 & 0 & 0 \\\\ \n", " 0 & 0 & 0 & C_{44} & 0 & 0 \\\\ \n", " 0 & 0 & 0 & 0 & C_{44} & 0 \\\\ \n", " 0 & 0 & 0 & 0 & 0 & C_{66} \\end{bmatrix}$$\n", " \n", " \n", "* **orthorhombic** : 9 independant constants $[C_{11}, C_{12}, C_{13}, C_{22}, C_{23}, C_{33}, C_{44}, C_{55}, C_{66}]$:\n", " $$\\begin{bmatrix} C_{11} & C_{12} & C_{13} & 0 & 0 & 0 \\\\ \n", " C_{12} & C_{22} & C_{23} & 0 & 0 & 0 \\\\ \n", " C_{13} & C_{23} & C_{33} & 0 & 0 & 0 \\\\ \n", " 0 & 0 & 0 & C_{44} & 0 & 0 \\\\ \n", " 0 & 0 & 0 & 0 & C_{55} & 0 \\\\ \n", " 0 & 0 & 0 & 0 & 0 & C_{66} \\end{bmatrix}$$\n", " \n", " \n", "* **monoclinic** : 13 independant constants $[C_{11}, C_{12}, C_{13}, C_{16}, C_{22}, C_{23}, C_{26}, C_{33}, C_{36}, C_{44}, C_{45}, C_{55}, C_{66}]$:\n", " $$\\begin{bmatrix} C_{11} & C_{12} & C_{13} & 0 & 0 & C_{16} \\\\ \n", " C_{12} & C_{22} & C_{23} & 0 & 0 & C_{26} \\\\ \n", " C_{13} & C_{23} & C_{33} & 0 & 0 & C_{36} \\\\ \n", " 0 & 0 & 0 & C_{44} & C_{45} & 0 \\\\ \n", " 0 & 0 & 0 & C_{45} & C_{55} & 0 \\\\ \n", " C_{16} & C_{26} & C_{36} & 0 & 0 & C_{66} \\end{bmatrix}$$\n", " \n", "* **triclinic** : all 21 constants $C_{IJ}$ " ] }, { "cell_type": "markdown", "id": "751d5ca9-91a4-429a-90d1-11c98731c828", "metadata": {}, "source": [ "
\n", "\n", "**Note** \n", " \n", "The unit for elastic constants in *Pymicro* is the mega Pascal (MPa).\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "54a6d7da-c0bd-4238-82e7-36ea79d675af", "metadata": {}, "source": [ "For the cubic phase created above, the three $[C_{11}, C_{12}, C_{44}]$ coefficients must be provided:" ] }, { "cell_type": "code", "execution_count": null, "id": "75b824f1-6a1f-40d4-8482-2e3a812a152a", "metadata": {}, "outputs": [], "source": [ "# create list to store C11, C12, C44 for Ni-16Cr (MPa):\n", "elastic_constants = [300000., 180000.,140000.]\n", "\n", "# set elastic constants:\n", "phase.set_elastic_constants(elastic_constants)\n", "\n", "print(phase)" ] }, { "cell_type": "markdown", "id": "09c9b00d-3bc6-49c5-95bd-1171fea9e3f7", "metadata": {}, "source": [ "Once set in the phase object, the 9 orthotropic constants ($E_1, E_2, E_3, \\nu_{12}, \\nu_{13}, \\nu_{23}, G_{12}, G_{13}, G_{23},$) or the stiffness matrix can be easily obtained: " ] }, { "cell_type": "code", "execution_count": null, "id": "156a8115-ea59-461c-996a-260c237eb050", "metadata": {}, "outputs": [], "source": [ "# get the orthotropic constants\n", "print(f\"The orthotropic elastic constants of the phase {phase.name} are : \\n {phase.orthotropic_constants()}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "3dea7127-073f-4b53-99ad-455061fee5dc", "metadata": {}, "outputs": [], "source": [ "# get the stiffness matrix \n", "print(f\"The complete stiffness matrix of the phase {phase.name} is : \\n {phase.stiffness_matrix()}\")" ] }, { "cell_type": "markdown", "id": "eea5bcac-f804-4dab-a24e-6a793e319c9b", "metadata": {}, "source": [ "## Include Phase data in datasets" ] }, { "cell_type": "markdown", "id": "b4793b0c-2a33-4364-868f-e53880b59455", "metadata": {}, "source": [ "The final step of this tutorial will show how to add a phase object into a polycrystalline dataset, with the `Microstructure` class. The first way to do it is to use the `add_phase` method:" ] }, { "cell_type": "code", "execution_count": null, "id": "a15fed44-4357-470e-8293-ae601b23c0a4", "metadata": {}, "outputs": [], "source": [ "# create microstructure \n", "micro = Microstructure(filename='micro_test', autodelete=True)\n", "# print content of phase data group\n", "micro.print_node_info('PhaseData')\n", "\n", "# add new phase\n", "micro.add_phase(phase)\n", "# print content of phase data group\n", "micro.print_group_content('PhaseData')" ] }, { "cell_type": "markdown", "id": "8a12c28f-a70f-449a-9b09-db55cb1f519d", "metadata": {}, "source": [ "As shown above, the microstructure is created by default with an *unknown* phase that has a cubic structure with a default lattice parameter of 1 nm, and no elastic constants. The `add_phase` method creates a new phase in the dataset, and attributes to it the next available *phase id number*, which is why the dataset has now two phases. \n", "\n", "To change the definition of an already existing phase in the dataset, use the `set_phase` method:" ] }, { "cell_type": "code", "execution_count": null, "id": "65ae0967-999f-4732-8b72-3ee92d5042ad", "metadata": {}, "outputs": [], "source": [ "# create a new phase \n", "phase_2 = CrystallinePhase(phase_id=1, name='Hcp Ti', lattice=Lattice.hexagonal(a=0.295, c=0.468))\n", "\n", "# set first dataset phase\n", "micro.set_phase(phase_2, id_number=1)\n", "\n", "# print content of phase data group\n", "micro.print_group_content('PhaseData')" ] }, { "cell_type": "code", "execution_count": null, "id": "c6328ad2-f960-4a34-b99d-6a21b572cdc2", "metadata": {}, "outputs": [], "source": [ "# close microstructure dataset\n", "del micro" ] }, { "cell_type": "markdown", "id": "0c660322-d5a6-4fcb-ad6b-e65028cf0719", "metadata": {}, "source": [ "The `Microstructure` class constructor allows to pass a list of phases as argument, to initialize the dataset with the desired phase data in it:" ] }, { "cell_type": "code", "execution_count": null, "id": "8feb59aa-ef67-4a22-8bd0-66d1e6cad805", "metadata": {}, "outputs": [], "source": [ "# create microstructure with 2 phase objects \n", "micro = Microstructure(filename='micro_test', autodelete=True, phase=[phase, phase_2])\n", "\n", "# print content of phase data group\n", "micro.print_group_content('PhaseData')\n", "\n", "# close microstructure dataset\n", "del micro" ] }, { "cell_type": "markdown", "id": "fabca93c-317a-4aaa-99c7-65a9ebce299c", "metadata": {}, "source": [ "With this method, the order of the phases in the created datasets is determined by the order of the phase objects in the list passed to the class constructor. \n", "\n", "This concludes the tutorial on Pymicro's material **phase objects**." ] }, { "cell_type": "code", "execution_count": null, "id": "fe67ca0b-7c5c-4716-99d4-9eda779388c8", "metadata": {}, "outputs": [], "source": [ "os.remove(dataset_file+'.h5')\n", "os.remove(dataset_file+'.xdmf')" ] } ], "metadata": { "kernelspec": { "display_name": "env_dev", "language": "python", "name": "env_dev" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.17" } }, "nbformat": 4, "nbformat_minor": 5 }