diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml new file mode 100644 index 00000000..76310246 --- /dev/null +++ b/.github/workflows/draft-pdf.yml @@ -0,0 +1,23 @@ +on: [push] + +jobs: + paper: + runs-on: ubuntu-latest + name: Paper Draft + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Build draft PDF + uses: openjournals/openjournals-draft-action@master + with: + journal: joss + # This should be the path to the paper within your repo. + paper-path: paper/paper.md + - name: Upload + uses: actions/upload-artifact@v1 + with: + name: paper + # This is the output path where Pandoc will write the compiled + # PDF. Note, this should be the same directory as the input + # paper.md + path: paper/paper.pdf diff --git a/.gitignore b/.gitignore index e8073823..27f88c06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.gitignore +venv/ .idea # Meson files diff --git a/AUTHORS b/AUTHORS index d60c442b..8b7b366d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,15 +5,19 @@ James Brixey (Warwick U.) James Brixey (Warwick U.) Adrien Gola (KIT) Adrien Gola (KIT) +Adrien Gola (KIT) Jacek Golebiowski (Imperial College London) Jacek Golebiowski (Imperial College London) +Jacek Golebiowski (Imperial College London) Jan Griesser (U. Freiburg) Jan Griesser (U. Freiburg) Jan Griesser (U. Freiburg) Jan Griesser (U. Freiburg) Jan Griesser (U. Freiburg) Jan Griesser (U. Freiburg) +Jan Griesser (U. Freibirg) Petr Grigorev (Warwick U.) +Jan Griesser (U. Freiburg) Johannes Hoermann (U. Freiburg) Richard Jana (KIT & U. Freiburg) Till Junge (EPFL) @@ -31,3 +35,4 @@ Punit Patel (Warwick U.) Thomas Reichenbach (Fraunhofer IWM) Lucas Frérot (U. Freiburg) Lucas Frérot (U. Freiburg) +Zhilin Zheng <1091749869@qq.com> diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..5d4c2311 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +generated/* +*.cfg diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 00000000..b6534365 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,39 @@ +API Reference +============= + +Application domains API +----------------------- + +.. autosummary:: + :toctree: generated + :recursive: + + matscipy.elasticity + matscipy.dislocation + matscipy.fracture_mechanics + + +Calculators +----------- + +.. autosummary:: + :toctree: generated + :recursive: + + matscipy.calculators.calculator + matscipy.calculators.pair_potential.PairPotential + matscipy.calculators.polydisperse.Polydisperse + matscipy.calculators.mcfm.MultiClusterForceMixingPotential + matscipy.calculators.eam.EAM + matscipy.calculators.ewald.Ewald + matscipy.calculators.manybody.Manybody + +Analysis tools +-------------- + +.. autosummary:: + :toctree: generated + :recursive: + + matscipy.neighbours + matscipy.numerical diff --git a/docs/api/pressurecoupling.rst b/docs/api/pressurecoupling.rst new file mode 100644 index 00000000..8f63a779 --- /dev/null +++ b/docs/api/pressurecoupling.rst @@ -0,0 +1,4 @@ +Pressure coupling +---------- + +.. automodule:: matscipy.pressurecoupling diff --git a/docs/api_reference.rst b/docs/api_reference.rst deleted file mode 100644 index 8cc1358c..00000000 --- a/docs/api_reference.rst +++ /dev/null @@ -1,11 +0,0 @@ -API Reference -============= - -.. toctree:: - ./api/root - ./api/calculators - ./api/elasticity - ./api/neighbours - ./api/numerical - ./api/fracture_mechanics - ./api/dislocation diff --git a/docs/applications/elastic_constants.ipynb b/docs/applications/elastic_constants.ipynb new file mode 100644 index 00000000..ea4d36b9 --- /dev/null +++ b/docs/applications/elastic_constants.ipynb @@ -0,0 +1,368 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1aad7bd0-ad93-4037-90be-39a90e0609fc", + "metadata": {}, + "source": [ + "# Elastic Constants\n", + "\n", + "Solids respond to small external loads through a reversible elastic response. The strength of the response is characterized by the elastic moduli. {py:mod}`matscipy.elasticity` implements functions for computing elastic moduli from small deformation of atomistic systems that consider potential symmetries of the underlying atomic system, in particular for crystals. {py:mod}`matscipy.elasticity` also implements analytic calculation of elastic moduli for some interatomic potentials, described in more detail below. The computation of elastic moduli is a basic prerequisite for multi-scale modelling of materials, as they are the most basic parameters of continuum material models.\n", + "\n", + "In this tutorial, we will go over different ways that `matscipy` can compute elastic constants of an atomic configuration.\n", + "\n", + "## Problem setup\n", + "\n", + "Let's first create an FCC system and view it interactively:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7edef8b-a391-44fb-8591-2e80db68f9fc", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.lattice.cubic import FaceCenteredCubic\n", + "\n", + "def interactive_view(system):\n", + " from ase.visualize import view\n", + " # Interactive view of the lattice\n", + " v = view(system, viewer='ngl')\n", + "\n", + " # Resize widget\n", + " v.view._remote_call(\"setSize\", target=\"Widget\", args=[\"300px\", \"300px\"])\n", + " v.view.center()\n", + " return v\n", + "\n", + "# Define FCC crystal\n", + "system = FaceCenteredCubic(directions=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], size=(3,3,3), symbol='Cu', pbc=(1,1,1))\n", + "interactive_view(system)" + ] + }, + { + "cell_type": "markdown", + "id": "5eb45b2e-ec03-4695-bc4b-3ce832c61964", + "metadata": {}, + "source": [ + "We'll assign a force-field to this system based on the Lennard-Jones potential ({py:class}`LennardJonesCut `) implemented in ``matscipy``." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fb38057-8e69-479b-bb17-d5060d05e05c", + "metadata": {}, + "outputs": [], + "source": [ + "from matscipy.calculators.pair_potential import PairPotential, LennardJonesCut\n", + "from ase.data import reference_states, atomic_numbers\n", + "\n", + "import numpy as np\n", + "\n", + "Cu_num = atomic_numbers[\"Cu\"]\n", + "lattice = reference_states[Cu_num][\"a\"]\n", + "sigma = lattice / (2**(2/3))\n", + "\n", + "system.calc = PairPotential({\n", + " # Type map: define Copper-Copper pair interaction\n", + " (Cu_num, Cu_num): LennardJonesCut(epsilon=1, sigma=lattice * 2**(-1/6) / np.sqrt(2), cutoff=3 * lattice)\n", + "})" + ] + }, + { + "cell_type": "markdown", + "id": "d95c366a-9180-426e-944a-1d4745e0f827", + "metadata": {}, + "source": [ + "We test we have a sensible potential energy and that we have equilibrium." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9bc1a2c-10a5-49bf-985f-d98d54e77bca", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy.testing import assert_allclose\n", + "from IPython.display import display, Latex\n", + "\n", + "display(Latex(f\"$E_\\\\mathrm{{pot}} = {system.get_potential_energy():.1f}$\"))\n", + "\n", + "# Testing negative potential energy\n", + "assert system.get_potential_energy() < 0\n", + "\n", + "# Testing equilibrium\n", + "assert_allclose(system.get_forces(), 0, atol=3e-14)" + ] + }, + { + "cell_type": "markdown", + "id": "3a3405e6-4efc-49a3-a453-d3d01b73f5a9", + "metadata": {}, + "source": [ + "## Crystalline elastic constants\n", + "\n", + "Let us first compute elastic constants with the {py:mod}`matscipy.elasticity` module, with two different functions:\n", + "\n", + "- {py:func}`measure_triclinic_elastic_constants ` computes the full Hooke's tensor by finite differences\n", + "- {py:func}`fit_elastic_constants ` computes a range of deformed configurations and fits a Hooke's tensor with least-squares" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b96c21b-fe28-4e93-8482-58ec73092b28", + "metadata": {}, + "outputs": [], + "source": [ + "from matscipy.elasticity import measure_triclinic_elastic_constants, fit_elastic_constants\n", + "from matscipy.elasticity import full_3x3x3x3_to_Voigt_6x6\n", + "\n", + "C_finite_differences = full_3x3x3x3_to_Voigt_6x6(measure_triclinic_elastic_constants(system))\n", + "C_least_squares, _ = fit_elastic_constants(system, verbose=False)" + ] + }, + { + "cell_type": "markdown", + "id": "12f6c608-7ca3-4782-b054-a18b399c4a8b", + "metadata": {}, + "source": [ + "Let's plot the Hooke tensor:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b2f88bb-e86e-4cb3-a356-985a654b4f6a", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "def spy_constants(ax, constants):\n", + " ax.imshow(constants, cmap='RdPu', interpolation='none')\n", + " labels = np.full_like(constants, \"\", dtype=object)\n", + " labels[:3, :3] = \"$\\\\lambda$\\n\"\n", + " labels[(np.arange(3), np.arange(3))] = \"$\\\\lambda + 2\\\\mu$\\n\"\n", + " labels[(np.arange(3, 6), np.arange(3, 6))] = \"$\\\\mu$\\n\"\n", + "\n", + " max_C = constants.max()\n", + " \n", + " for i in range(constants.shape[0]):\n", + " for j in range(constants.shape[1]):\n", + " color = \"white\" if constants[i, j] / max_C > 0.7 else \"black\"\n", + " numeric = f\"${constants[i, j]:.2f}$\" if np.abs(constants[i, j]) / max_C > 1e-3 else \"$\\\\sim 0$\"\n", + "\n", + " ax.annotate(labels[i, j] + numeric,\n", + " (i, j),\n", + " horizontalalignment='center',\n", + " verticalalignment='center', color=color)\n", + " \n", + " ax.set_xticks(np.arange(constants.shape[1]))\n", + " ax.set_yticks(np.arange(constants.shape[0]))\n", + " ax.set_xticklabels([f\"C$_{{i{j+1}}}$\" for j in range(constants.shape[1])])\n", + " ax.set_yticklabels([f\"C$_{{{i+1}j}}$\" for i in range(constants.shape[0])])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0cfb76fb-ee48-4b23-96df-4ad78bc85df2", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(1, 2, figsize=(9, 5))\n", + "\n", + "spy_constants(axs[0], C_finite_differences)\n", + "spy_constants(axs[1], C_least_squares)\n", + "\n", + "axs[0].set_title(f\"Finite differences\")\n", + "axs[1].set_title(f\"Least squares\")\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "11599564-f976-4813-8ebd-408792c9c6df", + "metadata": {}, + "source": [ + "### With second-order derivatives\n", + "\n", + "Most calculators in ``matscipy`` actually implement second-order derivatives, and can therefore directly compute elastic constants:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db51ca03-b229-463d-b6b8-43c19548928d", + "metadata": {}, + "outputs": [], + "source": [ + "C = full_3x3x3x3_to_Voigt_6x6(system.calc.get_property(\"elastic_constants\", system))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c664eedf-8890-4960-8a59-12a429223eff", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(1, 2, figsize=(9, 5))\n", + "\n", + "spy_constants(axs[0], C_least_squares)\n", + "spy_constants(axs[1], C)\n", + "\n", + "axs[0].set_title(f\"Least squares\")\n", + "axs[1].set_title(f\"Direct evaluation\")\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "81338c89-9e2b-4146-a2f1-36118a2e5af7", + "metadata": {}, + "source": [ + "## Amorphous elastic constants\n", + "\n", + "In amorphous solids, non-affine relaxation modes play an important role in elastic deformation.\n", + "\n", + "### Problem setup\n", + "\n", + "Let's randomize our atoms to mimic a glassy structure:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5643f2cb-c81d-4d7a-bd48-2555492bd101", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.io import read\n", + "from pathlib import Path\n", + "\n", + "data_path = Path(\"..\") / \"..\" / \"tests\"\n", + "\n", + "system = read(data_path / \"CuZr_glass_460_atoms.gz\")\n", + "interactive_view(system)" + ] + }, + { + "cell_type": "markdown", + "id": "872bbcc8-be7a-4433-8d3d-573b45a37b5d", + "metadata": {}, + "source": [ + "Setting up the calculator:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b683cc4d-832c-4679-a74d-ae891a873f79", + "metadata": {}, + "outputs": [], + "source": [ + "from matscipy.calculators.eam import EAM\n", + "\n", + "system.calc = EAM(data_path / 'ZrCu.onecolumn.eam.alloy')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "213de06c-b987-41e9-af09-11998076bc37", + "metadata": {}, + "outputs": [], + "source": [ + "display(Latex(f\"$E_\\\\mathrm{{pot}} = {system.get_potential_energy():.1f}$\"))" + ] + }, + { + "cell_type": "markdown", + "id": "fab46693-0ab7-4fb6-acff-3ee602236587", + "metadata": {}, + "source": [ + "### Fitting elastic constants\n", + "\n", + "The {py:func}`fit_elastic_constants ` function accepts a minimizer procedure as argument to account for non-affine relaxation modes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5019537-e39d-4ee8-be92-22336b046c11", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.optimize import FIRE\n", + "\n", + "delta = 1e-4 # Configuration change increment\n", + "\n", + "C_affine, _ = fit_elastic_constants(system, verbose=False, delta=delta)\n", + "C_relaxed, _ = fit_elastic_constants(system, verbose=False, delta=delta,\n", + " # adjust fmax to desired precision\n", + " optimizer=FIRE, fmax=5 * delta)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56b55c89-980b-4264-8e55-188e4aa60259", + "metadata": {}, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(1, 2, figsize=(9, 5))\n", + "\n", + "spy_constants(axs[0], C_affine)\n", + "spy_constants(axs[1], C_relaxed)\n", + "\n", + "axs[0].set_title(f\"Affine only\")\n", + "axs[1].set_title(f\"Affine + Non-affine\")\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "56b085ef-06ab-464f-8eaf-bceba08fd3ba", + "metadata": {}, + "source": [ + "One can see that elastic constants are significantly reduced when the internal relaxation is included. However, mind that the reduction is *very* dependent on the optimizer's stopping criterion ``fmax``, which should ideally be lower than the deformation increment (we set it higher in the example above for demonstration purposes only)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12744ced-a9e2-4adb-aa5f-748ed6cc22b5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/applications/fracture_mechanics.rst b/docs/applications/fracture_mechanics.rst new file mode 100644 index 00000000..93870457 --- /dev/null +++ b/docs/applications/fracture_mechanics.rst @@ -0,0 +1,24 @@ +Fracture Mechanics +================== + +Cracking is the process of generating new surface area by splitting the material +apart. The module :mod:`matscipy.fracture_mechanics` provides functionality for +calculating continuum linear elastic displacement fields near crack tips, +including support for anisotropy in the elastic response. + +The module also implements generation of atomic structures that are deformed +according to this near-tip field. This functionality has been used to quantify +lattice trapping, which is the pinning of cracks due to the discreteness of the +atomic lattice, and to compare simulations with experimental measurements of +crack speeds in silicon. An example of this is provided in the +quasi-static fracture tutorial linked below. + +Finally, there is support for flexible boundary conditions in fracture simulations +using the formalism proposed by Sinclair, where the finite atomistic domain is +coupled to an infinite elastic continuum. We also provide an extension of this +approach to give a flexible boundary scheme that uses numerical continuation to +obtain full solution paths for cracks. + +.. toctree:: + + quasi_static_crack \ No newline at end of file diff --git a/docs/applications/index.rst b/docs/applications/index.rst new file mode 100644 index 00000000..0a4c1f30 --- /dev/null +++ b/docs/applications/index.rst @@ -0,0 +1,11 @@ +Application domains +=================== + +`matscipy` contains general utility functionality which is widely applicable. + +.. toctree:: + + elastic_constants + plasticity + fracture_mechanics + tribology diff --git a/docs/plasticity.rst b/docs/applications/plasticity.rst similarity index 100% rename from docs/plasticity.rst rename to docs/applications/plasticity.rst diff --git a/docs/applications/quasi_static_crack.ipynb b/docs/applications/quasi_static_crack.ipynb new file mode 100644 index 00000000..2205aadd --- /dev/null +++ b/docs/applications/quasi_static_crack.ipynb @@ -0,0 +1,595 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Quasi static fracture simulations\n", + "\n", + "## Basic usage\n", + "\n", + "The `matscipy.fracture_mechanics.crack` module provides classes and functions for setting up and analysing atomistic simulations of fracture." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "import ase.io\n", + "import ase.units as units\n", + "from ase.build import bulk\n", + "from ase.constraints import ExpCellFilter\n", + "from ase.optimize.precon import PreconLBFGS\n", + "\n", + "from nglview import show_ase\n", + "\n", + "from matscipy.fracture_mechanics.crack import CubicCrystalCrack\n", + "from matscipy.fracture_mechanics.clusters import diamond, set_groups\n", + "from matscipy.calculators.manybody.explicit_forms.stillinger_weber import StillingerWeber, Holland_Marder_PRL_80_746_Si\n", + "from matscipy.calculators.manybody import Manybody\n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We first setup a manybody calculator using the \"inadvertendly modified Stillinger Weber\" (IMSW) parameters, in which the strength of the 3-body potential was (initially accidentally) doubled. This is useful for us here as it gives a brittle fracture response." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "calc = Manybody(**StillingerWeber(Holland_Marder_PRL_80_746_Si))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bulk and Elastic properties\n", + "\n", + "We can use this calculator to find the equilibrium lattice constant for bulk silicon by performing a variable cell relaxation using [ExpCellFilter](https://wiki.fysik.dtu.dk/ase/ase/constraints.html#the-expcellfilter-class) filter and [PreconLBFGS](https://wiki.fysik.dtu.dk/ase/ase/optimize.html#preconditioned-optimizers) optimizer from ASE. Since the cell is very small the preconditioning does not speed up the calculation; however, it is still useful as this is one of the most robust optimizers and it converges quickly. It is not necessary to use a cubic cell, we do it simply to make the calcualation of the cubic lattice constant `alat` straight-forward." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PreconLBFGS: 0 13:56:05 -34.692786 0.0000 0.0003\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/u1470235/gits/matscipy/venv/lib/python3.10/site-packages/ase/optimize/precon/lbfgs.py:133: UserWarning: The system is likely too small to benefit from the standard preconditioner, hence it is disabled. To re-enable preconditioning, call `PreconLBFGS` by explicitly providing the kwarg `precon`\n", + " warnings.warn('The system is likely too small to benefit from '\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PreconLBFGS: 1 13:56:06 -34.692800 0.0000 0.0000\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "el = 'Si' # chemical element\n", + "si = bulk(el, cubic=True)\n", + "si.calc = calc\n", + "ecf = ExpCellFilter(si)\n", + "opt = PreconLBFGS(ecf)\n", + "opt.run(fmax=1e-6, smax=1e-6)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "a0 = si.cell[0, 0]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we compute the elastic constants using `matscipy.elasticity.fit_elastic_constants()` assuming cubic symmetry to reduce the number of calculations required." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fitting C_11\n", + "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n", + "Stress array([-4.18433656e+00, -2.05309496e+00, 1.32979590e-05, 1.97563406e+00,\n", + " 3.87437477e+00]) GPa\n", + "Cij (gradient) / GPa : 201.4615167357166\n", + "Error in Cij / GPa : 2.647086489317546\n", + "Correlation coefficient : 0.9997411342093212\n", + "Setting C11 (1) to 1.257424 +/- 0.016522\n", + "\n", + "\n", + "Fitting C_21\n", + "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n", + "Stress array([-1.13682864e+00, -5.40632635e-01, 1.32979590e-05, 4.89079644e-01,\n", + " 9.30322868e-01]) GPa\n", + "Cij (gradient) / GPa : 51.640152889484874\n", + "Error in Cij / GPa : 1.7644310200757045\n", + "Correlation coefficient : 0.9982534274183545\n", + "Setting C21 (7) to 0.322312 +/- 0.011013\n", + "\n", + "\n", + "Fitting C_31\n", + "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n", + "Stress array([-1.13682864e+00, -5.40632635e-01, 1.32979590e-05, 4.89079644e-01,\n", + " 9.30322868e-01]) GPa\n", + "Cij (gradient) / GPa : 51.64015288948469\n", + "Error in Cij / GPa : 1.764431020075586\n", + "Correlation coefficient : 0.9982534274183548\n", + "Updating C31 (7) with value 0.322312 +/- 0.011013\n", + "\n", + "\n", + "Fitting C_44\n", + "Strain array([-0.02, -0.01, 0. , 0.01, 0.02])\n", + "Stress array([-2.43911515e+00, -1.19994966e+00, -1.05128340e-23, 1.16236289e+00,\n", + " 2.28858390e+00]) GPa\n", + "Cij (gradient) / GPa : 118.17710649364383\n", + "Error in Cij / GPa : 1.285752508674578\n", + "Correlation coefficient : 0.9998224896560226\n", + "Setting C44 (4) to 0.737603 +/- 0.008025\n", + "\n", + "\n", + "[[b C11 b C12 b C12 b b b ]\n", + " [b C12 b C11 b C12 b b b ]\n", + " [b C12 b C12 b C11 b b b ]\n", + " [b b b b C44 b b ]\n", + " [b b b b b C44 b ]\n", + " [b b b b b b C44]]\n", + "\n", + " = \n", + "\n", + "[[201.46 51.64 51.64 0. 0. 0. ]\n", + " [ 51.64 201.46 51.64 0. 0. 0. ]\n", + " [ 51.64 51.64 201.46 0. 0. 0. ]\n", + " [ 0. 0. 0. 118.18 0. 0. ]\n", + " [ 0. 0. 0. 0. 118.18 0. ]\n", + " [ 0. 0. 0. 0. 0. 118.18]]\n", + "C_11 = 201.46 +/- 2.65 GPa\n", + "C_12 = 51.64 +/- 1.76 GPa\n", + "C_44 = 118.18 +/- 1.29 GPa\n" + ] + } + ], + "source": [ + "from matscipy.elasticity import fit_elastic_constants\n", + "\n", + "si = bulk(el, a=a0)\n", + "si.calc = calc\n", + "C, C_err = fit_elastic_constants(si, symmetry='cubic')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "C_11 = C[0, 0] / units.GPa\n", + "C_12 = C[0, 1] / units.GPa\n", + "C_44 = C[3, 3] / units.GPa" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We extract the cubic elastic constants $C_{11}$, $C_{12}$ and $C_{44}$ and convert to units of GPa:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(201.4615167357166, 51.64015288948479, 118.17710649364383)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(C_11, C_12, C_44)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The remaining ingredient is the surface energy, which we can also compute on-the-fly" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# UPDATE to import from here once `adapticecont` branch is merged.\n", + "# from matscipy.fracture_mechanics.clusters import find_surface_energy\n", + "\n", + "def find_surface_energy(symbol,calc,a0,surface,size=(8,1,1),vacuum=10,fmax=0.0001,unit='0.1J/m^2'):\n", + " # Import required lattice builder\n", + " if surface.startswith('bcc'):\n", + " from ase.lattice.cubic import BodyCenteredCubic as lattice_builder\n", + " elif surface.startswith('fcc'):\n", + " from ase.lattice.cubic import FaceCenteredCubic as lattice_builder #untested\n", + " elif surface.startswith('diamond'):\n", + " from ase.lattice.cubic import Diamond as lattice_builder #untested\n", + " ## Append other lattice builders here\n", + " else:\n", + " print('Error: Unsupported lattice ordering.')\n", + "\n", + " # Set orthogonal directions for cell axes\n", + " if surface.endswith('100'):\n", + " directions=[[1,0,0], [0,1,0], [0,0,1]] #tested for bcc\n", + " elif surface.endswith('110'):\n", + " directions=[[1,1,0], [-1,1,0], [0,0,1]] #tested for bcc\n", + " elif surface.endswith('111'):\n", + " directions=[[1,1,1], [-2,1,1],[0,-1,1]] #tested for bcc\n", + " ## Append other cell axis options here\n", + " else:\n", + " print('Error: Unsupported surface orientation.')\n", + " \n", + " # Make bulk and slab with same number of atoms (size)\n", + " bulk = lattice_builder(directions=directions, size=size, symbol=symbol, latticeconstant=a0, pbc=(1,1,1))\n", + " cell = bulk.get_cell() ; cell[0,:] *=2 # vacuum along x axis (surface normal)\n", + " slab = bulk.copy() ; slab.set_cell(cell)\n", + " \n", + " # Optimize the geometries\n", + " from ase.optimize import LBFGSLineSearch\n", + " bulk.calc = calc ; opt_bulk = LBFGSLineSearch(bulk) ; opt_bulk.run(fmax=fmax)\n", + " slab.calc = calc ; opt_slab = LBFGSLineSearch(slab) ; opt_slab.run(fmax=fmax)\n", + "\n", + " # Find surface energy\n", + " import numpy as np\n", + " Ebulk = bulk.get_potential_energy() ; Eslab = slab.get_potential_energy()\n", + " area = np.linalg.norm(np.cross(slab.get_cell()[1,:],slab.get_cell()[2,:]))\n", + " gamma_ase = (Eslab - Ebulk)/(2*area)\n", + "\n", + " # Convert to required units\n", + " if unit == 'ASE':\n", + " return [gamma_ase,'ase_units']\n", + " else:\n", + " from ase import units\n", + " gamma_SI = (gamma_ase / units.J ) * (units.m)**2\n", + " if unit =='J/m^2':\n", + " return [gamma_SI,'J/m^2']\n", + " elif unit == '0.1J/m^2':\n", + " return [10*gamma_SI,'0.1J/m^2'] # units required for the fracture code\n", + " else:\n", + " print('Error: Unsupported unit of surface energy.')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Step Time Energy fmax\n", + "*Force-consistent energies used in optimization.\n", + "LBFGSLineSearch: 0 13:56:12 -416.313600* 0.0000\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Step Time Energy fmax\n", + "*Force-consistent energies used in optimization.\n", + "LBFGSLineSearch: 0 13:56:12 -403.303800* 0.0000\n" + ] + } + ], + "source": [ + "surface_energy, unit = find_surface_energy(el, calc, a0, 'diamond111')" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Build crack system\n", + "\n", + "We now have all the material parameters needed and can proceed to define the crack system. Usually the contents of the cell below (together with the code above to compute the elastic constants and surface energy) would be included in a file named `params.py` which would then be imported by the scripts in `matscipy/fracture_mechanics/scripts`, e.g. the `quasistatic_crack.py` script on which this tutorial is based." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "skin_x = 1*a0, skin_y = 1*a0\n", + "skin_x = 6.651528178589903, skin_y = 9.406681360668651\n" + ] + } + ], + "source": [ + "n = [ 4, 4, 1 ]\n", + "crack_surface = [ 1, 1, 1 ]\n", + "crack_front = [ 1, -1, 0 ]\n", + "crack_tip = [ 41, 56 ]\n", + "skin_x, skin_y = 1, 1\n", + "\n", + "vacuum = 6.0\n", + "fmax = 0.05\n", + "\n", + "# Setup crack system\n", + "cryst = diamond(el, a0, n, crack_surface, crack_front)\n", + "set_groups(cryst, n, skin_x, skin_y)\n", + "\n", + "ase.io.write('cryst.cfg', cryst)\n", + "\n", + "# Compute crack tip position\n", + "r0 = np.sum(cryst.get_positions()[crack_tip,:], axis=0)/len(crack_tip)\n", + "tip_x0, tip_y0, tip_z0 = r0" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now visualise the crystall unit cell:" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f21bcb0a6b514f489aede7d3407ba0e5", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "NGLWidget()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "show_ase(cryst)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We define a `CubicCrystalCrack` object to represent our crack system. This takes the crystal orientations and the elastic constants (either as three cubic constant as we do here, or as a full 6x6 stiffness matrix in either the crystal reference frame or rotated to the specimen frame)." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [], + "source": [ + "crk = CubicCrystalCrack(crack_surface, crack_front,\n", + " C_11, C_12, C_44)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By providing the surface energy, the `crk` object can be used to compute the Griffith estimate of the critical stress intensity factor at which fracture becomes thermodynamically favourable." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "70.85631549727216" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "k1g = crk.k1g(surface_energy)\n", + "k1g" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Apply initial strain field\n", + "\n", + "We have everything we need to apply the continuum linear elastic displacement field to our crystal. We centred the field on the middle of the `cryst` structure." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3d359a17854c4686b654ca50eee7c777", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "NGLWidget()" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tip_x = cryst.cell.diagonal()[0]/2\n", + "tip_y = cryst.cell.diagonal()[1]/2\n", + "\n", + "crack = cryst.copy()\n", + "crack.set_pbc([False, False, True])\n", + "\n", + "k1 = 1.0\n", + "\n", + "ux, uy = crk.displacements(cryst.positions[:,0], cryst.positions[:,1],\n", + " tip_x, tip_y, k1*k1g)\n", + "crack.positions[:, 0] += ux\n", + "crack.positions[:, 1] += uy\n", + "\n", + "# Center notched configuration in simulation cell and ensure enough vacuum.\n", + "oldr = crack[0].position.copy()\n", + "crack.center(vacuum=vacuum, axis=0)\n", + "crack.center(vacuum=vacuum, axis=1)\n", + "tip_x += crack[0].x - oldr[0]\n", + "tip_y += crack[0].y - oldr[1]\n", + "\n", + "show_ase(crack)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "fig, (ax1, ax2) = plt.subplots(1, 2)\n", + "ax1.scatter(cryst.positions[:,0], cryst.positions[:,1], s=50, c=cryst.arrays['groups'])\n", + "ax1.quiver(cryst.positions[:,0], cryst.positions[:,1], ux, uy)\n", + "ax2.scatter(cryst.positions[:,0] + ux, cryst.positions[:,1] + uy, s=50, c=cryst.arrays['groups'])\n", + "\n", + "ax1.set_title('Crystal + displacement field')\n", + "ax2.set_title('Crack')\n", + "\n", + "for ax in [ax1, ax2]:\n", + " ax.set_aspect('equal')\n", + " # ax.set_xticks([])\n", + " # ax.set_yticks([])\n", + " ax.set_xlim(-5, 30)\n", + " ax.set_ylim(-5, 45) \n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.10.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/applications/tribology.ipynb b/docs/applications/tribology.ipynb new file mode 100644 index 00000000..04b0b2cd --- /dev/null +++ b/docs/applications/tribology.ipynb @@ -0,0 +1,582 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1aad7bd0-ad93-4037-90be-39a90e0609fc", + "metadata": {}, + "source": [ + "# Tribology\n", + "\n", + "Tribology is the study of two interfaces sliding relative to one another, as encountered in frictional sliding or adhesion. Molecular dynamics simulations of representative volume elements of tribological interfaces are routinely used to gain\n", + "insights into the atomistic mechanisms underlying friction and wear. The module {py:mod}`matscipy.pressurecoupling` provides tools to perform such simulations under a constant normal load and sliding velocity.\n", + "\n", + "The example below shows how to perform an initial fast pressure equilibration of an interface prior to sliding.\n", + "Afterwards, during sliding, we apply the pressure coupling by [Pastewka et al.](https://doi.org/10.1007/s11249-009-9566-8) to dynamically adjust the distance between the two surfaces according to the local pressure. The algorithm ensures\n", + "mechanical boundary conditions that account for the inertia of the bulk material which\n", + "is not explicitly included in the simulation.\n", + "\n", + "\n", + "## System setup\n", + "\n", + "Let's first create an exemplary sliding interface consisting of two silicon crystals:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7edef8b-a391-44fb-8591-2e80db68f9fc", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.lattice.cubic import Diamond\n", + "\n", + "# create two slabs\n", + "slab1 = Diamond(directions=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], symbol='Si',\n", + " pbc=(1, 1, 1), latticeconstant=5.431, size=(2, 2, 2))\n", + "\n", + "slab2 = slab1.copy()\n", + "\n", + "# merge them\n", + "slab2.translate([1, 1, slab1.get_cell()[2][2] + 1.2])\n", + "atoms = slab1 + slab2\n", + "\n", + "# remove pbc along z direction\n", + "atoms.center(axis=2, vacuum=0)\n", + "atoms.set_pbc([True, True, False])" + ] + }, + { + "cell_type": "markdown", + "id": "5eb45b2e-ec03-4695-bc4b-3ce832c61964", + "metadata": {}, + "source": [ + "## Initial pressure equilibration\n", + "To apply a normal load along the *z* direction to this system, we will fix the two lowest atomic layers of the lower crystal. The corresponding atoms are defined with the mask (`bottom_mask`). The two topmost atomic layers of the upper crystal will be treated rigidly (`top_mask`). The *z* position of this rigid region can adapt according to the normal pressure. To reuse the masks in subsequent simulation runs (sliding or restarts), we save them in files.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fb38057-8e69-479b-bb17-d5060d05e05c", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "bottom_mask = atoms.get_positions()[:, 2] < 1.4 # mask for lower fixed atoms\n", + "top_mask = atoms.get_positions()[:, 2] > atoms.get_cell()[2][2] - 1.4 # mask for upper rigid atoms\n", + "\n", + "np.savetxt(\"bottom_mask.txt\", bottom_mask)\n", + "np.savetxt(\"top_mask.txt\", top_mask)" + ] + }, + { + "cell_type": "markdown", + "id": "d8358e72", + "metadata": {}, + "source": [ + "We now specify the numerical parameters of the MD simulation. A Langevin thermostat will be used in the *y* direction along which neither the normal load nor the sliding motion are applied. For simplicity, we will thermalize all atoms which are not part of the constraint regions. This makes sense for small systems which cannot have a dedicated thermostat region. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef4506ec", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.units import GPa, fs\n", + "\n", + "dt = 5.0 * fs # MD time step\n", + "C11 = 166.0 * GPa # material constant\n", + "M_factor = 1.0 # scaling factor for lid mass during equilibration,\n", + "# 1.0 will give fast equilibration for expensive calculators\n", + "\n", + "Pdir = 2 # index of cell axis along which the normal pressure is applied\n", + "P = 1.0 * GPa # target normal pressure\n", + "v = 0.0 # no sliding yet, only apply pressure\n", + "vdir = 0 # index of cell axis along which sliding happens\n", + "T = 300.0 # target temperature for thermostat in K\n", + "\n", + "t_langevin = 75.0 * fs # time constant for Langevin thermostat\n", + "gamma_langevin = 1. / t_langevin # derived Langevin parameter\n", + "t_integrate = 100.0 * fs # simulation time\n", + "steps_integrate = int(t_integrate / dt) # number of simulation steps" + ] + }, + { + "cell_type": "markdown", + "id": "d95c366a-9180-426e-944a-1d4745e0f827", + "metadata": {}, + "source": [ + "Next, we set up the calculation by attaching the constraint and a calculator to the atoms object. For the initial pressure equilibration, we critically damp the motion of the upper rigid layer while not increasing its mass. This is useful for a fast pressure equilibration.\n", + "Interatomic interactions are modelled with the Stillinger-Weber potential for silicon. For a fast temperature convergence, since we typically start from a local minimum, we also set the initial temperature of the system to twice the target temperature." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9bc1a2c-10a5-49bf-985f-d98d54e77bca", + "metadata": {}, + "outputs": [], + "source": [ + "from matscipy.calculators.manybody.explicit_forms.stillinger_weber import StillingerWeber, Stillinger_Weber_PRB_31_5262_Si\n", + "from matscipy.calculators.manybody import Manybody\n", + "from matscipy import pressurecoupling as pc\n", + "from ase.md.velocitydistribution import MaxwellBoltzmannDistribution\n", + "\n", + "damp = pc.FixedMassCriticalDamping(C11, M_factor) # specifies the critical damping with a fixed mass \n", + "slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask, Pdir,\n", + " P, vdir, v, damp) # ASE constraint for pressure coupling\n", + "\n", + "atoms.set_constraint(slider) # attach constraint to atoms object\n", + "\n", + "MaxwellBoltzmannDistribution(atoms, temperature_K=2 * T) # initialize temperature\n", + "\n", + "# clear momenta in constraint regions, otherwise upper rigid region might run away\n", + "atoms.arrays['momenta'][top_mask, :] = 0\n", + "atoms.arrays['momenta'][bottom_mask, :] = 0\n", + "\n", + "calc = Manybody(**StillingerWeber(Stillinger_Weber_PRB_31_5262_Si)) # specify calculator\n", + "atoms.set_calculator(calc) # attach calculator" + ] + }, + { + "cell_type": "markdown", + "id": "3a3405e6-4efc-49a3-a453-d3d01b73f5a9", + "metadata": {}, + "source": [ + "Finally, we setup the integrator and run the simulation. This will create an ASE trajectory file of the MD run and a log file that tracks the current status of the simulation in terms of the system temperature, the system height, the shear stress etc.\n", + "The log file can be read using the class ({py:class}`SlideLog `)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b96c21b-fe28-4e93-8482-58ec73092b28", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.io import Trajectory\n", + "from ase.md.langevin import Langevin\n", + "from io import open\n", + "\n", + "# only thermalize middle region in one direction\n", + "temps = np.zeros((len(atoms), 3))\n", + "temps[slider.middle_mask, slider.Tdir] = T\n", + "gammas = np.zeros((len(atoms), 3))\n", + "gammas[slider.middle_mask, slider.Tdir] = gamma_langevin\n", + "\n", + "# set up integrator\n", + "integrator = Langevin(atoms, dt, temperature_K=temps,\n", + " friction=gammas, fixcm=False)\n", + "\n", + "# set up trajectory file\n", + "trajectory = Trajectory('equilibrate_pressure_01.traj', 'w', atoms) \n", + "\n", + "# set up logger\n", + "log_handle = open('log_equilibrate_01.txt', 'w', 1, encoding='utf-8') # 1 means line buffered,\n", + "logger = pc.SlideLogger(log_handle, atoms, slider, integrator)\n", + "\n", + "logger.write_header() # write header of log file\n", + "integrator.attach(logger) # attach logger to integrator\n", + "integrator.attach(trajectory) # attach trajectory to integrator\n", + "integrator.run(steps_integrate) # run the simulation\n", + "log_handle.close()\n", + "trajectory.close()" + ] + }, + { + "cell_type": "markdown", + "id": "12f6c608-7ca3-4782-b054-a18b399c4a8b", + "metadata": {}, + "source": [ + "## Restarting a pressure equilibration\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "ae61341b", + "metadata": {}, + "source": [ + "To restart the pressure equilibration, we start by specifying the parameters of the MD simulation, read the previous trajectory to initialize the current status of the system, and read the masks for the constraint regions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0cfb76fb-ee48-4b23-96df-4ad78bc85df2", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.units import GPa, fs\n", + "from matscipy.calculators.manybody.explicit_forms.stillinger_weber import StillingerWeber, Stillinger_Weber_PRB_31_5262_Si\n", + "from matscipy.calculators.manybody import Manybody\n", + "from matscipy import pressurecoupling as pc\n", + "from ase.io import read\n", + "import numpy as np\n", + "\n", + "dt = 5.0 * fs # MD time step\n", + "C11 = 166.0 * GPa # material constant\n", + "M_factor = 1.0 # scaling factor for lid mass during equilibration\n", + "\n", + "Pdir = 2 # index of cell axis along normal pressure is applied\n", + "P = 1.0 * GPa # target normal pressure\n", + "v = 0.0 # no sliding yet, only apply pressure\n", + "vdir = 0 # index of cell axis along sliding happens\n", + "T = 300.0 # target temperature for the thermostat\n", + "\n", + "t_langevin = 75.0 * fs # time constant for Langevin thermostat\n", + "gamma_langevin = 1. / t_langevin # derived Langevin parameter\n", + "t_integrate = 100.0 * fs # simulation time\n", + "steps_integrate = int(t_integrate / dt) # number of simulation steps\n", + "\n", + "# get atoms from trajectory to also initialize correct velocities\n", + "atoms = read('equilibrate_pressure_01.traj')\n", + "\n", + "bottom_mask = np.loadtxt(\"bottom_mask.txt\").astype(bool)\n", + "top_mask = np.loadtxt(\"top_mask.txt\").astype(bool)\n", + "\n", + "damp = pc.FixedMassCriticalDamping(C11, M_factor)\n", + "slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask,\n", + " Pdir, P, vdir, v, damp)\n", + "atoms.set_constraint(slider)\n", + "\n", + "calc = Manybody(**StillingerWeber(Stillinger_Weber_PRB_31_5262_Si)) # specify calculator\n", + "atoms.set_calculator(calc)" + ] + }, + { + "cell_type": "markdown", + "id": "11599564-f976-4813-8ebd-408792c9c6df", + "metadata": {}, + "source": [ + "Next, we again initialize the integrator." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db51ca03-b229-463d-b6b8-43c19548928d", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.md.langevin import Langevin\n", + "\n", + "temps = np.zeros((len(atoms), 3))\n", + "temps[slider.middle_mask, slider.Tdir] = T\n", + "gammas = np.zeros((len(atoms), 3))\n", + "gammas[slider.middle_mask, slider.Tdir] = gamma_langevin\n", + "integrator = Langevin(atoms, dt, temperature_K=temps,\n", + " friction=gammas, fixcm=False)" + ] + }, + { + "cell_type": "markdown", + "id": "eceabc60", + "metadata": {}, + "source": [ + "We specify the trajectory file and the log file and run the simulation. Note that ASE automatically writes the initial step of a run, i.e., if you choose to append to the previous trajectory and log files, both will contain the information about the initial configuration twice, since this configuration has already been written to the files at the end of the previous run." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c664eedf-8890-4960-8a59-12a429223eff", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.io import Trajectory\n", + "from io import open\n", + "\n", + "trajectory = Trajectory('equilibrate_pressure_02.traj', 'w', atoms)\n", + "\n", + "with open('log_equilibrate_01.txt', 'r', encoding='utf-8') as log_handle:\n", + " step_offset = pc.SlideLog(log_handle).step[-1] # read last step\n", + "\n", + "log_handle = open('log_equilibrate_02.txt',\n", + " 'w', 1, encoding='utf-8') # line buffered\n", + "logger = pc.SlideLogger(log_handle, atoms, slider, integrator, step_offset)\n", + "logger.write_header() # write header of log file\n", + "\n", + "integrator.attach(logger)\n", + "integrator.attach(trajectory)\n", + "integrator.run(steps_integrate)\n", + "log_handle.close()\n", + "trajectory.close()" + ] + }, + { + "cell_type": "markdown", + "id": "81338c89-9e2b-4146-a2f1-36118a2e5af7", + "metadata": {}, + "source": [ + "## Sliding simulation\n", + "\n", + "After the pressure equilibration, we can start to apply a shear deformation to the system by applying a constant sliding velocity to the upper rigid layer. First, we specify the parameters of the MD simulation, read the equilibration trajectory to initialize the current status of the system, and read the masks for the constraint regions.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5643f2cb-c81d-4d7a-bd48-2555492bd101", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.units import GPa, fs, m, s\n", + "from ase.io import read\n", + "import numpy as np\n", + "from matscipy.calculators.manybody.explicit_forms.stillinger_weber import StillingerWeber, Stillinger_Weber_PRB_31_5262_Si\n", + "from matscipy.calculators.manybody import Manybody\n", + "\n", + "dt = 5.0 * fs # MD time step\n", + "C11 = 166.0 * GPa # material constant\n", + "p_c = 0.20 # empirical cutoff parameter value to remove high-frequency oscillations of the system \n", + "\n", + "Pdir = 2 # index of cell axis along normal pressure is applied\n", + "P = 1.0 * GPa # target normal pressure\n", + "v = 100.0 * m / s # constant sliding speed\n", + "vdir = 0 # index of cell axis along sliding happens\n", + "T = 300.0 # target temperature for thermostat\n", + "\n", + "t_langevin = 75.0 * fs # time constant for Langevin thermostat\n", + "gamma_langevin = 1. / t_langevin # derived Langevin parameter\n", + "t_integrate = 100.0 * fs # simulation time\n", + "steps_integrate = int(t_integrate / dt) # number of simulation steps\n", + "\n", + "# get atoms from trajectory to also initialize correct velocities\n", + "atoms = read('equilibrate_pressure_02.traj')\n", + "\n", + "bottom_mask = np.loadtxt(\"bottom_mask.txt\").astype(bool)\n", + "top_mask = np.loadtxt(\"top_mask.txt\").astype(bool)\n", + "\n", + "calc = Manybody(**StillingerWeber(Stillinger_Weber_PRB_31_5262_Si)) # specify calculator\n", + "atoms.set_calculator(calc)" + ] + }, + { + "cell_type": "markdown", + "id": "872bbcc8-be7a-4433-8d3d-573b45a37b5d", + "metadata": {}, + "source": [ + "Since we change the pressure coupling algorithm, we need to reset the velocities of the upper rigid layer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b683cc4d-832c-4679-a74d-ae891a873f79", + "metadata": {}, + "outputs": [], + "source": [ + "velocities = atoms.get_velocities()\n", + "velocities[top_mask, Pdir] = 0.0\n", + "\n", + "atoms.set_velocities(velocities)" + ] + }, + { + "cell_type": "markdown", + "id": "68f16247", + "metadata": {}, + "source": [ + "In analogy to the pressure equilibration, we set up the constraint for the simulation, but this time we use the pressure coupling algorithm of [Pastewka et al.](https://doi.org/10.1007/s11249-009-9566-8) rather than critical damping with a fixed mass." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "213de06c-b987-41e9-af09-11998076bc37", + "metadata": {}, + "outputs": [], + "source": [ + "from matscipy import pressurecoupling as pc\n", + "\n", + "damp = pc.AutoDamping(C11, p_c) # Damping by Pastewka et al.\n", + "slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask,\n", + " Pdir, P, vdir, v, damp)\n", + "atoms.set_constraint(slider)" + ] + }, + { + "cell_type": "markdown", + "id": "fab46693-0ab7-4fb6-acff-3ee602236587", + "metadata": {}, + "source": [ + "Afterwards, in analogy to the pressure equilibration, we initialize the integrator, the trajectory and log files, and run the simulation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5019537-e39d-4ee8-be92-22336b046c11", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.io import Trajectory\n", + "from ase.md.langevin import Langevin\n", + "from io import open\n", + "\n", + "temps = np.zeros((len(atoms), 3))\n", + "temps[slider.middle_mask, slider.Tdir] = T\n", + "gammas = np.zeros((len(atoms), 3))\n", + "gammas[slider.middle_mask, slider.Tdir] = gamma_langevin\n", + "integrator = Langevin(atoms, dt, temperature_K=temps,\n", + " friction=gammas, fixcm=False)\n", + "\n", + "trajectory = Trajectory('slide_01.traj', 'w', atoms)\n", + "log_handle = open('log_slide_01.txt', 'w', 1, encoding='utf-8') # line buffered\n", + "logger = pc.SlideLogger(log_handle, atoms, slider, integrator)\n", + "# log can be read using pc.SlideLog (see docstring there)\n", + "logger.write_header()\n", + "\n", + "integrator.attach(logger)\n", + "integrator.attach(trajectory)\n", + "integrator.run(steps_integrate)\n", + "log_handle.close()\n", + "trajectory.close()" + ] + }, + { + "cell_type": "markdown", + "id": "56b085ef-06ab-464f-8eaf-bceba08fd3ba", + "metadata": {}, + "source": [ + "## Restarting a sliding simulation\n", + "To restart the sliding simulation, the following script could be used.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12744ced-a9e2-4adb-aa5f-748ed6cc22b5", + "metadata": {}, + "outputs": [], + "source": [ + "from ase.io import Trajectory, read\n", + "from ase.units import GPa, fs, m, s\n", + "import numpy as np\n", + "from ase.md.langevin import Langevin\n", + "from matscipy import pressurecoupling as pc\n", + "from io import open\n", + "from matscipy.calculators.manybody.explicit_forms.stillinger_weber import StillingerWeber, Stillinger_Weber_PRB_31_5262_Si\n", + "from matscipy.calculators.manybody import Manybody\n", + "\n", + "# Parameters\n", + "dt = 5.0 * fs # MD time step\n", + "C11 = 166.0 * GPa # material constant\n", + "p_c = 0.20 # empirical cutoff parameter value to remove high-frequency oscillations of the system\n", + "\n", + "Pdir = 2 # index of cell axis along which normal pressure is applied\n", + "P = 1.0 * GPa # target normal pressure\n", + "v = 100.0 * m / s # constant sliding speed\n", + "vdir = 0 # index of cell axis along which sliding happens\n", + "T = 300.0 # target temperature for thermostat\n", + "\n", + "t_langevin = 75.0 * fs # time constant for Langevin thermostat\n", + "gamma_langevin = 1. / t_langevin # derived Langevin parameter\n", + "t_integrate = 100.0 * fs # simulation time\n", + "steps_integrate = int(t_integrate / dt) # number of simulation steps\n", + "\n", + "# get atoms from trajectory to also initialize correct velocities\n", + "atoms = read('slide_01.traj')\n", + "\n", + "bottom_mask = np.loadtxt(\"bottom_mask.txt\").astype(bool)\n", + "top_mask = np.loadtxt(\"top_mask.txt\").astype(bool)\n", + "\n", + "# set up sliding constraints\n", + "damp = pc.AutoDamping(C11, p_c)\n", + "slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask,\n", + " Pdir, P, vdir, v, damp)\n", + "atoms.set_constraint(slider)\n", + "\n", + "# set up calculator\n", + "calc = Manybody(**StillingerWeber(Stillinger_Weber_PRB_31_5262_Si))\n", + "atoms.set_calculator(calc)\n", + "\n", + "# set up integrator\n", + "temps = np.zeros((len(atoms), 3))\n", + "temps[slider.middle_mask, slider.Tdir] = T\n", + "gammas = np.zeros((len(atoms), 3))\n", + "gammas[slider.middle_mask, slider.Tdir] = gamma_langevin\n", + "integrator = Langevin(atoms, dt, temperature_K=temps,\n", + " friction=gammas, fixcm=False)\n", + "\n", + "# specify log file and trajectory file and run the simulation\n", + "trajectory = Trajectory('slide_02.traj', 'w', atoms)\n", + "\n", + "with open('log_slide_01.txt', 'r', encoding='utf-8') as log_handle:\n", + " step_offset = pc.SlideLog(log_handle).step[-1] # read last step\n", + "\n", + "log_handle = open('log_slide_02.txt', 'w',\n", + " 1, encoding='utf-8') # line buffered\n", + "logger = pc.SlideLogger(log_handle, atoms, slider, integrator, step_offset)\n", + "logger.write_header()\n", + "\n", + "integrator.attach(logger)\n", + "integrator.attach(trajectory)\n", + "integrator.run(steps_integrate)\n", + "log_handle.close()\n", + "trajectory.close()" + ] + }, + { + "cell_type": "markdown", + "id": "b9fe764b", + "metadata": {}, + "source": [ + "To remove all simulation files created during this tutorial, you can use the following code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0618c8e8", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "for tmp_file in ['log_equilibrate_01.txt', 'log_equilibrate_02.txt',\n", + " 'log_slide_01.txt', 'log_slide_02.txt',\n", + " 'equilibrate_pressure_01.traj', 'equilibrate_pressure_02.traj',\n", + " 'slide_01.traj', 'slide_02.traj',\n", + " 'top_mask.txt', 'bottom_mask.txt']:\n", + " try:\n", + " os.remove(tmp_file)\n", + " except FileNotFoundError:\n", + " continue" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "039b85d3", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "test", + "language": "python", + "name": "test" + }, + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/calculators.rst b/docs/calculators.rst deleted file mode 100644 index 14ca09bd..00000000 --- a/docs/calculators.rst +++ /dev/null @@ -1,5 +0,0 @@ -Calculators -=========== - -Here's a description of the calculators available in `matscipy` and how they can -be composed. diff --git a/docs/calculators/index.rst b/docs/calculators/index.rst new file mode 100644 index 00000000..64bb4e1f --- /dev/null +++ b/docs/calculators/index.rst @@ -0,0 +1,8 @@ +Interatomic potentials +====================== + +`matscipy` implements a select set of interatomic potentials as ASE calculators. + +.. toctree:: + + manybody diff --git a/docs/calculators/manybody.ipynb b/docs/calculators/manybody.ipynb new file mode 100644 index 00000000..cee08844 --- /dev/null +++ b/docs/calculators/manybody.ipynb @@ -0,0 +1,170 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Many-body potentials\n", + "\n", + "``matscipy`` implements support for generic manybody potentials. The class {py:class}`matscipy.calculators.manybody.Manybody` implements the functional form\n", + "\n", + "```{math}\n", + " U\n", + " =\n", + " \\frac{1}{2}\n", + " \\sum_{\\substack{ij\\\\ i\\neq j}}\n", + " U_2(r^2_{ij}) + U_\\text{m}(r^2_{ij}, \\xi_{ij})\n", + "```\n", + "\n", + "with\n", + "\n", + "```{math}\n", + " \\xi_{ij} \n", + " = \n", + " \\sum_{\\substack{k\\\\ k\\neq i,j}} \n", + " \\Xi(r^2_{ij}, r^2_{ik}, r^2_{jk})\n", + "```\n", + "\n", + "as described, e.g. by [Müser et al.](https://doi.org/10.1080/23746149.2022.2093129) and [Grießer et al.](https://doi.org/10.48550/arXiv.2302.08754). On top of energies and forces, the calculator can compute second derivatives (with respect to positions and strain degrees of freedom). Explicit functional forms of {math}`U_2`, {math}`U_\\text{m}` and {math}`\\Xi` are implemented for a number of potential." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Kumagai potential\n", + "\n", + "The following example computes the elastic constants of a small representation of amorphous silicon using the potential by [Kumagai et al.](https://doi.org/10.1016/j.commatsci.2006.07.013). We first load the amorphous structure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ase.io import read\n", + "\n", + "def interactive_view(system):\n", + " from ase.visualize import view\n", + " # Interactive view of the lattice\n", + " v = view(system, viewer='ngl')\n", + "\n", + " # Resize widget\n", + " v.view._remote_call(\"setSize\", target=\"Widget\", args=[\"300px\", \"300px\"])\n", + " v.view.center()\n", + " return v\n", + "\n", + "a = read('../../tests/aSi.cfg')\n", + "a.symbols[:] = 'Si'\n", + "\n", + "interactive_view(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we setup the calculator with the Kumagai potential and its parametrization." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from matscipy.calculators.manybody import Manybody\n", + "from matscipy.calculators.manybody.explicit_forms import Kumagai\n", + "from matscipy.calculators.manybody.explicit_forms.kumagai import Kumagai_Comp_Mat_Sci_39_Si\n", + "from IPython.display import display, Markdown\n", + "\n", + "a.calc = Manybody(**Kumagai(Kumagai_Comp_Mat_Sci_39_Si))\n", + "\n", + "display(Markdown(f'Cohesive energy = {a.get_potential_energy() / len(a):.2f} eV/atom'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also compute the Born elastic constants, i.e. the affine elastic constants:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from ase.units import GPa\n", + "from matscipy.elasticity import elastic_moduli, full_3x3x3x3_to_Voigt_6x6\n", + "\n", + "# Born elastic constants (without nonaffine displacements)\n", + "C = a.calc.get_property('born_constants', a)\n", + "\n", + "# Get isotropic elastic moduli\n", + "E, nu, G, B, K = elastic_moduli(full_3x3x3x3_to_Voigt_6x6(C))\n", + "\n", + "display(Markdown(f\"Young's modulus = {E.mean() / GPa:.1f} GPa\"))\n", + "display(Markdown(f\"Poisson number = {(nu + np.eye(3)).sum()/6:.3f}\"))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Other many-body potentials\n", + "\n", + "To runs the same code with [Stillinger-Weber](https://doi.org/10.1103/PhysRevB.31.5262), replace the calculator by" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from matscipy.calculators.manybody.explicit_forms import StillingerWeber\n", + "from matscipy.calculators.manybody.explicit_forms.stillinger_weber import Stillinger_Weber_PRB_31_5262_Si\n", + "\n", + "a.calc = Manybody(**StillingerWeber(Stillinger_Weber_PRB_31_5262_Si))\n", + "\n", + "display(Markdown(f'Cohesive energy = {a.get_potential_energy() / len(a):.2f} eV/atom'))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Other examples of potentials that are implemented are [Tersoff](https://doi.org/10.1103/PhysRevB.39.5566), [Brenner](https://doi.org/10.1103/PhysRevB.42.9458) (without the lookup tables), [Erhart-Albe](https://doi.org/10.1103/PhysRevB.71.035211) and others. Second derivatives are supported for all of these." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/conf.py b/docs/conf.py index 4fa60355..9e535e28 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,6 +42,8 @@ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.autosummary', + 'sphinx.ext.mathjax', + 'myst_nb' ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/elastic_constants.rst b/docs/elastic_constants.rst deleted file mode 100644 index 6ff88f6c..00000000 --- a/docs/elastic_constants.rst +++ /dev/null @@ -1,4 +0,0 @@ -Elastic Constants -================= - -Here's how to use `matscipy` to evaluate elastic constants of an RVE. diff --git a/docs/fracture_mechanics.rst b/docs/fracture_mechanics.rst deleted file mode 100644 index 8eeeca0f..00000000 --- a/docs/fracture_mechanics.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fracture Mechanics -================== - -Here's what you can do with Matscipy in the field of fracture mechanics. diff --git a/docs/index.rst b/docs/index.rst index 0eb8607e..783090d7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,54 +1,14 @@ -.. matscipy documentation master file, created by - sphinx-quickstart on Sun May 17 16:40:20 2015. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - .. include:: ../README.rst - -.. - Matscipy submodules - =================== - - .. autosummary:: - - matscipy.angle_distribution - matscipy.atomic_strain - matscipy.calculators - matscipy.calculators.eam - matscipy.calculators.pair_potential - matscipy.calculators.mcfm - matscipy.calculators.supercell_calculator - matscipy.calculators.manybody - matscipy.contact_mechanics - matscipy.contact_mechanics.DMT - matscipy.contact_mechanics.greens_function - matscipy.contact_mechanics.Hertz - matscipy.contact_mechanics.JKR - matscipy.elasticity - matscipy.fracture_mechanics - matscipy.fracture_mechanics.clusters - matscipy.fracture_mechanics.crack - matscipy.fracture_mechanics.crackpathsel - matscipy.fracture_mechanics.energy_release - matscipy.fracture_mechanics.idealbrittlesolid - matscipy.hydrogenate - matscipy.io - matscipy.logger - matscipy.neighbours - matscipy.numerical - matscipy.rings - matscipy.surface - .. toctree:: :maxdepth: 2 :caption: Table of Contents: - ./calculators - ./fracture_mechanics - ./plasticity - ./elastic_constants - ./api_reference + self + ./applications/index + ./tools/index + ./calculators/index + ./api Indices and tables diff --git a/docs/matscipy.calculators.eam.rst b/docs/matscipy.calculators.eam.rst deleted file mode 100644 index 137b51f9..00000000 --- a/docs/matscipy.calculators.eam.rst +++ /dev/null @@ -1,39 +0,0 @@ -matscipy.calculators.eam package -================================ - -.. automodule:: matscipy.calculators.eam - :members: - :undoc-members: - :show-inheritance: - -Modules -------- - -.. autosummary:: - matscipy.calculators.eam.calculator - matscipy.calculators.eam.io - matscipy.calculators.eam.average_atom - -matscipy.calculators.eam.calculator module ------------------------------------------- - -.. automodule:: matscipy.calculators.eam.calculator - :members: - :undoc-members: - :show-inheritance: - -matscipy.ios.eam.io module --------------------------- - -.. automodule:: matscipy.calculators.eam.io - :members: - :undoc-members: - :show-inheritance: - -matscipy.average_atoms.eam.average_atom module ----------------------------------------------- - -.. automodule:: matscipy.calculators.eam.average_atom - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.calculators.manybody.rst b/docs/matscipy.calculators.manybody.rst deleted file mode 100644 index 753e10b1..00000000 --- a/docs/matscipy.calculators.manybody.rst +++ /dev/null @@ -1,22 +0,0 @@ -matscipy.calculators.manybody package -===================================== - -.. automodule:: matscipy.calculators.manybody - :members: - :undoc-members: - :show-inheritance: - -Modules -------- - -.. autosummary:: - matscipy.calculators.manybody.explicit_forms - -matscipy.calculators.manybody.explicit_forms --------------------------------------------- - -.. automodule:: matscipy.calculators.manybody.explicit_forms.harmonic - :members: - :undoc-members: - :show-inheritance: - :special-members: diff --git a/docs/matscipy.calculators.mcfm.mcfm_parallel.rst b/docs/matscipy.calculators.mcfm.mcfm_parallel.rst deleted file mode 100644 index 694af9c5..00000000 --- a/docs/matscipy.calculators.mcfm.mcfm_parallel.rst +++ /dev/null @@ -1,30 +0,0 @@ -matscipy.calculators.mcfm.mcfm_parallel package -=============================================== - -.. automodule:: matscipy.calculators.mcfm.mcfm_parallel - :members: - :undoc-members: - :show-inheritance: - -Modules -------- - -.. autosummary:: - matscipy.calculators.mcfm.mcfm_parallel.mcfm_parallel_control - matscipy.calculators.mcfm.mcfm_parallel.mcfm_parallel_worker - -matscipy.calculators.mcfm.mcfm_parallel_control module ------------------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.mcfm_parallel.mcfm_parallel_control - :members: - :undoc-members: - :show-inheritance: - -matscipy.calculators.mcfm.mcfm_parallel_worker module ------------------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.mcfm_parallel.mcfm_parallel_worker - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.calculators.mcfm.neighbour_list_mcfm.rst b/docs/matscipy.calculators.mcfm.neighbour_list_mcfm.rst deleted file mode 100644 index fc30b962..00000000 --- a/docs/matscipy.calculators.mcfm.neighbour_list_mcfm.rst +++ /dev/null @@ -1,30 +0,0 @@ -matscipy.calculators.mcfm.neighbour_list_mcfm package -===================================================== - -.. automodule:: matscipy.calculators.mcfm.neighbour_list_mcfm - :members: - :undoc-members: - :show-inheritance: - -Modules -------- - -.. autosummary:: - matscipy.calculators.mcfm.neighbour_list_mcfm.neighbour_list_base - matscipy.calculators.mcfm.neighbour_list_mcfm.neighbour_list_mcfm - -matscipy.calculators.mcfm.neighbour_list_mcfm.neighbour_list_base module ------------------------------------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.neighbour_list_mcfm.neighbour_list_base - :members: - :undoc-members: - :show-inheritance: - -matscipy.calculators.mcfm.neighbour_list_mcfm.neighbour_list_mcfm module ------------------------------------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.neighbour_list_mcfm.neighbour_list_mcfm - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.calculators.mcfm.qm_cluster_tools.rst b/docs/matscipy.calculators.mcfm.qm_cluster_tools.rst deleted file mode 100644 index 68235475..00000000 --- a/docs/matscipy.calculators.mcfm.qm_cluster_tools.rst +++ /dev/null @@ -1,39 +0,0 @@ -matscipy.calculators.mcfm.qm_cluster_tools package -================================================== - -.. automodule:: matscipy.calculators.mcfm.qm_cluster_tools - :members: - :undoc-members: - :show-inheritance: - -Modules -------- - -.. autosummary:: - matscipy.calculators.mcfm.qm_cluster_tools.base_qm_cluster_tool - matscipy.calculators.mcfm.qm_cluster_tools.qm_clustering_tool - matscipy.calculators.mcfm.qm_cluster_tools.qm_flagging_tool - -matscipy.calculators.mcfm.qm_cluster_tools.base_qm_cluster_tool module ----------------------------------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.qm_cluster_tools.base_qm_cluster_tool - :members: - :undoc-members: - :show-inheritance: - -matscipy.calculators.mcfm.qm_cluster_tools.qm_clustering_tools module ---------------------------------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.qm_cluster_tools.qm_clustering_tool - :members: - :undoc-members: - :show-inheritance: - -matscipy.calculators.mcfm.qm_cluster_tools.qm_flagging_tool module ------------------------------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.qm_cluster_tools.qm_flagging_tool - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.calculators.mcfm.rst b/docs/matscipy.calculators.mcfm.rst deleted file mode 100644 index c8cf576d..00000000 --- a/docs/matscipy.calculators.mcfm.rst +++ /dev/null @@ -1,47 +0,0 @@ -matscipy.calculators.mcfm package -================================= - -.. automodule:: matscipy.calculators.mcfm - :members: - :undoc-members: - :show-inheritance: - -Modules -------- - -.. autosummary:: - matscipy.calculators.mcfm.calculator - matscipy.calculators.mcfm.cluster_data - matscipy.calculators.mcfm.qm_cluster - -Packages --------- - -.. autosummary:: - matscipy.calculators.mcfm.mcfm_parallel - matscipy.calculators.mcfm.neighbour_list_mcfm - matscipy.calculators.mcfm.qm_cluster_tools - -matscipy.calculators.mcfm.calculator module -------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.calculator - :members: - :undoc-members: - :show-inheritance: - -matscipy.calculators.mcfm.cluster_data module ---------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.cluster_data - :members: - :undoc-members: - :show-inheritance: - -matscipy.calculators.mcfm.qm_cluster module -------------------------------------------- - -.. automodule:: matscipy.calculators.mcfm.qm_cluster - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.calculators.pair_potential.rst b/docs/matscipy.calculators.pair_potential.rst deleted file mode 100644 index ee2249d3..00000000 --- a/docs/matscipy.calculators.pair_potential.rst +++ /dev/null @@ -1,21 +0,0 @@ -matscipy.calculators.pair_potential package -=========================================== - -.. automodule:: matscipy.calculators.pair_potential - :members: - :undoc-members: - :show-inheritance: - -Modules -------- - -.. autosummary:: - matscipy.calculators.pair_potential.calculator - -matscipy.calculators.pair_potential.calculator module ------------------------------------------------------ - -.. automodule:: matscipy.calculators.pair_potential.calculator - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.calculators.rst b/docs/matscipy.calculators.rst deleted file mode 100644 index aed09bf3..00000000 --- a/docs/matscipy.calculators.rst +++ /dev/null @@ -1,24 +0,0 @@ -matscipy.calculators package -============================ - -.. automodule:: matscipy.calculators - :members: - :undoc-members: - :show-inheritance: - -Packages --------- - -.. autosummary:: - - matscipy.calculators.eam - matscipy.calculators.mcfm - matscipy.calculators.pair_potential - -Modules -------- - -.. autosummary:: - - matscipy.calculators.supercell_calculator - diff --git a/docs/matscipy.calculators.supercell_calculator.rst b/docs/matscipy.calculators.supercell_calculator.rst deleted file mode 100644 index 7e90368b..00000000 --- a/docs/matscipy.calculators.supercell_calculator.rst +++ /dev/null @@ -1,7 +0,0 @@ -matscipy.calculators.supercell_calculator module -================================================ - -.. automodule:: matscipy.calculators.supercell_calculator - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.contact_mechanics.rst b/docs/matscipy.contact_mechanics.rst deleted file mode 100644 index 2f525dbf..00000000 --- a/docs/matscipy.contact_mechanics.rst +++ /dev/null @@ -1,46 +0,0 @@ -matscipy.contact_mechanics package -================================== - -Submodules ----------- - -matscipy.contact_mechanics.DMT module -------------------------------------- - -.. automodule:: matscipy.contact_mechanics.DMT - :members: - :undoc-members: - :show-inheritance: - -matscipy.contact_mechanics.Hertz module ---------------------------------------- - -.. automodule:: matscipy.contact_mechanics.Hertz - :members: - :undoc-members: - :show-inheritance: - -matscipy.contact_mechanics.JKR module -------------------------------------- - -.. automodule:: matscipy.contact_mechanics.JKR - :members: - :undoc-members: - :show-inheritance: - -matscipy.contact_mechanics.greens_function module -------------------------------------------------- - -.. automodule:: matscipy.contact_mechanics.greens_function - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: matscipy.contact_mechanics - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.fracture_mechanics.rst b/docs/matscipy.fracture_mechanics.rst deleted file mode 100644 index 73c2ef69..00000000 --- a/docs/matscipy.fracture_mechanics.rst +++ /dev/null @@ -1,54 +0,0 @@ -matscipy.fracture_mechanics package -=================================== - -Submodules ----------- - -matscipy.fracture_mechanics.clusters module -------------------------------------------- - -.. automodule:: matscipy.fracture_mechanics.clusters - :members: - :undoc-members: - :show-inheritance: - -matscipy.fracture_mechanics.crack module ----------------------------------------- - -.. automodule:: matscipy.fracture_mechanics.crack - :members: - :undoc-members: - :show-inheritance: - -matscipy.fracture_mechanics.crackpathsel module ------------------------------------------------ - -.. automodule:: matscipy.fracture_mechanics.crackpathsel - :members: - :undoc-members: - :show-inheritance: - -matscipy.fracture_mechanics.energy_release module -------------------------------------------------- - -.. automodule:: matscipy.fracture_mechanics.energy_release - :members: - :undoc-members: - :show-inheritance: - -matscipy.fracture_mechanics.idealbrittlesolid module ----------------------------------------------------- - -.. automodule:: matscipy.fracture_mechanics.idealbrittlesolid - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: matscipy.fracture_mechanics - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/matscipy.rst b/docs/matscipy.rst deleted file mode 100644 index 894efec2..00000000 --- a/docs/matscipy.rst +++ /dev/null @@ -1,110 +0,0 @@ -matscipy package -================ - -Subpackages ------------ - -.. toctree:: - - matscipy.contact_mechanics - matscipy.fracture_mechanics - matscipy.calculators - -Submodules ----------- - -matscipy.angle_distribution module ----------------------------------- - -.. automodule:: matscipy.angle_distribution - :members: - :undoc-members: - :show-inheritance: - -matscipy.atomic_strain module ------------------------------ - -.. automodule:: matscipy.atomic_strain - :members: - :undoc-members: - :show-inheritance: - -matscipy.elasticity module --------------------------- - -.. automodule:: matscipy.elasticity - :members: - :undoc-members: - :show-inheritance: - -matscipy.hydrogenate module ---------------------------- - -.. automodule:: matscipy.hydrogenate - :members: - :undoc-members: - :show-inheritance: - -matscipy.io module ------------------- - -.. automodule:: matscipy.io - :members: - :undoc-members: - :show-inheritance: - -matscipy.logger module ----------------------- - -.. automodule:: matscipy.logger - :members: - :undoc-members: - :show-inheritance: - -matscipy.neighbours module --------------------------- - -.. automodule:: matscipy.neighbours - :members: - :undoc-members: - :show-inheritance: - -matscipy.rings module ---------------------- - -.. automodule:: matscipy.rings - :members: - :undoc-members: - :show-inheritance: - -matscipy.socketcalc module --------------------------- - -.. automodule:: matscipy.socketcalc - :members: - :undoc-members: - :show-inheritance: - -matscipy.structure_identification module ----------------------------------------- - -.. automodule:: matscipy.structure_identification - :members: - :undoc-members: - :show-inheritance: - -matscipy.surface module ------------------------ - -.. automodule:: matscipy.surface - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: matscipy - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/modules.rst b/docs/modules.rst deleted file mode 100644 index 296b7ae5..00000000 --- a/docs/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -matscipy -======== - -.. toctree:: - :maxdepth: 4 - - matscipy diff --git a/docs/tools/index.rst b/docs/tools/index.rst new file mode 100644 index 00000000..a622286f --- /dev/null +++ b/docs/tools/index.rst @@ -0,0 +1,10 @@ +Analysis tools +============== + +`matscipy` contains general utility functionality which is widely applicable. + +.. toctree:: + + neighbour_list + pair_distribution + ring_analysis diff --git a/docs/tools/neighbour_list.ipynb b/docs/tools/neighbour_list.ipynb new file mode 100644 index 00000000..7e980423 --- /dev/null +++ b/docs/tools/neighbour_list.ipynb @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neighbour list\n", + "\n", + "## Basic usage\n", + "\n", + "`matscipy` neighbour lists are stored in a format similar to the coordinate (`COO`) format of sparse matrices. The basic neighbor list consists of two array that each contain the indices of the atoms that constitute the pair." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(['O', 'H', 'H'],\n", + " array([0, 0, 1, 2], dtype=int32),\n", + " array([2, 1, 0, 0], dtype=int32))" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "from ase.build import molecule\n", + "from matscipy.neighbours import neighbour_list\n", + "\n", + "# Single water in a box with vacuum\n", + "a = molecule('H2O')\n", + "a.center(vacuum=5)\n", + "\n", + "# Create neighbor list\n", + "i, j = neighbour_list('ij', a, cutoff=1.2)\n", + "\n", + "# Return list of neighbor pairs\n", + "a.get_chemical_symbols(), i, j" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The water molecule has four pairs at a cutoff of 1.2, which are the O-H bonds. Each of the bonds occurs twice in the neighbor list.\n", + "\n", + "This list format allows simple analysis. For example, coordination numbers can be computed by counting the number of entries in the index arrays." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([2, 1, 1])" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Bincount counts the number of times a specific entry shows up in the array\n", + "np.bincount(i)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The oxygen atom has a coordination of 2 (both hydrogens) while each of the hydrogens has a coordination of 1 (since only the oxygen is the neighbor)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Per-atom properties\n", + "\n", + "The neighbour list can also compute per atom properties, in particular distances and distance vectors. The first argument to the `neighbour_list` function is a string that identifies the members of the return tuple. If we want distances between atoms, we additionally specific a 'd' in this string. The return tuple then has three members." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.96856502, 0.96856502, 0.96856502, 0.96856502])" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "i, j, d = neighbour_list('ijd', a, cutoff=1.2)\n", + "\n", + "d" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This the O-H bond length. If we increase the cutoff to 2 Å, we also capture the H-H distance." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.96856502, 0.96856502, 0.96856502, 1.526478 , 0.96856502,\n", + " 1.526478 ])" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "neighbour_list('d', a, cutoff=2.0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.10.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tools/pair_distribution.ipynb b/docs/tools/pair_distribution.ipynb new file mode 100644 index 00000000..6cddf268 --- /dev/null +++ b/docs/tools/pair_distribution.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pair-distribution function\n", + "\n", + "## Real-space\n", + "\n", + "A pair distribution function can be straightforwardly calculated from a neighbor list. We need to instruct the neighbor list to return the distance between atoms." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.0, 4.0)" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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RxBFbmtJREREgSVgaoCIiIiIJAyAiIiIKOAwACIiIqKAwwCIiIiIAg4DICIiIgo4DICIiNykqqoKEydOREJCAp577jlvD4eIGsEAiIjITT744ANoNBp88cUX+OSTT3D8+HFvD4mIHGAAREStQhAEfPbZZ94eRquora1FamoqduzYYXM8IiICMTEx6NatG9q1a4eIiAib1+fNm4dHH33Uk0MlIgcYABGR06ZNmwZBECAIAtRqNWJjY3HLLbfg/fffb9AjKTc3F7fddptT121rwdKyZcvQuXNnDB8+3Ob4H/7wB2zduhWRkZFIT09HQkKCzeuPP/44PvjgA5w7d86TwyUiOxgAEZFLbr31VuTm5uL8+fPYtGkTbrrpJsyaNQu33347jEajfF5cXBy0Wq0XR9o6RFHEkiVLcP/99zd4rbCwEKdOncKTTz7ZIDsEAB06dMCYMWOwdOlSTwyViBrBAIiIXKLVahEXF4fExEQMHDgQTz31FDZs2IBNmzZh1apV8nnWWZ3a2lo88sgjiI+Ph06nQ6dOnZCZmQkASElJAQDcddddEARBfn727FnceeediI2NRWhoKAYPHozvvvvOZiwpKSl49dVXMWPGDISFhSE5OblB89BLly5h8uTJiIqKQkhICNLT07F792759Q0bNmDgwIHQ6XTo0qULXnjhBZtA7lr79+/H2bNnMW7cuAavffTRRxg4cCDmzZuHo0eP4pdffmlwzvjx47FmzRqH1yciz2AAREQtNmrUKPTr1w/r1q2z+/rbb7+Nzz//HJ988glOnjyJjz76SA509u7dCwBYuXIlcnNz5ecVFRUYO3YstmzZgoMHD+LWW2/F+PHjkZOTY3PtRYsWIT09HQcPHsT/+3//Dw8//DBOnjwpX2PkyJG4fPkyPv/8cxw6dAhPPvmkPF33448/YsqUKZg1axaOHTuGd999F6tWrcIrr7zi8Hv98ccfcd111yEsLKzBaytXrsR9992HiIgI3H777Vi5cmWDc4YMGYJLly7h/Pnzjf9HJaLW5c4OrkTk36ZOnSreeeeddl+75557xJ49e8rPAYjr168XRVEUH330UXHUqFGi2Wy2+17rcxvTu3dv8R//+If8vFOnTuJ9990nPzebzWJMTIy4dOlSURRF8d133xXDwsLEwsJCu9e7+eabxVdffdXm2L///W8xPj7e4RhmzZoljho1qsHx/fv3i2q1Wrxy5YooiqK4fv16MTo6WqytrbU5r7S0VAQgbtu2rYnvlohaEzNAROQWoihCEAS7r02bNg1ZWVno3r07HnvsMXz77bdNXq+iogKPP/44evbsiXbt2iE0NBTHjx9vkAFKS0uTvxYEAXFxcSgoKAAAZGVlYcCAAYiKirL7GYcOHcKLL76I0NBQ+fHggw8iNzcXVVVVdt9TXV0NnU7X4PjKlSsxZswYdOjQAQAwduxYmEwmfPXVVzbnBQUFAYDD6xORZ6i8PQAi8g/Hjx9H586d7b42cOBAZGdnY9OmTfjuu+/w+9//HqNHj8ann37q8HqPP/44Nm/ejL///e9ITU1FUFAQ7r77btTW1tqcp1arbZ4LgiBPcUnBhiMVFRV44YUXMHHixAav2QtyAEsh8+HDh22O6fV6rF69GsXFxVCp6v9ZNZlMWLlyJSZMmCAfKyoqAgBER0c3OjYial0MgIioxb7//nscPnwYc+bMcXhOeHg47rnnHtxzzz24++67ceutt6KoqAhRUVFQq9UwmUw25//888+YNm0a7rrrLgCWYMXVupm0tDSsWLFC/pxrDRw4ECdPnkRqaqrT1xwwYACWLl1qk/H6/PPPUVtbi4MHD0KpVMrnnjhxApMnT0ZBQQFiYmIAAEeOHIFarUbv3r1d+l6IyL0YABGRS/R6PfLy8mAymZCfn4+vv/4amZmZuP322zFlyhS773njjTcQHx+PAQMGQKFQYO3atYiLi0O7du0AWFZzbdmyBSNGjIBWq0VkZCS6deuGdevWYfz48RAEAc8880yDvYaaMnnyZLz66quYMGECMjMzER8fj4MHDyIhIQEZGRl49tlncfvttyM5ORl33303FAoFDh06hCNHjuDll1+2e82bbroJFRUVOHr0KPr06QPAMv01btw49OvXz+bc3r17IzY2Fv/5z38wd+5cAJYi6htuuKHJ7BQRtS7WABGRS77++mvEx8cjJSUFt956K7Zu3Yq3334bGzZssMl+WAsLC8Prr7+O9PR0DB48GOfPn8fGjRuhUFj+CVq0aBE2b96MpKQkDBgwAIAlaIqMjMTw4cMxfvx4jBkzBgMHDnRprBqNBt9++y1iYmIwduxY9O3bFwsWLJDHOWbMGHz55Zf49ttvMXjwYAwbNgyLFy9Gp06dHF6zffv2uOuuu/DRRx8BAH799Vd8++23+N3vftfgXEEQMHHiRJvVYGvWrMGDDz7o0vdBRO4niKIoensQRERtyS+//IJbbrkFZ8+eRWhoqNPv27RpE/7yl7/gl19+sakVIiLPYwaIiMhFaWlpeO2115Cdne3S+yorK7Fy5UoGP0Q+gBkgIiIiCjjMABEREVHAYQBEREREAYcBEBEREQUcBkBEREQUcBgAERERUcBhAEREREQBhwEQERERBRwGQERERBRwGAARERFRwGEARERERAHn/wMfYekncDv67AAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from ase.io import read\n", + "from matscipy.neighbours import neighbour_list\n", + "\n", + "# Load a disordered configuration (amorphous carbon)\n", + "a = read('../../tests/aC.cfg')\n", + "\n", + "# Get distance between atoms up to a distance of 5 Angstroms\n", + "d = neighbour_list('d', a, cutoff=10.0)\n", + "\n", + "# Get distance distribution\n", + "n, x = np.histogram(d, bins=100, density=False)\n", + "\n", + "# Plot the pair-distribution function\n", + "mean_distance = (x[1:] + x[:-1])/2\n", + "mean_density = len(a) / a.get_volume()\n", + "shell_volume = 4 * np.pi * (x[1:] ** 3 - x[:-1] ** 3) / 3\n", + "plt.plot(mean_distance, n / (shell_volume * len(a) * mean_density))\n", + "plt.xlabel('Distance (Å)')\n", + "plt.ylabel('Pair-distribution function')\n", + "plt.xlim(0, 10)\n", + "plt.ylim(0, 4)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fourier space\n", + "\n", + "`matscipy` has utility function to compute pair-distribution (and other pair-correlation) functions. The utility function uses FFT to compute the long tail of the correlation function." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.0, 4.0)" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matscipy.spatial_correlation_function import spatial_correlation_function\n", + "\n", + "n, x = spatial_correlation_function(a, [1]*len(a), approx_FFT_gridsize=0.1)\n", + "\n", + "plt.plot(x[x<10][1:], n[x<10][1:])\n", + "plt.xlabel('Distance (Å)')\n", + "plt.ylabel('Pair-distribution function')\n", + "plt.xlim(0, 10)\n", + "plt.ylim(0, 4)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.10.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/tools/ring_analysis.ipynb b/docs/tools/ring_analysis.ipynb new file mode 100644 index 00000000..705ffc15 --- /dev/null +++ b/docs/tools/ring_analysis.ipynb @@ -0,0 +1,82 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Ring analysis\n", + "\n", + "A common topological measure of amorphous systems is a statistics of shortest-path (SP) rings. `matscipy` has a C-implementation of a backtracking algorithms that computes sp-rings according to the algorithm by [Franzblau](https://doi.org/10.1103/PhysRevB.44.4925). The function `ring_statistics` accept a cutoff for neighbor search and a maximum ring length as arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'Number of rings')" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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eQFfQZEB2tisWbNksVi0cOHCRsdXrlwpi8ViUipzzZs3T1FRUcrPz290Q1ZPeGNBc7HZbDp+/Hir3O9n+vTp2r9/f4ufF/AVFBnABBEREXrmmWd06tQps6O0mJqammb/7MGDBzVq1Ch16tRJCQkJLZjKOwQHBys1NVUhIS17e7va2lq1a9dOycnJLXpewJdQZAATTJw4UampqVqwYMFF3/PUU099a5hl0aJF6ty5s/v5rFmzNG3aNP32t79VSkqK4uLiNH/+fNXV1enRRx9VfHy80tPTtWTJkm+df9++fbr22msVERGh/v37Kycnp9Hru3fv1uTJkxUdHa2UlBT9v//3/1RaWup+fezYsXrggQc0Z84cJSYmatKkSRf8ezidTs2fP1/p6ekKDw9332TVxWKxaPv27Zo/f74sFoueeuqpC55n1apVGjVqlOLi4pSQkKCbb75ZBw8edL/epUsXSdLVV18ti8XivhHm5T7fdSXn73//u6677jq1a9dOw4YN0/79+5Wbm6uhQ4cqOjpakydPVklJifvn1q1bp+HDhysqKkpxcXEaOXKkvvzyywtm/+bVonXr1slisWjNmjUaOnSoIiMjde211yo/P/+CP3/+Od58802NGTNGERER+tvf/vatoSXX/27++te/qnPnzrJarZoxY4YqKirc76moqNDtt9+uqKgodejQQS+++KLGjh2rOXPmXPTzAW9FkQFMEBwcrN/+9rf6/e9/r6NHj17RudauXatjx45p/fr1euGFFzRv3jzdfPPNat++vbZs2aL77rtPP/7xj7/1OY8++qgeeeQR7dixQ5mZmZoyZYrKysokSadPn9b48eN19dVXa9u2bVq1apWKi4v1gx/8oNE5Xn/9dYWFhWnjxo364x//eMF8L730kp5//nn97ne/086dOzVp0iR997vf1YEDByRJx48fV79+/fTII4/o+PHj+tnPfnbB81RVVWnu3Lnatm2b1qxZo6CgIH3ve9+T0+mUJG3dulWS9PHHH+v48eN65513mvT5LvPmzdMTTzyhTz/9VCEhIfrhD3+oxx57TC+99JI++eQTFRQU6Mknn5Qk1dXVadq0aRozZox27typTZs26d577/V4aPCXv/ylnn/+eW3btk0hISG66667Lvszjz/+uB566CHt3bv3ouXx4MGDWrlypT744AN98MEHysnJaTSUOXfuXG3cuFHvvfeePvroI33yySf69NNPPcoOeA0DQJuaOXOmMXXqVMMwDOOaa64x7rrrLsMwDGPFihXG+f9Kzps3zxg4cGCjn33xxReNTp06NTpXp06djPr6evexXr16Gdddd537eV1dnREVFWW88cYbhmEYxuHDhw1JxsKFC93vqa2tNdLT041nnnnGMAzD+M///E/jhhtuaPTZhYWFhiQjPz/fMAzDGDNmjHH11Vdf9u+blpZm/OY3v2l0bNiwYcZPfvIT9/OBAwca8+bNu+y5zldSUmJIMnbt2tXo77Vjxw6PPt/1c//93//tfv2NN94wJBlr1qxxH1uwYIHRq1cvwzAMo6yszJBkrFu3rklZv5ktOzvbkGR8/PHH7vf885//NCQZZ8+eveQ5Fi1a1Oj4kiVLDKvV6n4+b948IzIy0rDb7e5jjz76qDFixAjDMAzDbrcboaGhxltvveV+/fTp00ZkZKTx0EMPNenvA3gTrsgAJnrmmWf0+uuva+/evc0+R79+/RQU9PW/yikpKRowYID7eXBwsBISEnTixIlGP5eZmen+c0hIiIYOHerO8dlnnyk7O1vR0dHuR+/evSWp0XDOkCFDLpnNbrfr2LFjGjlyZKPjI0eO9PjvfODAAd12223q2rWrYmNj3UNsR44caZHPz8jIcP85JSVFkhp9jykpKe7vMD4+XrNmzdKkSZM0ZcoUvfTSSzp+/LhHf59vfmaHDh0k6Vv/nL5p6NChlz1v586dFRMT0+jcrvMeOnRItbW1Gj58uPt1q9WqXr16eZQd8BYUGcBEo0eP1qRJk5SVlfWt14KCgmQYRqNjtbW133pfaGhoo+cWi+WCx1xDME1RWVmpKVOmKC8vr9HjwIEDGj16tPt9UVFRTT7nlZoyZYpOnjypP//5z9qyZYu2bNki6comGZ/v/O/MNUT0zWPnf4dLlizRpk2bdO211+rNN99Uz549tXnz5iv+zMv9c2rKd36l//wBX0KRAUy2cOFCvf/++9q0aVOj40lJSSoqKmpUZlpyafH5v3Tr6uq0fft29enTR5I0ePBg7dmzR507d1b37t0bPTwpL7GxsUpLS9PGjRsbHd+4caP69u3b5POUlZUpPz9fTzzxhCZMmKA+ffp8a8VXWFiYJKm+vr7FP/9irr76amVlZenf//63+vfvr2XLll3xOVtb165dFRoaqtzcXPex8vJylnDDZ7XsWkAAHhswYIBuv/12vfzyy42Ojx07ViUlJXr22Wf1/e9/X6tWrdKHH36o2NjYFvncV155RT169FCfPn304osv6tSpU+7JprNnz9af//xn3XbbbXrssccUHx+vgoICLV++XP/93/+t4ODgJn/Oo48+qnnz5qlbt24aNGiQlixZory8PP3tb39r8jnat2+vhIQE/dd//Zc6dOigI0eO6PHHH2/0nuTkZLVr106rVq1Senq6IiIiZLVaW+Tzv+nw4cP6r//6L333u99VWlqa8vPzdeDAAd1xxx3NPmdbiYmJ0cyZM92r2pKTkzVv3jwFBQUF7D5G8G1ckQG8wPz587916b9Pnz569dVX9corr2jgwIHaunXrRVf0NMfChQu1cOFCDRw4UBs2bNB7772nxMRESXJfxaivr9cNN9ygAQMGaM6cOYqLi2s0H6cpfvrTn2ru3Ll65JFHNGDAAK1atUrvvfeeevTo0eRzBAUFafny5dq+fbv69++vhx9+WM8991yj94SEhOjll1/Wn/70J6WlpWnq1Kkt9vnfFBkZqX379unWW29Vz549de+992r27Nn68Y9/3OxztqUXXnhBmZmZuvnmmzVx4kSNHDlSffr0UUREhNnRAI9ZjG8OwgMAAkpVVZWuuuoqPf/887r77rvNjgN4hKElAAgwO3bs0L59+zR8+HCVl5dr/vz5kuS+igX4EooMAASg3/3ud8rPz1dYWJiGDBmiTz75xD20CPgShpYAAIDPYrIvAADwWRQZAADgsygyAADAZ1FkAACAz6LIAAAAn0WRAQAAPosiAwAAfBZFBgAA+Kz/H3HL/BzyS307AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from ase.io import read\n", + "from matscipy.rings import ring_statistics\n", + "\n", + "# Load a disordered configuration (amorphous silicon)\n", + "a = read('../../tests/aSi.cfg')\n", + "\n", + "# Identify rings\n", + "r = ring_statistics(a, cutoff=3.0, maxlength=6)\n", + "\n", + "# Plot statistics\n", + "plt.plot(r)\n", + "plt.xlabel('Number of atoms in ring')\n", + "plt.ylabel('Number of rings')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.10.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/pressure_coupling/equilibrate_pressure.py b/examples/pressure_coupling/equilibrate_pressure.py deleted file mode 100644 index 2f6e3cfe..00000000 --- a/examples/pressure_coupling/equilibrate_pressure.py +++ /dev/null @@ -1,93 +0,0 @@ -# -# Copyright 2021 Lars Pastewka (U. Freiburg) -# 2020, 2022 Thomas Reichenbach (Fraunhofer IWM) -# -# matscipy - Materials science with Python at the atomic-scale -# https://github.com/libAtoms/matscipy -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -from ase.io import Trajectory -from ase.units import GPa, fs -import numpy as np -from ase.md.langevin import Langevin -from ase.md.velocitydistribution import MaxwellBoltzmannDistribution -from matscipy import pressurecoupling as pc -from io import open - -# Parameters -dt = 1.0 * fs # MD time step -C11 = 500.0 * GPa # material constant -M_factor = 1.0 # scaling factor for lid mass during equilibration, -# 1.0 will give fast equilibration for expensive calculators - -Pdir = 2 # index of cell axis along normal pressure is applied -P = 5.0 * GPa # target normal pressure -v = 0.0 # no sliding yet, only apply pressure -vdir = 0 # index of cell axis along sliding happens -T = 300.0 # target temperature for thermostat -# thermostat is applied in the third direction which -# is neither pressure nor sliding direction and only -# in the middle region between top and bottom. -# This makes sense for small systems which cannot have -# a dedicated thermostat region. - -t_langevin = 75.0 * fs # time constant for Langevin thermostat -gamma_langevin = 1. / t_langevin # derived Langevin parameter -t_integrate = 1000.0 * fs # simulation time -steps_integrate = int(t_integrate / dt) # number of simulation steps - -atoms = ASE_ATOMS_OBJECT # put a specific system here -bottom_mask = BOOLEAN_NUMPY_ARRAY_TRUE_FOR_FIXED_BOTTOM_ATOMS # adjust -top_mask = BOOLEAN_NUMPY_ARRAY_TRUE_FOR_CONSTRAINT_TOP_ATOMS # adjust - -# save masks for sliding simulations or restart runs -np.savetxt("bottom_mask.txt", bottom_mask) -np.savetxt("top_mask.txt", top_mask) - -# set up calculation: -damp = pc.FixedMassCriticalDamping(C11, M_factor) -slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask, Pdir, - P, vdir, v, damp) -atoms.set_constraint(slider) -# if we start from local minimum, zero potential energy, -# use double temperature for faster temperature convergence in the beginning: -MaxwellBoltzmannDistribution(atoms, temperature_K=2 * T) -# clear momenta in constraint regions, otherwise lid might run away -atoms.arrays['momenta'][top_mask, :] = 0 -atoms.arrays['momenta'][bottom_mask, :] = 0 - -calc = ASE_CALCULATOR_OBJECT # put a specific calculator here - -atoms.set_calculator(calc) - -# only thermalize middle region in one direction -temps = np.zeros((len(atoms), 3)) -temps[slider.middle_mask, slider.Tdir] = T -gammas = np.zeros((len(atoms), 3)) -gammas[slider.middle_mask, slider.Tdir] = gamma_langevin - -integrator = Langevin(atoms, dt, temperature_K=temps, - friction=gammas, fixcm=False) -trajectory = Trajectory('equilibrate_pressure.traj', 'w', atoms) -log_handle = open('log_equilibrate.txt', - 'w', 1, encoding='utf-8') # 1 means line buffered -logger = pc.SlideLogger(log_handle, atoms, slider, integrator) -# log can be read using pc.SlideLog (see docstring there) -logger.write_header() -integrator.attach(logger) -integrator.attach(trajectory) -integrator.run(steps_integrate) -log_handle.close() -trajectory.close() diff --git a/examples/pressure_coupling/plot_slidelog b/examples/pressure_coupling/plot_slidelog deleted file mode 100644 index 7a39e043..00000000 --- a/examples/pressure_coupling/plot_slidelog +++ /dev/null @@ -1,106 +0,0 @@ -# -# Copyright 2021 Lars Pastewka (U. Freiburg) -# 2020, 2022 Thomas Reichenbach (Fraunhofer IWM) -# -# matscipy - Materials science with Python at the atomic-scale -# https://github.com/libAtoms/matscipy -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -from matplotlib import pyplot as pp -from matscipy import pressurecoupling as pc -from scipy.ndimage import uniform_filter -from ase.units import fs -import sys -import numpy as np -from io import open - -if len(sys.argv) > 1: - fn = sys.argv[1] -else: - fn = 'log.txt' -if len(sys.argv) > 2: - dt_mean = float(sys.argv[2]) * fs -else: - dt_mean = 100.0 * fs -with open(fn, 'r', encoding='utf-8') as handle: - log = pc.SlideLog(handle) - -dt = (log.time[1] - log.time[0]) * fs - - -def cummean(a): - return np.cumsum(a) / np.arange(1, len(log.time) + 1) - - -def leftmwmean(a, time): - steps = int(np.around(time / dt)) - ret = uniform_filter(a, steps, mode="constant", - cval=0.0, origin=(steps - 1) // 2) - ret[:steps - 1] = np.nan - return ret - - -def plot(attrs, labels, dt_mean, ylabel): - pp.figure() - for attr, label in zip(attrs, labels): - pp.plot(log.time, getattr(log, attr), ':', label=label + ' current') - pp.gca().set_prop_cycle(None) - for attr, label in zip(attrs, labels): - pp.plot(log.time, cummean(getattr(log, attr)), - '--', label=label + ' last $t$ mean') - pp.gca().set_prop_cycle(None) - for attr, label in zip(attrs, labels): - pp.plot(log.time, leftmwmean(getattr(log, attr), dt_mean), - '-', label=label + ' last %.0f fs mean' % (dt_mean / fs, )) - pp.xlabel(r'$t / \mathrm{fs}$') - pp.ylabel(ylabel) - pp.legend(loc='upper left') - pp.tight_layout() - - -plot(['T_thermostat'], ['thermostat dof'], dt_mean, r'$T / \mathrm{K}$') -plot(['P_top', 'P_bottom'], ['top', 'bottom'], dt_mean, r'$P / \mathrm{GPa}$') -plot(['tau_top', 'tau_bottom'], ['top', 'bottom'], dt_mean, - r'$\tau / \mathrm{GPa}$') -plot(['h'], ['lid normal pos.'], dt_mean, r'$h / \mathrm{\AA}$') -plot(['v'], ['lid normal vel.'], dt_mean, - r'$v_{\mathrm{normal}} / \mathrm{\AA}\mathrm{fs}^{-1}$') -plot(['a'], ['lid normal acc.'], dt_mean, - r'$a_{\mathrm{normal}} / \mathrm{\AA}\mathrm{fs}^{-2}$') - -# friction coefficient -pp.figure() -pp.plot(log.time, cummean(-log.tau_top) / cummean(log.P_top), - '--', label='top last $t$ mean') -pp.plot(log.time, cummean(-log.tau_bottom) / cummean(log.P_bottom), - '--', label='bottom last $t$ mean') -pp.gca().set_prop_cycle(None) -pp.plot(log.time, leftmwmean(-log.tau_top, - dt_mean) / leftmwmean(log.P_top, dt_mean), - '-', label='top last %.0f fs mean' % (dt_mean / fs, )) -pp.plot(log.time, leftmwmean(-log.tau_bottom, - dt_mean) / leftmwmean(log.P_bottom, dt_mean), - '-', label='bottom last %.0f fs mean' % (dt_mean / fs, )) -pp.xlabel(r'$t / \mathrm{fs}$') -pp.ylabel(r'$\mu$') -pp.legend(loc='upper left') -pp.tight_layout() - -tau_mean = 0.5 * (leftmwmean(-log.tau_top, dt_mean)[-1] - + leftmwmean(log.tau_bottom, dt_mean)[-1]) -print("Final tau last %.0f fs mean top/bottom: %.1f GPa" - % (dt_mean / fs, tau_mean)) - -pp.show() diff --git a/examples/pressure_coupling/restart_equilibrate_pressure.py b/examples/pressure_coupling/restart_equilibrate_pressure.py deleted file mode 100644 index c98f1c3c..00000000 --- a/examples/pressure_coupling/restart_equilibrate_pressure.py +++ /dev/null @@ -1,82 +0,0 @@ -# -# Copyright 2021 Lars Pastewka (U. Freiburg) -# 2020, 2022 Thomas Reichenbach (Fraunhofer IWM) -# -# matscipy - Materials science with Python at the atomic-scale -# https://github.com/libAtoms/matscipy -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -from ase.io import Trajectory, read -from ase.units import GPa, fs -import numpy as np -from ase.md.langevin import Langevin -from matscipy import pressurecoupling as pc -from io import open - -# Parameters -dt = 1.0 * fs # MD time step -C11 = 500.0 * GPa # material constant -M_factor = 1.0 # scaling factor for lid mass during equilibration -# 1.0 will give fast equilibration for expensive -# calculators - -Pdir = 2 # index of cell axis along normal pressure is applied -P = 5.0 * GPa # target normal pressure -v = 0.0 # no sliding yet, only apply pressure -vdir = 0 # index of cell axis along sliding happens -T = 300.0 # target temperature for thermostat -# thermostat is applied in the third direction which -# is neither pressure nor sliding direction and only -# in the middle region between top and bottom. -# This makes sense for small systems which cannot have -# a dedicated thermostat region. - -t_langevin = 75.0 * fs # time constant for Langevin thermostat -gamma_langevin = 1. / t_langevin # derived Langevin parameter -t_integrate = 1000.0 * fs # simulation time -steps_integrate = int(t_integrate / dt) # number of simulation steps - -# get atoms from trajectory to also initialize correct velocities -atoms = read('equilibrate_pressure.traj') - -bottom_mask = np.loadtxt("bottom_mask.txt").astype(bool) -top_mask = np.loadtxt("top_mask.txt").astype(bool) - -damp = pc.FixedMassCriticalDamping(C11, M_factor) -slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask, - Pdir, P, vdir, v, damp) -atoms.set_constraint(slider) - -calc = ASE_CALCULATOR_OBJECT # put a specific calculator here - -atoms.set_calculator(calc) -temps = np.zeros((len(atoms), 3)) -temps[slider.middle_mask, slider.Tdir] = T -gammas = np.zeros((len(atoms), 3)) -gammas[slider.middle_mask, slider.Tdir] = gamma_langevin -integrator = Langevin(atoms, dt, temperature_K=temps, - friction=gammas, fixcm=False) -trajectory = Trajectory('equilibrate_pressure.traj', 'a', atoms) # append -with open('log_equilibrate.txt', 'r', encoding='utf-8') as log_handle: - step_offset = pc.SlideLog(log_handle).step[-1] -log_handle = open('log_equilibrate.txt', - 'a', 1, encoding='utf-8') # line buffered append -logger = pc.SlideLogger(log_handle, atoms, slider, integrator, step_offset) - -integrator.attach(logger) -integrator.attach(trajectory) -integrator.run(steps_integrate) -log_handle.close() -trajectory.close() diff --git a/examples/pressure_coupling/restart_slide.py b/examples/pressure_coupling/restart_slide.py deleted file mode 100644 index a6eb4f50..00000000 --- a/examples/pressure_coupling/restart_slide.py +++ /dev/null @@ -1,81 +0,0 @@ -# -# Copyright 2021 Lars Pastewka (U. Freiburg) -# 2020, 2022 Thomas Reichenbach (Fraunhofer IWM) -# -# matscipy - Materials science with Python at the atomic-scale -# https://github.com/libAtoms/matscipy -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -from ase.io import Trajectory, read -from ase.units import GPa, fs, m, s -import numpy as np -from ase.md.langevin import Langevin -from matscipy import pressurecoupling as pc -from io import open - - -# Parameters -dt = 1.0 * fs # MD time step -C11 = 500.0 * GPa # material constant -p_c = 0.10 # experience value -Pdir = 2 # index of cell axis along normal pressure is applied -P = 5.0 * GPa # target normal pressure -v = 100.0 * m / s # constant sliding speed -vdir = 0 # index of cell axis along sliding happens -T = 300.0 # target temperature for thermostat -# thermostat is applied in the third direction which -# is neither pressure nor sliding direction and only -# in the middle region between top and bottom. -# This makes sense for small systems which cannot have -# a dedicated thermostat region. - -t_langevin = 75.0 * fs # time constant for Langevin thermostat -gamma_langevin = 1. / t_langevin # derived Langevin parameter -t_integrate = 1000.0 * fs # simulation time -steps_integrate = int(t_integrate / dt) # number of simulation steps - -# get atoms from trajectory to also initialize correct velocities -atoms = read('slide.traj') - -bottom_mask = np.loadtxt("bottom_mask.txt").astype(bool) -top_mask = np.loadtxt("top_mask.txt").astype(bool) - -damp = pc.AutoDamping(C11, p_c) -slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask, - Pdir, P, vdir, v, damp) -atoms.set_constraint(slider) - -calc = ASE_CALCULATOR_OBJECT # put a specific calculator here - -atoms.set_calculator(calc) -temps = np.zeros((len(atoms), 3)) -temps[slider.middle_mask, slider.Tdir] = T -gammas = np.zeros((len(atoms), 3)) -gammas[slider.middle_mask, slider.Tdir] = gamma_langevin -integrator = Langevin(atoms, dt, temperature_K=temps, - friction=gammas, fixcm=False) -trajectory = Trajectory('slide.traj', 'a', atoms) # append - -with open('log_slide.txt', 'r', encoding='utf-8') as log_handle: - step_offset = pc.SlideLog(log_handle).step[-1] -log_handle = open('log_slide.txt', 'a', - 1, encoding='utf-8') # line buffered append -logger = pc.SlideLogger(log_handle, atoms, slider, integrator, step_offset) - -integrator.attach(logger) -integrator.attach(trajectory) -integrator.run(steps_integrate) -log_handle.close() -trajectory.close() diff --git a/examples/pressure_coupling/slide.py b/examples/pressure_coupling/slide.py deleted file mode 100644 index eb2ed48c..00000000 --- a/examples/pressure_coupling/slide.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# Copyright 2021 Lars Pastewka (U. Freiburg) -# 2020, 2022 Thomas Reichenbach (Fraunhofer IWM) -# -# matscipy - Materials science with Python at the atomic-scale -# https://github.com/libAtoms/matscipy -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -from ase.io import Trajectory, read -from ase.units import GPa, fs, m, s -import numpy as np -from ase.md.langevin import Langevin -from matscipy import pressurecoupling as pc -from io import open - -# Parameters -dt = 1.0 * fs # MD time step -C11 = 500.0 * GPa # material constant -p_c = 0.10 # experience value -Pdir = 2 # index of cell axis along normal pressure is applied -P = 5.0 * GPa # target normal pressure -v = 100.0 * m / s # constant sliding speed -vdir = 0 # index of cell axis along sliding happens -T = 300.0 # target temperature for thermostat -# thermostat is applied in the third direction which -# is neither pressure nor sliding direction and only -# in the middle region between top and bottom. -# This makes sense for small systems which cannot have -# a dedicated thermostat region. -t_langevin = 75.0 * fs # time constant for Langevin thermostat -gamma_langevin = 1. / t_langevin # derived Langevin parameter -t_integrate = 1000.0 * fs # simulation time -steps_integrate = int(t_integrate / dt) # number of simulation steps - - -# get atoms from trajectory to also initialize correct velocities -atoms = read('equilibrate_pressure.traj') - -bottom_mask = np.loadtxt("bottom_mask.txt").astype(bool) -top_mask = np.loadtxt("top_mask.txt").astype(bool) - -velocities = atoms.get_velocities() -velocities[top_mask, Pdir] = 0.0 -# large mass will run away with v from equilibration - -atoms.set_velocities(velocities) - -damp = pc.AutoDamping(C11, p_c) -slider = pc.SlideWithNormalPressureCuboidCell(top_mask, bottom_mask, - Pdir, P, vdir, v, damp) -atoms.set_constraint(slider) - -calc = ASE_CALCULATOR_OBJECT # put a specific calculator here - -atoms.set_calculator(calc) -temps = np.zeros((len(atoms), 3)) -temps[slider.middle_mask, slider.Tdir] = T -gammas = np.zeros((len(atoms), 3)) -gammas[slider.middle_mask, slider.Tdir] = gamma_langevin -integrator = Langevin(atoms, dt, temperature_K=temps, - friction=gammas, fixcm=False) -trajectory = Trajectory('slide.traj', 'w', atoms) -log_handle = open('log_slide.txt', 'w', 1, encoding='utf-8') # line buffered -logger = pc.SlideLogger(log_handle, atoms, slider, integrator) -# log can be read using pc.SlideLog (see docstring there) -logger.write_header() - -integrator.attach(logger) -integrator.attach(trajectory) -integrator.run(steps_integrate) -log_handle.close() -trajectory.close() diff --git a/generate b/generate new file mode 100644 index 00000000..d56d7f7d --- /dev/null +++ b/generate @@ -0,0 +1,146 @@ +#!/usr/bin/ruby + +# For an OO language, this is distinctly procedural. Should probably fix that. +require 'json' + +details = Hash.new({}) + +capture_params = [ + { :name => "title", :message => "Enter project name." }, + { :name => "url", :message => "Enter the URL of the project repository." }, + { :name => "description", :message => "Enter the (short) project description." }, + { :name => "license", :message => "Enter the license this software shared under. (hit enter to skip)\nFor example MIT, BSD, GPL v3.0, Apache 2.0" }, + { :name => "doi", :message => "Enter the DOI of the archived version of this code. (hit enter to skip)\nFor example http://dx.doi.org/10.6084/m9.figshare.828487" }, + { :name => "keywords", :message => "Enter keywords that should be associated with this project (hit enter to skip)\nComma-separated, for example: turkey, chicken, pot pie" }, + { :name => "version", :message => "Enter the version of your software (hit enter to skip)\nSEMVER preferred: http://semver.org e.g. v1.0.0" } +] + +puts "I'm going to try and help you prepare some things for your JOSS submission" +puts "If all goes well then we'll have a nice codemeta.json file soon..." +puts "" +puts "************************************" +puts "* First, some basic details *" +puts "************************************" +puts "" + +# Loop through the desired captures and print out for clarity +capture_params.each do |param| + puts param[:message] + print "> " + input = gets + + details[param[:name]] = input.chomp + + puts "" + puts "OK, your project has #{param[:name]}: #{input}" + puts "" +end + +puts "" +puts "************************************" +puts "* Experimental stuff *" +puts "************************************" +puts "" + +puts "Would you like me to try and build a list of authors for you?" +puts "(You need to be running this script in a git repository for this to work)" +print "> (Y/N)" +answer = gets.chomp + +case answer.downcase +when "y", "yes" + + # Use git shortlog to extract a list of author names and commit counts. + # Note we don't extract emails here as there's often different emails for + # each user. Instead we capture emails at the end. + + git_log = `git shortlog --summary --numbered --no-merges` + + # ["252\tMichael Jackson", "151\tMC Hammer"] + authors_and_counts = git_log.split("\n").map(&:strip) + + authors_and_counts.each do |author_count| + count, author = author_count.split("\t").map(&:strip) + + puts "Looks like #{author} made #{count} commits" + puts "Add them to the output?" + print "> (Y/N)" + answer = gets.chomp + + # If a user chooses to add this author to the output then we ask for some + # additional information including their email, ORCID and affiliation. + case answer.downcase + when "y", "yes" + puts "What is #{author}'s email address? (hit enter to skip)" + print "> " + email = gets.chomp + + puts "What is #{author}'s ORCID? (hit enter to skip)" + puts "For example: http://orcid.org/0000-0000-0000-0000" + print "> " + orcid = gets.chomp + + puts "What is #{author}'s affiliation? (hit enter to skip)" + print "> " + affiliation = gets.chomp + + + details['authors'].merge!(author => { 'commits' => count, + 'email' => email, + 'orcid' => orcid, + 'affiliation' => affiliation }) + + when "n", "no" + puts "OK boss..." + puts "" + end + end +when "n", "no" + puts "OK boss..." + puts "" +end + +puts "Reticulating splines" + +5.times do + print "." + sleep 0.5 +end + +puts "" +puts "Generating some JSON goodness..." + +# TODO: work out how to use some kind of JSON template here. +# Build the output list of authors from the inputs we've collected. +output_authors = [] + +details['authors'].each do |author_name, values| + entry = { + "@id" => values['orcid'], + "@type" => "Person", + "email" => values['email'], + "name" => author_name, + "affiliation" => values['affiliation'] + } + output_authors << entry +end + +# TODO: this is currently a static template (written out here). It would be good +# to do something smarter here. +output = { + "@context" => "https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld", + "@type" => "Code", + "author" => output_authors, + "identifier" => details['doi'], + "codeRepository" => details['url'], + "datePublished" => Time.now.strftime("%Y-%m-%d"), + "dateModified" => Time.now.strftime("%Y-%m-%d"), + "dateCreated" => Time.now.strftime("%Y-%m-%d"), + "description" => details['description'], + "keywords" => details['keywords'], + "license" => details['license'], + "title" => details['title'], + "version" => details['version'] +} + +File.open('codemeta.json', 'w') {|f| f.write(JSON.pretty_generate(output)) } diff --git a/matscipy/calculators/manybody/calculator.py b/matscipy/calculators/manybody/calculator.py index 1727dd6a..d1d98476 100644 --- a/matscipy/calculators/manybody/calculator.py +++ b/matscipy/calculators/manybody/calculator.py @@ -78,6 +78,7 @@ class Manybody(MatscipyCalculator): 'stress', 'forces', 'hessian', + 'born_constants', 'nonaffine_forces', 'birch_coefficients', 'dynamical_matrix', diff --git a/matscipy/calculators/mcfm/__init__.py b/matscipy/calculators/mcfm/__init__.py index de0dd2fc..e8e469b3 100644 --- a/matscipy/calculators/mcfm/__init__.py +++ b/matscipy/calculators/mcfm/__init__.py @@ -19,4 +19,5 @@ # along with this program. If not, see . # -__all__ = [] +from .calculator import MultiClusterForceMixingPotential +__all__ = ["MultiClusterForceMixingPotential"] diff --git a/matscipy/cli/electrochemistry/README.md b/matscipy/cli/electrochemistry/README.md index 1054ac97..2c1d38fc 100644 --- a/matscipy/cli/electrochemistry/README.md +++ b/matscipy/cli/electrochemistry/README.md @@ -1,12 +1,8 @@ ### Content -* `c2d.py`: Command line interface to `continuous2discrete.py` * `pnp.py`: Command line interface to `poisson_boltzmann_distribution.py` +* `c2d.py`: Command line interface to `continuous2discrete.py` * `stericify.py`: Command line interface to `steric_correction.py` -The following assumes you have made these scripts available for execution, -i.e. by adding this directory to your `PATH` -or creating suitable symbolic links somewhere else. - ### Usage `pnp.py` (Poisson-Nernst-Planck), `c2d.py` (continuous2discrete) and `stericiy.py` executable scripts offer simple command line interfaces diff --git a/matscipy/dislocation.py b/matscipy/dislocation.py index 67b00e5f..95bf3f40 100644 --- a/matscipy/dislocation.py +++ b/matscipy/dislocation.py @@ -21,6 +21,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # + +"""Tools for studying structure and movement of dislocations.""" + import numpy as np from scipy.optimize import minimize diff --git a/matscipy/elasticity.py b/matscipy/elasticity.py index 426045d2..28c9667a 100644 --- a/matscipy/elasticity.py +++ b/matscipy/elasticity.py @@ -22,6 +22,10 @@ # along with this program. If not, see . # +""" +Computing elastic moduli of atomistic systems under small deformations. +""" + import itertools import warnings diff --git a/matscipy/electrochemistry/continuous2discrete.py b/matscipy/electrochemistry/continuous2discrete.py index 9c5314a1..ac174e6d 100644 --- a/matscipy/electrochemistry/continuous2discrete.py +++ b/matscipy/electrochemistry/continuous2discrete.py @@ -28,19 +28,21 @@ Johannes Hoermann Lukas Elflein """ -import logging, os, sys +import logging +import os import os.path +import sys from six.moves import builtins from collections.abc import Iterable import numpy as np -# import matplotlib.pyplot as plt import scipy.constants as sc from scipy import integrate, optimize logger = logging.getLogger(__name__) + def exponential(x, rate=0.1): """Exponential distribution.""" return rate * np.exp(-1 * rate * x) @@ -50,6 +52,7 @@ def uniform(x, *args, **kwargs): """Uniform distribution.""" return np.ones(np.array(x).shape) / 2 + def pdf_to_cdf(pdf): """Transform partial distribution to cumulative distribution function @@ -61,6 +64,7 @@ def pdf_to_cdf(pdf): return cdf + def get_nearest_pos(array, value): """Find the value of an array clostest to the second argument. @@ -88,6 +92,7 @@ def get_histogram(struc, box, n_bins=100): histograms += [(hist, bins)] return histograms + def quartile_function(distribution, p, support=None): """Inverts a distribution x->p, and returns the x-value belonging to the provided p. @@ -96,38 +101,40 @@ def quartile_function(distribution, p, support=None): Parameters ---------- - distribution: a function x -> p; x should be approximatable by a compact support - p: an output of the distribution function, probablities in (0,1) are preferrable + distribution: a function x -> p; x should be approximable by a compact support + p: an output of the distribution function, probabilities in (0,1) are preferable + support: interval to evaluate distribution function on, default: [0,1) in steps of 0.01 """ if support is None: # Define the x-values to evaluate the function on - support = np.arange(0,1,0.01) + support = np.arange(0, 1, 0.01) # Calculate the histogram of the distribution hist = distribution(support) - # Sum the distribution to get the cumulatative distribution - cdf = pdf_to_cdf(hist) + # Sum the distribution to get the cumulative distribution + cdf = pdf_to_cdf(hist) # If the p is not in the image of the support, get the nearest hit instead nearest_pos = get_nearest_pos(cdf, p) - # Get the x-value belonging to the probablity value provided in the input + # Get the x-value belonging to the probability value provided in the input x = support[nearest_pos] return x + def inversion_sampler(distribution, support): """Wrapper for quartile_function.""" # z is distributed according to the given distribution - # To approximate this, we insert an atom with probablity dis(z) at place z. - # This we do by inverting the distribution, and sampling uniformely from distri^-1: + # To approximate this, we insert an atom with probability dis(z) at place z. + # This we do by inverting the distribution, and sampling uniformly from distri^-1: p = np.random.uniform() sample = quartile_function(distribution, p, support=support) return sample -def rejection_sampler(distribution, support=(0.0,1.0), max_tries=10000, scale_M=1.1): +def rejection_sampler(distribution, support=(0.0, 1.0), max_tries=10000, scale_M=1.1): """Sample distribution by drawing from support and keeping according to distribution. Draw a random sample from our support, and keep it if another random number is @@ -138,10 +145,10 @@ def rejection_sampler(distribution, support=(0.0,1.0), max_tries=10000, scale_M= Parameters ---------- distribution: callable(x) - target distribut10on + target distribution support: list or 2-tuple either discrete list of locations in space where our distribution is - defined, or 2-tuple defining conitnuous support interval + defined, or 2-tuple defining continuous support interval max_tries: how often the sampler should attempt to draw before giving up. If the distribution is very sparse, increase this parameter to still get results. scale_M: float, optional @@ -149,7 +156,7 @@ def rejection_sampler(distribution, support=(0.0,1.0), max_tries=10000, scale_M= Returns ------- - sample: a location which is conistent (in expectation) with being drawn from the distribution. + sample: a location which is consistent (in expectation) with being drawn from the distribution. """ # rejection sampling (https://en.wikipedia.org/wiki/Rejection_sampling): @@ -169,38 +176,40 @@ def rejection_sampler(distribution, support=(0.0,1.0), max_tries=10000, scale_M= logger.debug("Rejection sampler on distribution f(x) ({}) with".format( distribution)) - # coninuous support case - if isinstance(support,tuple) and len(support) == 2: + # continuous support case + if isinstance(support, tuple) and len(support) == 2: a = support[0] b = support[1] - logger.debug("continuous support X (interval [{},{}]".format(a,b)) + logger.debug("continuous support X (interval [{},{}]".format(a, b)) # uniform probability density g(x) on support is - g = 1 / ( b - a ) + g = 1 / (b - a) # find maximum value fmax on distribution at x0 - xatol = (b - a)*1e-6 # optimization absolute tolerance - x0 = optimize.minimize_scalar( lambda x: -distribution(x), - bounds=(a,b), method='bounded', options={'xatol':xatol}).x + xatol = (b - a)*1e-6 # optimization absolute tolerance + x0 = optimize.minimize_scalar(lambda x: -distribution(x), + bounds=(a, b), + method='bounded', + options={'xatol': xatol}).x fmax = distribution(x0) M = scale_M*fmax / g logger.debug("Uniform probability density g(x) = {:g} and".format(g)) logger.debug("maximum probability density f(x0) = {:g} at x0 = {:g}".format(fmax, x0)) logger.debug("require M >= scale_M*g(x)/max(f(x)), i.e. M = {:g}.".format(M)) - for i in range(max_tries): + for _ in range(max_tries): # draw a sample from a uniformly distributed support sample = np.random.random() * (b-a) + a # Generate random float in the half-open interval [0.0, 1.0) and . - # keep sample with probablity of distribution + # keep sample with probability of distribution if np.random.random() < distribution(sample) / (M*g): return sample - else: # discrete support case + else: # discrete support case logger.debug("discrete support X ({:d} points in interval [{},{}]".format( len(support), np.min(support), np.max(support))) # uniform probability density g(x) on support is - g = 1.0 / len(support) # for discrete support - # maximum probability on distributiom f(x) is + g = 1.0 / len(support) # for discrete support + # maximum probability on distribution f(x) is fmax = np.max(distribution(support)) # thus M must be at least M = scale_M * fmax / g @@ -208,39 +217,37 @@ def rejection_sampler(distribution, support=(0.0,1.0), max_tries=10000, scale_M= logger.debug("maximum probability max(f(x)) = {:g} require".format(fmax)) logger.debug("M >= scale_M*g(x)/max(f(x)), i.e. M = {:g}.".format(M)) - for i in range(max_tries): + for _ in range(max_tries): # draw a sample from support sample = np.random.choice(support) # Generate random float in the half-open interval [0.0, 1.0) and . - # keep sample with probablity of distribution + # keep sample with probability of distribution if np.random.random() < distribution(sample) / (M*g): return sample raise RuntimeError('Maximum of attempts max_tries {} exceeded!'.format(max_tries)) -def generate_structure( - distribution, box=np.array([50, 50, 100]), - count=100, n_gridpoints=np.nan): - """Generate 'atoms' from continuous distributuion(s). +def generate_structure(distribution, box=np.array([50, 50, 100]), count=100, n_gridpoints=np.nan): + """Generate 'atoms' from continuous distribution(s). Coordinates are distributed according to given distributions. - Per default, X and Y coordinates are drawn uniformely. + Per default, X and Y coordinates are drawn uniformly. Parameters ---------- distribution: func(x) or list of func(x) - With one function, uniform sampling appplies along x and y axes, + With one function, uniform sampling applies along x and y axes, while applying 'distribution' along z axis. With a list of functions, - apllies the respective distribution function along x, y and z direction. + applies the respective distribution function along x, y and z direction. box: np.ndarray(3), optional (default: np.array([50, 50, 100]) ) dimensions of volume to be filled with samples count: int, optional (default: 100) number of samples to draw n_gridpoints: int or (int,int,int), optional (default: np.nan) - If spcefified, samples are not placed arbitrarily, but on an evenly spaced + If specified, samples are not placed arbitrarily, but on an evenly spaced grid of this many grid points along each axis. Specify np.nan for continuous sampling, i.e. (10,np.nan,20) @@ -254,18 +261,17 @@ def generate_structure( logger.info("Using uniform distribution along x and y direction.") logger.info("Using distribution {} along z direction.".format( distribution)) - distribution = [ uniform, uniform, distribution] + distribution = [uniform, uniform, distribution] for d in distribution: assert callable(d), "distribution {} must be callable".format(d) assert np.array(box).shape == (3,), "wrong specification of 3d box dimensions" - #if isinstance(n_gridpoints,int) or n_gridpoints == np.nan: if not isinstance(n_gridpoints, Iterable): - n_gridpoints = 3*[n_gridpoints] # to list + n_gridpoints = 3*[n_gridpoints] # to list - n_gridpoints = np.array(n_gridpoints,dtype=float) + n_gridpoints = np.array(n_gridpoints, dtype=float) logger.info("Using {} grid as sampling support.".format( n_gridpoints)) @@ -277,22 +283,22 @@ def generate_structure( for k, d in enumerate(distribution): # Using the box parameter, we construct a grid inside the box # This results in a 100x100x100 grid: - if np.isnan( n_gridpoints[k] ): # continuous support - support.append((0,box[k])) # interval + if np.isnan(n_gridpoints[k]): # continuous support + support.append((0, box[k])) # interval # Normalization constant: Z, _ = integrate.quad(d, support[-1][0], support[-1][1]) - else : # discrete supoport + else: # discrete support support.append(np.linspace(0, box[k], n_gridpoints[k])) Z = np.sum(d(support[-1])) # Normalization constant - logger.info("Normalizing 'distribution' {} by {}.".format(d,Z)) + logger.info("Normalizing 'distribution' {} by {}.".format(d, Z)) normalized_distribution.append( - lambda x,k=k,Z=Z: distribution[k](x) / Z ) + lambda x, k=k, Z=Z: distribution[k](x) / Z) # For every atom, draw random x, y and z coordinates - positions = np.array( [ [ - rejection_sampler(d,s) for d,s in zip(normalized_distribution,support) ] - for i in range(int(count)) ]) + positions = np.array([[ + rejection_sampler(d, s) for d, s in zip(normalized_distribution, support)] + for _ in range(int(count))]) logger.info("Drew {} samples from distributions.".format(positions.shape)) return positions diff --git a/matscipy/electrochemistry/poisson_boltzmann_distribution.py b/matscipy/electrochemistry/poisson_boltzmann_distribution.py index 94c5f3ab..415b4829 100644 --- a/matscipy/electrochemistry/poisson_boltzmann_distribution.py +++ b/matscipy/electrochemistry/poisson_boltzmann_distribution.py @@ -34,7 +34,8 @@ np.random.seed(74) -def ionic_strength(c,z): + +def ionic_strength(c, z): """Compute ionic strength from charges and concentrations Parameters @@ -50,14 +51,15 @@ def ionic_strength(c,z): ionic strength ( 1/2 * sum(z_i^2*c_i) ) [concentration unit, i.e. mol m^-3] """ - return 0.5*np.sum( np.square(z) * c ) + return 0.5*np.sum(np.square(z) * c) + def debye(c, z, - T=298.15, - relative_permittivity=79, - vacuum_permittivity=sc.epsilon_0, - R = sc.value('molar gas constant'), - F=sc.value('Faraday constant') ): + T=298.15, + relative_permittivity=79, + vacuum_permittivity=sc.epsilon_0, + R=sc.value('molar gas constant'), + F=sc.value('Faraday constant')): """Calculate the Debye length (in SI units per default). The Debye length indicates at which distance a charge will be screened off. @@ -84,11 +86,11 @@ def debye(c, z, lambda_D : float Debye length, sqrt( epsR*eps*R*T/(2*F^2*I) ) [m] """ - I = ionic_strength(c,z) + I = ionic_strength(c, z) return np.sqrt(relative_permittivity*vacuum_permittivity*R*T/(2.0*F**2*I)) -def gamma(u, T = 298.15): +def gamma(u, T=298.15): """Calculate term from Gouy-Chapmann theory. Parameters @@ -105,6 +107,7 @@ def gamma(u, T = 298.15): product = sc.value('Faraday constant') * u / (4 * sc.value('molar gas constant') * T) return np.tanh(product) + def potential(x, c, z, u, T=298.15, relative_permittivity=79): """The potential near a charged surface in an ionic solution. @@ -114,15 +117,19 @@ def potential(x, c, z, u, T=298.15, relative_permittivity=79): If only normal float precision is used, the potential is a step function. Steps in the potential result in unphysical particle concentrations. - Paramters + Parameters --------- x : (N,) ndarray z-distance from the surface [m] c : (M,) ndarray bulk concentrations of each ionic species [mol/m^3] + z : (M,) ndarray + charge number of each ionic species [1] + u: float + electrostatic potential at the metal/solution boundary in Volts, e.g. 0.05 [V] T : float - temperature of the soultion [Kelvin] (default: 298.15) - relative_permittivity: + temperature of the solution [Kelvin] (default: 298.15) + relative_permittivity: float relative permittivity of the ionic solution [] (default: 79) Returns @@ -130,26 +137,23 @@ def potential(x, c, z, u, T=298.15, relative_permittivity=79): phi: (N,) ndarray Electrostatic potential [V] """ - # Increase the precision of the calculation to 30 digits to ensure a smooth potential - #decimal.getcontext().prec = 30 - # Calculate the term in front of the log, containing a bunch of constants prefactor = 2 * sc.value('molar gas constant') * T / sc.value('Faraday constant') # For the later calculations we need the debye length - debye_value = debye(c, z, T, relative_permittivity) + debye_value = debye(c, z, T, relative_permittivity) kappa = 1 / debye_value # We also need to evaluate the gamma function - gamma_value = gamma(u, T) + gamma_value = gamma(u, T) # The e^{-kz} term exponential = np.exp(-kappa * x) # The fraction inside the log numerator = 1.0 + gamma_value * exponential - divisor = 1.0 - gamma_value * exponential + divisor = 1.0 - gamma_value * exponential # This is the complete term for the potential phi = prefactor * np.log(numerator / divisor) @@ -160,8 +164,7 @@ def potential(x, c, z, u, T=298.15, relative_permittivity=79): return phi -def concentration(x, c, z, u, T=298.15, - relative_permittivity=79): +def concentration(x, c, z, u, T=298.15, relative_permittivity=79): """The concentration of ions near a charged surface. Calculates the molar concentration of ions of a species, at a distance x @@ -178,27 +181,29 @@ def concentration(x, c, z, u, T=298.15, z : (M,) ndarray number charge of each ionic species [1] u : float - eletrostatic potential at the metal/liquid interface against bulk [V] + electrostatic potential at the metal/liquid interface against bulk [V] T : float temperature of the solution [K] (default: 298.15) relative_permittivity : float relative permittivity of the ionic solution, 80 for water [1] - Returns: + Returns + ------- c : (M,N) ndarray molar concentrations of ion species [mol/m^3] """ # Evaluate the potential at the current location potential_value = potential(x, c, z, u, T, relative_permittivity) - phi_z = np.outer(potential_value,np.array(z))# N x M matrix (rows, cols) + phi_z = np.outer(potential_value, np.array(z)) # N x M matrix (rows, cols) f = sc.value('Faraday constant') / (sc.value('molar gas constant') * T) # The concentration is an exponential function of the potential - C = np.exp( - f * phi_z ) + C = np.exp(- f * phi_z) # The concentration is scaled relative to the bulk concentration C *= c - return C.T # M x N matrix (rows, cols) + return C.T # M x N matrix (rows, cols) + def charge_density(x, c, z, u, T=298.15, relative_permittivity=79): """ @@ -212,19 +217,19 @@ def charge_density(x, c, z, u, T=298.15, relative_permittivity=79): z : (M,) ndarray number charge of each ionic species [1] u : float - eletrostatic potential at the metal/liquid interface against bulk [V] + electrostatic potential at the metal/liquid interface against bulk [V] T : float temperature of the solution [K] (default: 298.15) relative_permittivity : float relative permittivity of the ionic solution, 80 for water [1] - Returns: + Returns + ------- c : (N,) ndarray charge density [C/m^3] """ - C = concentration(x,c,z,u,T,relative_permittivity) - return sc.value("Faraday constant") * np.sum(C.T*z,axis=1) - + C = concentration(x, c, z, u, T, relative_permittivity) + return sc.value("Faraday constant") * np.sum(C.T*z, axis=1) def test(): diff --git a/matscipy/electrochemistry/poisson_nernst_planck_solver.py b/matscipy/electrochemistry/poisson_nernst_planck_solver.py index a5ee68c6..53bb9505 100644 --- a/matscipy/electrochemistry/poisson_nernst_planck_solver.py +++ b/matscipy/electrochemistry/poisson_nernst_planck_solver.py @@ -34,16 +34,15 @@ logger = logging.getLogger(__name__) -# Druecke nicht-linearen Teil der Transportgleichung (genauer, des Flusses) ueber -# Bernoulli-Funktionen +# For the employed controlled volume scheme, we express the transport +# equation's nonlinear part by the Bernoulli function # # $$ B(x) = \frac{x}{\exp(x)-1} $$ # -# aus. Damit wir in der Naehe von 0 nicht "in die Bredouille geraten", verwenden -# wir hier lieber die Taylorentwicklung. In der Literatur (Selbherr, S. Analysis -# and Simulation of Semiconductor Devices, Spriger 1984) wird eine noch -# aufwendigere stueckweise Definition empfohlen, allerdings werden wir im -# Folgenden sehen, dass unser Ansatz fuer dieses stationaere Problem genuegt. +# and use its Taylor expansion in the vicinity of zero for numeric stability. +# Selbherr, S. Analysis and Simulation of Semiconductor Devices, Springer 1984 +# employs a more complex piecewise definition, but for the stationary problem +# our approach suffices. def B(x): @@ -76,7 +75,6 @@ def jacobian(f, x0, dx=np.NaN): convention: F_ij = dfidxj, where i are array rows, j are array columns """ - N = len(x0) # choose step as small as possible if np.isnan(dx).any(): @@ -87,7 +85,7 @@ def jacobian(f, x0, dx=np.NaN): if np.isscalar(dx): dx = np.ones(N) * dx - F = np.zeros((N,N)) # Jacobian Fij + F = np.zeros((N, N)) # Jacobian Fij # convention: dfi_dxj # i are rows, j are columns @@ -95,10 +93,11 @@ def jacobian(f, x0, dx=np.NaN): dxj = np.zeros(N) dxj[j] = dx[j] - F[:,j] = (f(x0 + dxj) - f(x0 - dxj)) / (2.0*dxj[j]) + F[:, j] = (f(x0 + dxj) - f(x0 - dxj)) / (2.0*dxj[j]) return F + class PoissonNernstPlanckSystem: """Describes and solves a 1D Poisson-Nernst-Planck system""" @@ -114,19 +113,17 @@ def potential(self): @property def concentration(self): return np.where(self.nij > np.finfo('float64').resolution, - self.nij*self.c_unit, 0.0) + self.nij*self.c_unit, 0.0) @property def charge_density(self): - return np.sum(self.F * self.concentration.T * self.z,axis=1) + return np.sum(self.F * self.concentration.T * self.z, axis=1) @property def x1_scaled(self): return self.x0_scaled + self.L_scaled - #TODO: replace "didactic" Newton solver from IMTEK Simulation course with - # some standard package - def newton(self,f,xij,**kwargs): + def newton(self, f, xij, **kwargs): """Newton solver expects system f and initial value xij Parameters @@ -171,15 +168,13 @@ def newton(self,f,xij,**kwargs): self.logger.debug(' Jacobian ({}) rank {:d}'.format(J.shape, rank)) if rank < self.N: - self.logger.warn("Singular jacobian of rank" - + "{:d} < {:d} at step {:d}".format( - rank, self.N, i )) + self.logger.warn("Singular jacobian of rank {:d} < {:d} at step {:d}".format(rank, self.N, i)) break F = f(xij) invJ = np.linalg.inv(J) - dxij = np.dot( invJ, F ) + dxij = np.dot(invJ, F) delta = np.linalg.norm(dxij) delta_rel = delta / np.linalg.norm(xij) @@ -190,14 +185,14 @@ def newton(self,f,xij,**kwargs): normF = np.linalg.norm(F) self.logger.debug(' convergence norm(dxij), absolute {:> 8.4g}'.format(delta)) - self.logger.debug(' convergence norm(dxij), realtive {:> 8.4g}'.format(delta_rel)) + self.logger.debug(' convergence norm(dxij), relative {:> 8.4g}'.format(delta_rel)) self.logger.debug(' residual norm(F), absolute {:> 8.4g}'.format(normF)) self.convergenceStepAbsolute[i] = delta self.convergenceStepRelative[i] = delta_rel self.convergenceResidualAbsolute[i] = normF self.logger.info("Step {:4d}: norm(dx)/norm(x) = {:4.2e}, norm(dx) = {:4.2e}, norm(F) = {:4.2e}".format( - i, delta_rel, delta, normF) ) + i, delta_rel, delta, normF)) i += 1 @@ -221,11 +216,11 @@ def solver_callback(self, xij, *_): if self.callback_count == 0: logger.info( "{:>12s} {:>12s} {:>12s} {:>12s} {:>12s} {:>12s}".format( - "#callback","residual norm","abs dx norm", "rel dx norm", - "timing, step", "timing, tot.") ) - self.xij = [ self.xi0 ] + "#callback", "residual norm", "abs dx norm", "rel dx norm", + "timing, step", "timing, tot.")) + self.xij = [self.xi0] - self.converged = True # TODO remove (?) + self.converged = True # TODO remove (?) self.convergenceStepAbsolute = [] self.convergenceStepRelative = [] self.convergenceResidualAbsolute = [] @@ -253,12 +248,11 @@ def solver_callback(self, xij, *_): logger.info( "{:12d} {:12.5e} {:12.5e} {:12.5e} {:12.5e} {:12.5e}".format( - self.callback_count, norm_fj, delta , delta_rel, dt, dT)) + self.callback_count, norm_fj, delta, delta_rel, dt, dT)) self.callback_count += 1 return - def discretize(self): """Sets up discretization scheme and initial value""" # indices @@ -271,7 +265,7 @@ def discretize(self): 'grid points N', self.Ni, lwidth=self.label_width)) # discretization - self.dx = self.L_scaled / self.N # spatial step + self.dx = self.L_scaled / self.N # spatial step self.logger.info('{:<{lwidth}s} {:> 8.4g}'.format( 'dx', self.dx, lwidth=self.label_width)) @@ -279,7 +273,7 @@ def discretize(self): # positions (scaled) self.X = self.x0_scaled + I*self.dx - # Bounary & initial values + # Boundary & initial values # internally: # n: dimensionless concentrations @@ -291,35 +285,35 @@ def discretize(self): # Kronecker product, M rows (ion species), Ni cols (grid points), if self.ni0 is None: - self.ni0 = np.kron( self.c_scaled, np.ones((self.Ni,1)) ).T + self.ni0 = np.kron(self.c_scaled, np.ones((self.Ni, 1))).T if self.zi0 is None: - self.zi0 = np.kron( self.z, np.ones((self.Ni,1)) ).T # does not change + self.zi0 = np.kron(self.z, np.ones((self.Ni, 1))).T # does not change # self.initial_values() def initial_values(self): """ - Solves decoupled linear system to get inital potential distribution. + Solves decoupled linear system to get initial potential distribution. """ - zini0 = self.zi0*self.ni0 # z*n + zini0 = self.zi0*self.ni0 # z*n # shape: ion species (rows), grid points (cols), sum over ion species (along rows) rhoi0 = 0.5*zini0.sum(axis=0) # system matrix of spatial poisson equation - Au = np.zeros((self.Ni,self.Ni)) + Au = np.zeros((self.Ni, self.Ni)) bu = np.zeros(self.Ni) - Au[0,0] = 1 - Au[-1,-1] = 1 - for i in range(1,self.N): - Au[i,[i-1,i,i+1]] = [1.0, -2.0, 1.0] # 1D Laplace operator, 2nd order + Au[0, 0] = 1 + Au[-1, -1] = 1 + for i in range(1, self.N): + Au[i, [i-1, i, i+1]] = [1.0, -2.0, 1.0] # 1D Laplace operator, 2nd order - bu = rhoi0*self.dx**2 # => Poisson equation - bu[0] = self.u0 + bu = rhoi0*self.dx**2 # => Poisson equation + bu[0] = self.u0 bu[-1] = self.u1 # get initial potential distribution by solving Poisson equation - self.ui0 = np.dot( np.linalg.inv(Au), bu) # A u - b = 0 <=> u = A^-1 b + self.ui0 = np.dot(np.linalg.inv(Au), bu) # A u - b = 0 <=> u = A^-1 b return self.ui0 @@ -348,35 +342,38 @@ def solve(self): self.callback_count = 0 self.t0 = time.perf_counter() - self.tj = self.t0 # previosu callback timer value + self.tj = self.t0 # previous callback timer value # neat lecture on scipy optimizers # http://scipy-lectures.org/advanced/mathematical_optimization/ if isinstance(self.solver, str) and self.solver in [ - 'hybr','lm','broyden1','broyden2','anderson','linearmixing', - 'diagbroyden','excitingmixing','krylov','df-sane']: - res = scipy.optimize.root(self.G,self.xi0, - method=self.solver,callback=self.solver_callback, - options = self.options) + 'hybr', 'lm', 'broyden1', 'broyden2', 'anderson', 'linearmixing', + 'diagbroyden', 'excitingmixing', 'krylov', 'df-sane']: + res = scipy.optimize.root(self.G, self.xi0, + method=self.solver, + callback=self.solver_callback, + options=self.options) self.xij1 = res.x if not res.success: logger.warn(res.message) - elif isinstance( self.solver, str): + elif isinstance(self.solver, str): f = lambda x: np.linalg.norm(self.G(x)) - res = scipy.optimize.minimize(f,self.xi0.copy(), - method=self.solver,callback=self.solver_callback, - options=self.options) + res = scipy.optimize.minimize(f, self.xi0.copy(), + method=self.solver, + callback=self.solver_callback, + options=self.options) self.xij1 = res.x if not res.success: logger.warn(res.message) else: - self.xij1 = self.solver(self.G,self.xi0.copy(), - callback=self.solver_callback, options=self.options) + self.xij1 = self.solver(self.G, self.xi0.copy(), + callback=self.solver_callback, + options=self.options) # store results: - self.uij = self.xij1[:self.Ni] # potential - self.nij = self.xij1[self.Ni:(self.M+1)*self.Ni].reshape(self.M, self.Ni) # concentrations - self.lamj = self.xij1[(self.M+1)*self.Ni:] # Lagrange multipliers + self.uij = self.xij1[:self.Ni] # potential + self.nij = self.xij1[self.Ni:(self.M+1)*self.Ni].reshape(self.M, self.Ni) # concentrations + self.lamj = self.xij1[(self.M+1)*self.Ni:] # Lagrange multipliers return self.uij, self.nij, self.lamj @@ -388,23 +385,25 @@ def useStandardInterfaceBC(self): # Potential Dirichlet BC self.u0 = self.delta_u_scaled self.u1 = 0 - self.logger.info('Left hand side Dirichlet boundary condition: u0 = {:> 8.4g}'.format(self.u0)) - self.logger.info('Right hand side Dirichlet boundary condition: u1 = {:> 8.4g}'.format(self.u1)) + self.logger.info( + 'Left hand side Dirichlet boundary condition: u0 = {:> 8.4g}'.format(self.u0)) + self.logger.info( + 'Right hand side Dirichlet boundary condition: u1 = {:> 8.4g}'.format(self.u1)) self.boundary_conditions.extend([ - lambda x: self.leftPotentialDirichletBC(x,self.u0), - lambda x: self.rightPotentialDirichletBC(x,self.u1) ]) - # self.rightPotentialBC = lambda x: self.rightPotentialDirichletBC(x,self.u1) + lambda x: self.leftPotentialDirichletBC(x, self.u0), + lambda x: self.rightPotentialDirichletBC(x, self.u1)]) - #self.rightConcentrationBC = [] for k in range(self.M): - self.logger.info('Ion species {:02d} left hand side concentration Flux boundary condition: j0 = {:> 8.4g}'.format(k,0)) - self.logger.info('Ion species {:02d} right hand side concentration Dirichlet boundary condition: c1 = {:> 8.4g}'.format(k,self.c_scaled[k])) - self.boundary_conditions.extend( [ - lambda x, k=k: self.leftControlledVolumeSchemeFluxBC(x,k), - lambda x, k=k: self.rightDirichletBC(x,k,self.c_scaled[k]) ] ) - #self.rightConcentrationBC.append( - # lambda x, k=k: self.rightDirichletBC(x,k,self.c_scaled[k]) ) + self.logger.info( + 'Ion species {:02d} left hand side concentration Flux boundary condition: j0 = {:> 8.4g}'.format( + k,0)) + self.logger.info( + 'Ion species {:02d} right hand side concentration Dirichlet boundary condition: c1 = {:> 8.4g}'.format( + k, self.c_scaled[k])) + self.boundary_conditions.extend([ + lambda x, k=k: self.leftControlledVolumeSchemeFluxBC(x, k), + lambda x, k=k: self.rightDirichletBC(x, k, self.c_scaled[k])]) # counter-intuitive behavior of lambda in loop: # https://stackoverflow.com/questions/2295290/what-do-lambda-function-closures-capture # workaround: default parameter k=k @@ -421,21 +420,21 @@ def useStandardCellBC(self): self.logger.info('{:>{lwidth}s} u1 = {:< 8.4g}'.format( 'Right hand side Dirichlet boundary condition', self.u1, lwidth=self.label_width)) self.boundary_conditions.extend([ - lambda x: self.leftPotentialDirichletBC(x,self.u0), - lambda x: self.rightPotentialDirichletBC(x,self.u1) ]) + lambda x: self.leftPotentialDirichletBC(x, self.u0), + lambda x: self.rightPotentialDirichletBC(x, self.u1)]) - N0 = self.L_scaled*self.c_scaled # total amount of species in cell + N0 = self.L_scaled*self.c_scaled # total amount of species in cell for k in range(self.M): - self.logger.info('{:>{lwidth}s} j0 = {:<8.4g}'.format( - 'Ion species {:02d} left hand side concentration Flux boundary condition'.format(k), - 0.0, lwidth=self.label_width)) - self.logger.info('{:>{lwidth}s} N0 = {:<8.4g}'.format( - 'Ion species {:02d} number conservation constraint'.format(k), - N0[k], lwidth=self.label_width)) + self.logger.info('{:>{lwidth}s} j0 = {:<8.4g}'.format( + 'Ion species {:02d} left hand side concentration Flux boundary condition'.format(k), + 0.0, lwidth=self.label_width)) + self.logger.info('{:>{lwidth}s} N0 = {:<8.4g}'.format( + 'Ion species {:02d} number conservation constraint'.format(k), + N0[k], lwidth=self.label_width)) - self.boundary_conditions.extend( [ - lambda x, k=k: self.leftControlledVolumeSchemeFluxBC(x,k), - lambda x, k=k, N0=N0[k]: self.numberConservationConstraint(x,k,N0) ] ) + self.boundary_conditions.extend([ + lambda x, k=k: self.leftControlledVolumeSchemeFluxBC(x, k), + lambda x, k=k, N0=N0[k]: self.numberConservationConstraint(x, k, N0)]) def useSternLayerCellBC(self, implicit=False): """Interfaces at left hand side and right hand side, @@ -458,18 +457,18 @@ def useSternLayerCellBC(self, implicit=False): self.u0 = self.delta_u_scaled / 2.0 self.u1 = - self.delta_u_scaled / 2.0 - if implicit: # implicitly treat Stern layer via Robin BC + if implicit: # implicitly treat Stern layer via Robin BC self.logger.info('Implicitly treating Stern layer via Robin BC') self.logger.info('{:>{lwidth}s} u0 + lambda_S*dudx = {:< 8.4g}'.format( - 'Left hand side Robin boundary condition', self.u0, lwidth=self.label_width)) + 'Left hand side Robin boundary condition', self.u0, lwidth=self.label_width)) self.logger.info('{:>{lwidth}s} u1 + lambda_S*dudx = {:< 8.4g}'.format( - 'Right hand side Robin boundary condition', self.u1, lwidth=self.label_width)) + 'Right hand side Robin boundary condition', self.u1, lwidth=self.label_width)) self.boundary_conditions.extend([ - lambda x: self.leftPotentialRobinBC(x,self.lambda_S_scaled,self.u0), - lambda x: self.rightPotentialRobinBC(x,self.lambda_S_scaled,self.u1) ]) + lambda x: self.leftPotentialRobinBC(x, self.lambda_S_scaled, self.u0), + lambda x: self.rightPotentialRobinBC(x, self.lambda_S_scaled, self.u1)]) - else: # explicitly treat Stern layer via linear regime + else: # explicitly treat Stern layer via linear regime self.logger.info('Explicitly treating Stern layer as uniformly charged regions') # set left and right hand side outer Helmholtz plane @@ -477,26 +476,26 @@ def useSternLayerCellBC(self, implicit=False): self.rhs_ohp = self.x0_scaled + self.L_scaled - self.lambda_S_scaled self.logger.info('{:>{lwidth}s} u0 = {:< 8.4g}'.format( - 'Left hand side Dirichlet boundary condition', self.u0, lwidth=self.label_width)) + 'Left hand side Dirichlet boundary condition', self.u0, lwidth=self.label_width)) self.logger.info('{:>{lwidth}s} u1 = {:< 8.4g}'.format( - 'Right hand side Dirichlet boundary condition', self.u1, lwidth=self.label_width)) + 'Right hand side Dirichlet boundary condition', self.u1, lwidth=self.label_width)) self.boundary_conditions.extend([ - lambda x: self.leftPotentialDirichletBC(x,self.u0), - lambda x: self.rightPotentialDirichletBC(x,self.u1) ]) + lambda x: self.leftPotentialDirichletBC(x, self.u0), + lambda x: self.rightPotentialDirichletBC(x, self.u1)]) - N0 = self.L_scaled*self.c_scaled # total amount of species in cell + N0 = self.L_scaled*self.c_scaled # total amount of species in cell for k in range(self.M): - self.logger.info('{:>{lwidth}s} j0 = {:<8.4g}'.format( - 'Ion species {:02d} left hand side concentration Flux boundary condition'.format(k), - 0.0, lwidth=self.label_width)) - self.logger.info('{:>{lwidth}s} N0 = {:<8.4g}'.format( - 'Ion species {:02d} number conservation constraint'.format(k), - N0[k], lwidth=self.label_width)) + self.logger.info('{:>{lwidth}s} j0 = {:<8.4g}'.format( + 'Ion species {:02d} left hand side concentration Flux boundary condition'.format(k), + 0.0, lwidth=self.label_width)) + self.logger.info('{:>{lwidth}s} N0 = {:<8.4g}'.format( + 'Ion species {:02d} number conservation constraint'.format(k), + N0[k], lwidth=self.label_width)) - self.boundary_conditions.extend( [ - lambda x, k=k: self.leftControlledVolumeSchemeFluxBC(x,k), - lambda x, k=k, N0=N0[k]: self.numberConservationConstraint(x,k,N0) ] ) + self.boundary_conditions.extend([ + lambda x, k=k: self.leftControlledVolumeSchemeFluxBC(x, k), + lambda x, k=k, N0=N0[k]: self.numberConservationConstraint(x, k, N0)]) # TODO: meaningful test for Dirichlet BC def useStandardDirichletBC(self): @@ -506,23 +505,29 @@ def useStandardDirichletBC(self): self.u0 = self.delta_u_scaled self.u1 = 0 - self.logger.info('Left hand side potential Dirichlet boundary condition: u0 = {:> 8.4g}'.format(self.u0)) - self.logger.info('Right hand side potential Dirichlet boundary condition: u1 = {:> 8.4g}'.format(self.u1)) + self.logger.info( + 'Left hand side potential Dirichlet boundary condition: u0 = {:> 8.4g}'.format(self.u0)) + self.logger.info( + 'Right hand side potential Dirichlet boundary condition: u1 = {:> 8.4g}'.format(self.u1)) # set up boundary conditions - self.boundary_conditions.extend( [ - lambda x: self.leftPotentialDirichletBC(x,self.u0), - lambda x: self.rightPotentialDirichletBC(x,self.u1) ] ) + self.boundary_conditions.extend([ + lambda x: self.leftPotentialDirichletBC(x, self.u0), + lambda x: self.rightPotentialDirichletBC(x, self.u1)]) for k in range(self.M): - self.logger.info('Ion species {:02d} left hand side concentration Dirichlet boundary condition: c0 = {:> 8.4g}'.format(k,self.c_scaled[k])) - self.logger.info('Ion species {:02d} right hand side concentration Dirichlet boundary condition: c1 = {:> 8.4g}'.format(k,self.c_scaled[k])) - self.boundary_conditions.extend( [ - lambda x, k=k: self.leftDirichletBC(x,k,self.c_scaled[k]), - lambda x, k=k: self.rightDirichletBC(x,k,self.c_scaled[k]) ] ) + self.logger.info( + 'Ion species {:02d} left hand side concentration Dirichlet boundary condition: c0 = {:> 8.4g}'.format( + k, self.c_scaled[k])) + self.logger.info( + 'Ion species {:02d} right hand side concentration Dirichlet boundary condition: c1 = {:> 8.4g}'.format( + k, self.c_scaled[k])) + self.boundary_conditions.extend([ + lambda x, k=k: self.leftDirichletBC(x, k, self.c_scaled[k]), + lambda x, k=k: self.rightDirichletBC(x, k, self.c_scaled[k])]) # boundary conditions and constraints building blocks: - def leftFiniteDifferenceSchemeFluxBC(self,x,k,j0=0): + def leftFiniteDifferenceSchemeFluxBC(self, x, k, j0=0): """ Parameters ---------- @@ -543,17 +548,17 @@ def leftFiniteDifferenceSchemeFluxBC(self,x,k,j0=0): # df0dx = 1 / (2*dx) * (-3 f0 + 4 f1 - f2 ) + O(dx^2) # - dndx - z n dudx = j0 dndx = -3.0*nijk[0] + 4.0*nijk[1] - nijk[2] - dudx = -3.0*uij[0] + 4.0*uij[1] - uij[2] - bcval = - dndx - self.zi0[k,0]*nijk[0]*dudx - 2.0*self.dx*j0 + dudx = -3.0*uij[0] + 4.0*uij[1] - uij[2] + bcval = - dndx - self.zi0[k, 0]*nijk[0]*dudx - 2.0*self.dx*j0 self.logger.debug( 'Flux BC F[0] = - dndx - z n dudx - 2*dx*j0 = {:> 8.4g}'.format(bcval)) self.logger.debug( ' = - ({:.2f}) - ({:.0f})*{:.2f}*({:.2f}) - 2*{:.2f}*({:.2f})'.format( - dndx, self.zi0[k,0], nijk[0], dudx, self.dx, j0)) + dndx, self.zi0[k, 0], nijk[0], dudx, self.dx, j0)) return bcval - def rightFiniteDifferenceSchemeFluxBC(self,x,k,j0=0): + def rightFiniteDifferenceSchemeFluxBC(self, x, k, j0=0): """ See ```leftFiniteDifferenceSchemeFluxBC``` """ @@ -563,17 +568,17 @@ def rightFiniteDifferenceSchemeFluxBC(self,x,k,j0=0): # df0dx = 1 / (2*dx) * (-3 f0 + 4 f1 - f2 ) + O(dx^2) # - dndx - z n dudx = j0 dndx = 3.0*nijk[-1] - 4.0*nijk[-2] + nijk[-3] - dudx = 3.0*uij[-1] - 4.0*uij[-2] + uij[-3] - bcval = - dndx - self.zi0[k,-1]*nijk[-1]*dudx - 2.0*self.dx*j0 + dudx = 3.0*uij[-1] - 4.0*uij[-2] + uij[-3] + bcval = - dndx - self.zi0[k, -1]*nijk[-1]*dudx - 2.0*self.dx*j0 self.logger.debug( 'FD flux BC F[-1] = - dndx - z n dudx - 2*dx*j0 = {:> 8.4g}'.format(bcval)) self.logger.debug( ' = - {:.2f} - {:.0f}*{:.2f}*{:.2f} - 2*{:.2f}*{:.2f}'.format( - dndx, self.zi0[k,-1], nijk[-1], dudx, self.dx, j0)) + dndx, self.zi0[k, -1], nijk[-1], dudx, self.dx, j0)) return bcval - def leftControlledVolumeSchemeFluxBC(self,x,k,j0=0): + def leftControlledVolumeSchemeFluxBC(self, x, k, j0=0): """ Compute left hand side flux boundary condition residual in accord with controlled volume scheme. @@ -596,14 +601,14 @@ def leftControlledVolumeSchemeFluxBC(self,x,k,j0=0): # flux by controlled volume scheme: - bcval = ( + B(self.z[k]*(uij[0]-uij[1]))*nijk[1] - - B(self.z[k]*(uij[1]-uij[0]))*nijk[0] - self.dx*j0 ) + bcval = (+ B(self.z[k]*(uij[0]-uij[1]))*nijk[1] + - B(self.z[k]*(uij[1]-uij[0]))*nijk[0] - self.dx*j0) self.logger.debug( 'CV flux BC F[0] = n1*B(z(u0-u1)) - n0*B(z(u1-u0)) - j0*dx = {:> 8.4g}'.format(bcval)) return bcval - def rightControlledVolumeSchemeFluxBC(self,x,k,j0=0): + def rightControlledVolumeSchemeFluxBC(self, x, k, j0=0): """ Compute right hand side flux boundary condition residual in accord with controlled volume scheme. See ``leftControlledVolumeSchemeFluxBC`` @@ -613,32 +618,32 @@ def rightControlledVolumeSchemeFluxBC(self,x,k,j0=0): # flux by controlled volume scheme: - bcval = ( + B(self.z[k]*(uij[-2]-uij[-1]))*nijk[-1] - - B(self.z[k]*(uij[-1]-uij[-2]))*nijk[-2] - self.dx*j0 ) + bcval = (+ B(self.z[k]*(uij[-2]-uij[-1]))*nijk[-1] + - B(self.z[k]*(uij[-1]-uij[-2]))*nijk[-2] - self.dx*j0) self.logger.debug( 'CV flux BC F[-1] = n[-1]*B(z(u[-2]-u[-1])) - n[-2]*B(z(u[-1]-u[-2])) - j0*dx = {:> 8.4g}'.format(bcval)) return bcval - def leftPotentialDirichletBC(self,x,u0=0): - return self.leftDirichletBC(x,-1,u0) + def leftPotentialDirichletBC(self, x, u0=0): + return self.leftDirichletBC(x, -1, u0) - def leftDirichletBC(self,x,k,x0=0): + def leftDirichletBC(self, x, k, x0=0): """Construct Dirichlet BC at left boundary""" nijk = x[(k+1)*self.Ni:(k+2)*self.Ni] return nijk[0] - x0 - def rightPotentialDirichletBC(self,x,x0=0): - return self.rightDirichletBC(x,-1,x0) + def rightPotentialDirichletBC(self, x, x0=0): + return self.rightDirichletBC(x, -1, x0) - def rightDirichletBC(self,x,k,x0=0): + def rightDirichletBC(self, x, k, x0=0): nijk = x[(k+1)*self.Ni:(k+2)*self.Ni] return nijk[-1] - x0 - def leftPotentialRobinBC(self,x,lam,u0=0): - return self.leftRobinBC(x,-1,lam,u0) + def leftPotentialRobinBC(self, x, lam, u0=0): + return self.leftRobinBC(x, -1, lam, u0) - def leftRobinBC(self,x,k,lam,x0=0): + def leftRobinBC(self, x, k, lam, x0=0): """ Compute left hand side Robin (u + lam*dudx = u0 ) BC at in accord with 2nd order finite difference scheme. @@ -656,24 +661,24 @@ def leftRobinBC(self,x,k,lam,x0=0): and thus linear potential drop across the interface. x0 : float right hand side value of BC, corresponds to potential beyond Stern - layer if applied to poential variable in PNP system. + layer if applied to potential variable in PNP system. Returns ------- float: boundary condition residual """ nijk = x[(k+1)*self.Ni:(k+2)*self.Ni] - return nijk[0] + lam/(2*self.dx)* ( 3.0*nijk[0] - 4.0*nijk[1] + nijk[2] ) - x0 + return nijk[0] + lam/(2*self.dx) * (3.0*nijk[0] - 4.0*nijk[1] + nijk[2]) - x0 - def rightPotentialRobinBC(self,x,lam,u0=0): - return self.rightRobinBC(x,-1,lam,u0) + def rightPotentialRobinBC(self, x, lam, u0=0): + return self.rightRobinBC(x, -1, lam, u0) - def rightRobinBC(self,x,k,lam,x0=0): + def rightRobinBC(self, x, k, lam, x0=0): """Construct Robin (u + lam*dudx = u0 ) BC at right boundary.""" nijk = x[(k+1)*self.Ni:(k+2)*self.Ni] - return nijk[-1] + lam/(2*self.dx) * ( 3.0*nijk[-1] - 4.0*nijk[-2] + nijk[-3] ) - x0 + return nijk[-1] + lam/(2*self.dx) * (3.0*nijk[-1] - 4.0*nijk[-2] + nijk[-3]) - x0 - def numberConservationConstraint(self,x,k,N0): + def numberConservationConstraint(self, x, k, N0): """N0: total amount of species, k: ion species""" nijk = x[(k+1)*self.Ni:(k+2)*self.Ni] @@ -686,7 +691,7 @@ def numberConservationConstraint(self,x,k,N0): self.logger.debug( 'Number conservation constraint F(x) = N - N0 = {:.4g} - {:.4g} = {:.4g}'.format( - N, N0, constraint_val ) ) + N, N0, constraint_val)) return constraint_val # TODO: remove or standardize @@ -700,7 +705,7 @@ def numberConservationConstraint(self,x,k,N0): # return bcval # # def rightNeumannBC(self,x,j0): - # """Construct finite difference Neumann BC (flux BC) at right boundray""" + # """Construct finite difference Neumann BC (flux BC) at right boundary""" # # left hand side first derivative of second order error # # dfndx = 1 / (2*dx) * (+3 fn - 4 fn-1 + fn-2 ) + O(dx^2) = 0 # bcval = 3.0*x[-1] - 4.0*x[-2] + x[-3] - 2.0*self.dx*j0 @@ -709,33 +714,33 @@ def numberConservationConstraint(self,x,k,N0): # return bcval # standard Poisson equation residual for potential - def poisson_pde(self,x): - """Returns Poisson equation resiudal by applying 2nd order FD scheme""" + def poisson_pde(self, x): + """Returns Poisson equation residual by applying 2nd order FD scheme""" uij1 = x[:self.Ni] self.logger.debug( 'potential range [u_min, u_max] = [ {:>.4g}, {:>.4g} ]'.format( - np.min(uij1),np.max(uij1))) + np.min(uij1), np.max(uij1))) nij1 = x[self.Ni:(self.M+1)*self.Ni] - nijk1 = nij1.reshape( self.M, self.Ni ) + nijk1 = nij1.reshape(self.M, self.Ni) for k in range(self.M): - self.logger.debug( - 'ion species {:02d} concentration range [c_min, c_max] = [ {:>.4g}, {:>.4g} ]'.format( - k,np.min(nijk1[k,:]),np.max(nijk1[k,:]))) + self.logger.debug( + 'ion species {:02d} concentration range [c_min, c_max] = [ {:>.4g}, {:>.4g} ]'.format( + k, np.min(nijk1[k, :]), np.max(nijk1[k, :]))) # M rows (ion species), N_i cols (grid points) - zi0nijk1 = self.zi0*nijk1 # z_ik*n_ijk + zi0nijk1 = self.zi0*nijk1 # z_ik*n_ijk for k in range(self.M): - self.logger.debug( - 'ion species {:02d} charge range [z*c_min, z*c_max] = [ {:>.4g}, {:>.4g} ]'.format( - k,np.min(zi0nijk1[k,:]), np.max(zi0nijk1[k,:]))) + self.logger.debug( + 'ion species {:02d} charge range [z*c_min, z*c_max] = [ {:>.4g}, {:>.4g} ]'.format( + k, np.min(zi0nijk1[k, :]), np.max(zi0nijk1[k, :]))) # charge density sum_k=1^M (z_ik*n_ijk) rhoij1 = zi0nijk1.sum(axis=0) self.logger.debug( - 'charge density range [rho_min, rho_max] = [ {:>.4g}, {:>.4g} ]'.format( - np.min(rhoij1),np.max(rhoij1))) + 'charge density range [rho_min, rho_max] = [ {:>.4g}, {:>.4g} ]'.format( + np.min(rhoij1), np.max(rhoij1))) # reduced Poisson equation: d2udx2 = rho Fu = -(np.roll(uij1, -1)-2*uij1+np.roll(uij1, 1))-0.5*rhoij1*self.dx**2 @@ -746,29 +751,29 @@ def poisson_pde(self,x): if not np.isnan(self.lhs_ohp): lhs_linear_regime_ndx = (self.X <= self.lhs_ohp) - lhs_ohp_ndx = np.max( np.nonzero( lhs_linear_regime_ndx ) ) + lhs_ohp_ndx = np.max(np.nonzero(lhs_linear_regime_ndx)) self.logger.debug( - 'selected {:d} grid points within lhs OHP at grid point index {:d} with x_scaled <= {:>.4g}'.format( - np.count_nonzero(lhs_linear_regime_ndx), lhs_ohp_ndx, self.lhs_ohp) ) + 'selected {:d} grid points within lhs OHP at grid point index {:d} with x_scaled <= {:>.4g}'.format( + np.count_nonzero(lhs_linear_regime_ndx), lhs_ohp_ndx, self.lhs_ohp)) # dudx = (u[ohp]-u[0])/lambda_S within Stern layer Fu[lhs_linear_regime_ndx] = ( - ( np.roll(uij1,-1) - uij1 )[lhs_linear_regime_ndx] - * self.lambda_S_scaled - (uij1[lhs_ohp_ndx]-uij1[0])*self.dx ) + (np.roll(uij1, -1) - uij1)[lhs_linear_regime_ndx] + * self.lambda_S_scaled - (uij1[lhs_ohp_ndx]-uij1[0])*self.dx) if not np.isnan(self.rhs_ohp): rhs_linear_regime_ndx = (self.X >= self.rhs_ohp) - rhs_ohp_ndx = np.min( np.nonzero( rhs_linear_regime_ndx ) ) + rhs_ohp_ndx = np.min(np.nonzero(rhs_linear_regime_ndx)) self.logger.debug( - 'selected {:d} grid points within lhs OHP at grid point index {:d} with x_scaled >= {:>.4g}'.format( - np.count_nonzero(rhs_linear_regime_ndx), rhs_ohp_ndx, self.rhs_ohp) ) + 'selected {:d} grid points within lhs OHP at grid point index {:d} with x_scaled >= {:>.4g}'.format( + np.count_nonzero(rhs_linear_regime_ndx), rhs_ohp_ndx, self.rhs_ohp)) # dudx = (u[ohp]-u[0])/lambda_S within Stern layer Fu[rhs_linear_regime_ndx] = ( - ( uij1 - np.roll(uij1,1) )[rhs_linear_regime_ndx] - * self.lambda_S_scaled - (uij1[-1]-uij1[rhs_ohp_ndx])*self.dx ) + (uij1 - np.roll(uij1, 1))[rhs_linear_regime_ndx] + * self.lambda_S_scaled - (uij1[-1]-uij1[rhs_ohp_ndx])*self.dx) Fu[0] = self.boundary_conditions[0](x) Fu[-1] = self.boundary_conditions[1](x) @@ -778,93 +783,60 @@ def poisson_pde(self,x): return Fu - def nernst_planck_pde(self,x): - """Returns Nernst-Planck equation resiudal by applying controlled + def nernst_planck_pde(self, x): + """Returns Nernst-Planck equation residual by applying controlled volume scheme""" uij1 = x[:self.Ni] self.logger.debug( 'potential range [u_min, u_max] = [ {:>.4g}, {:>.4g} ]'.format( - np.min(uij1),np.max(uij1))) + np.min(uij1), np.max(uij1))) nij1 = x[self.Ni:(self.M+1)*self.Ni] - nijk1 = nij1.reshape( self.M, self.Ni ) + nijk1 = nij1.reshape(self.M, self.Ni) for k in range(self.M): - self.logger.debug( - 'ion species {:02d} concentration range [c_min, c_max] = [ {:>.4g}, {:>.4g} ]'.format( - k,np.min(nijk1[k,:]),np.max(nijk1[k,:]) ) ) + self.logger.debug( + 'ion species {:02d} concentration range [c_min, c_max] = [ {:>.4g}, {:>.4g} ]'.format( + k, np.min(nijk1[k, :]), np.max(nijk1[k, :]))) Fn = np.zeros([self.M, self.Ni]) # loop over k = 1..M reduced Nernst-Planck equations: # - d2nkdx2 - ddx (zk nk dudx ) = 0 for k in range(self.M): - # conrolled volume implementation: constant flux across domain - Fn[k,:] = ( - + B(self.zi0[k,:]*(uij1 - np.roll(uij1,-1))) * np.roll(nijk1[k,:],-1) - - B(self.zi0[k,:]*(np.roll(uij1,-1) - uij1)) * nijk1[k,:] - - B(self.zi0[k,:]*(np.roll(uij1,+1) - uij1)) * nijk1[k,:] - + B(self.zi0[k,:]*(uij1 - np.roll(uij1,+1))) * np.roll(nijk1[k,:],+1) ) - - # controlled volume implementation: flux j = 0 in every grid point - # - # Fn[k,:] = ( - # B(self.zi0[k,:]*(uij1 - np.roll(uij1,-1)))*np.roll(nijk1[k,:],-1) - # - B(self.zi0[k,:]*(np.roll(uij1,-1) - uij1))*nijk1[k,:] ) - - # linear potential regime due to steric effects incorporated here - # TODO: incorporate "spatially finite" BC into Robin BC functions - # replace left and right hand side residuals with linear potential FD - - # left and right hand side outer Helmholtz plane - # lhs_ohp = self.x0_scaled + self.lambda_S_scaled - # rhs_ohp = self.x0_scaled + self.L_scaled - self.lambda_S_scaled - # - # lhs_linear_regime_ndx = (self.X <= lhs_ohp) - # rhs_linear_regime_ndx = (self.X >= rhs_ohp) - # - # lhs_ohp_ndx = np.max( np.nonzero( lhs_linear_regime_ndx ) ) - # rhs_ohp_ndx = np.min( np.nonzero( rhs_linear_regime_ndx ) ) - # - # self.logger.debug( - # 'selected {:d} grid points within lhs OHP at grid point index {:d} with x_scaled <= {:>.4g}'.format( - # np.count_nonzero(lhs_linear_regime_ndx), lhs_ohp_ndx, lhs_ohp) ) - # self.logger.debug( - # 'selected {:d} grid points within lhs OHP at grid point index {:d} with x_scaled >= {:>.4g}'.format( - # np.count_nonzero(rhs_linear_regime_ndx), rhs_ohp_ndx, rhs_ohp) ) - # - # # zero concentration gradient in Stern layer - # Fn[k,lhs_linear_regime_ndx] = ( - # ( np.roll(nijk1[k,:],-1)-np.roll(nijk1[k,:],1))[lhs_linear_regime_ndx]) - # Fn[k,rhs_linear_regime_ndx] = ( - # ( np.roll(nijk1[k,:],-1)-np.roll(nijk1[k,:],1))[rhs_linear_regime_ndx]) - - Fn[k,0] = self.boundary_conditions[2*k+2](x) - Fn[k,-1] = self.boundary_conditions[2*k+3](x) - - self.logger.debug( - 'ion species {k:02d} BC residual Fn[{k:d},0] = {:> 8.4g}'.format( - Fn[k,0],k=k)) - self.logger.debug( - 'ion species {k:02d} BC residual Fn[{k:d},-1] = {:> 8.4g}'.format( - Fn[k,-1],k=k)) + # controlled volume implementation: constant flux across domain + Fn[k, :] = ( + + B(self.zi0[k, :]*(uij1 - np.roll(uij1, -1))) * np.roll(nijk1[k, :], -1) + - B(self.zi0[k, :]*(np.roll(uij1, -1) - uij1)) * nijk1[k, :] + - B(self.zi0[k, :]*(np.roll(uij1, +1) - uij1)) * nijk1[k, :] + + B(self.zi0[k, :]*(uij1 - np.roll(uij1, +1))) * np.roll(nijk1[k, :], +1)) + + Fn[k, 0] = self.boundary_conditions[2*k+2](x) + Fn[k, -1] = self.boundary_conditions[2*k+3](x) + + self.logger.debug( + 'ion species {k:02d} BC residual Fn[{k:d},0] = {:> 8.4g}'.format( + Fn[k, 0], k=k)) + self.logger.debug( + 'ion species {k:02d} BC residual Fn[{k:d},-1] = {:> 8.4g}'.format( + Fn[k, -1], k=k)) return Fn # non-linear system, "controlled volume" method - # Selbherr, S. Analysis and Simulation of Semiconductor Devices, Spriger 1984 + # Selbherr, S. Analysis and Simulation of Semiconductor Devices, Springer 1984 def G(self, x): """Non-linear system Discretization of Poisson-Nernst-Planck system with M ion species. Implements "controlled volume" method as found in - Selbherr, Analysis and Simulation of Semiconductor Devices, Spriger 1984 + Selbherr, Analysis and Simulation of Semiconductor Devices, Springer 1984 Parameters ---------- x : ((M+1)*Ni,) ndarray - system variables. 1D array of (M+1)*Ni values, wher M is number of - ion sepcies, Ni number of spatial discretization points. First Ni + system variables. 1D array of (M+1)*Ni values, where M is number of + ion species, Ni number of spatial discretization points. First Ni entries are expected to contain potential, following M*Ni points contain ion concentrations. Returns @@ -878,14 +850,14 @@ def G(self, x): # Apply constraints if set (not implemented properly, do not use): if len(self.g) > 0: Flam = np.array([g(x) for g in self.g]) - F = np.concatenate([Fu,Fn.flatten(),Flam]) + F = np.concatenate([Fu, Fn.flatten(), Flam]) else: - F = np.concatenate([Fu,Fn.flatten()]) + F = np.concatenate([Fu, Fn.flatten()]) return F @property - def I(self): # ionic strength + def I(self): # ionic strength """Compute the system's ionic strength from charges and concentrations. Returns @@ -894,7 +866,7 @@ def I(self): # ionic strength ionic strength ( 1/2 * sum(z_i^2*c_i) ) [concentration unit, i.e. mol m^-3] """ - return 0.5*np.sum( np.square(self.z) * self.c ) + return 0.5*np.sum(np.square(self.z) * self.c) @property def lambda_D(self): @@ -907,28 +879,28 @@ def lambda_D(self): """ return np.sqrt( self.relative_permittivity*self.vacuum_permittivity*self.R*self.T/( - 2.0*self.F**2*self.I ) ) + 2.0*self.F**2*self.I)) # default 0.1 mM (i.e. mol/m^3) NaCl aqueous solution def init(self, - c = np.array([0.1,0.1]), - z = np.array([1,-1]), - L = 100e-9, # 100 nm - lambda_S=0, # Stern layer (compact layer) thickness - x0 = 0, # zero position - T = 298.15, - delta_u = 0.05, # potential difference [V] - relative_permittivity = 79, - vacuum_permittivity = sc.epsilon_0, - R = sc.value('molar gas constant'), - F = sc.value('Faraday constant'), - N = 200, # number of grid segments, number of grid points Ni = N + 1 - e = 1e-10, # absolute tolerance, TODO: switch to standaradized measure - maxit = 20, # maximum number of Newton iterations - solver = None, - options = None, - potential0 = None, - concentration0 = None ): + c=np.array([0.1, 0.1]), + z=np.array([1, -1]), + L=100e-9, # 100 nm + lambda_S=0, # Stern layer (compact layer) thickness + x0=0, # zero position + T=298.15, + delta_u=0.05, # potential difference [V] + relative_permittivity=79, + vacuum_permittivity=sc.epsilon_0, + R=sc.value('molar gas constant'), + F=sc.value('Faraday constant'), + N=200, # number of grid segments, number of grid points Ni = N + 1 + e=1e-10, # absolute tolerance, TODO: switch to standardized measure + maxit=20, # maximum number of Newton iterations + solver=None, + options=None, + potential0=None, + concentration0=None): """Initializes a 1D Poisson-Nernst-Planck system description. Expects quantities in SI units per default. @@ -971,52 +943,51 @@ def init(self, concentration0: (M,N+1) ndarray, optional (default: None) concentration initial values """ - self.logger = logging.getLogger(__name__) assert len(c) == len(z), "Provide concentration AND charge for ALL ion species!" # TODO: integrate with constructor initialization parameters above # default solver settings - self.converged = False # solver's convergence flag - self.N = N # discretization segments - self.e = e # Newton solver default tolerance - self.maxit = maxit # Newton solver maximum iterations + self.converged = False # solver's convergence flag + self.N = N # discretization segments + self.e = e # Newton solver default tolerance + self.maxit = maxit # Newton solver maximum iterations # default output settings # self.output = False # let Newton solver output convergence plots... # self.outfreq = 1 # ...at every nth iteration - self.label_width = 40 # charcater width of quantity labels in log + self.label_width = 40 # character width of quantity labels in log # standard governing equations - self.potential_pde = self.poisson_pde - self.concentration_pde = self.nernst_planck_pde + self.potential_pde = self.poisson_pde + self.concentration_pde = self.nernst_planck_pde # empty BC self.boundary_conditions = [] # empty constraints - self.g = [] # list of constrain functions, not fully implemented / tested + self.g = [] # list of constrain functions, not fully implemented / tested # system parameters - self.M = len(c) # number of ion species + self.M = len(c) # number of ion species - self.c = c # concentrations - self.z = z # number charges - self.T = T # temperature - self.L = L # 1d domain size - self.lambda_S = lambda_S # Stern layer thickness - self.x0 = x0 # reference position - self.delta_u = delta_u # potential difference + self.c = c # concentrations + self.z = z # number charges + self.T = T # temperature + self.L = L # 1d domain size + self.lambda_S = lambda_S # Stern layer thickness + self.x0 = x0 # reference position + self.delta_u = delta_u # potential difference self.relative_permittivity = relative_permittivity - self.vacuum_permittivity = vacuum_permittivity + self.vacuum_permittivity = vacuum_permittivity # R = N_A * k_B # (universal gas constant = Avogadro constant * Boltzmann constant) - self.R = R - self.F = F + self.R = R + self.F = F - self.f = F / (R*T) # for convenience + self.f = F / (R*T) # for convenience # print all quantities to log for i, (c, z) in enumerate(zip(self.c, self.z)): @@ -1075,7 +1046,7 @@ def init(self, # reference position self.x0_scaled = self.x0 / self.l_unit - # bulk conectrations + # bulk concentrations self.c_scaled = self.c / self.c_unit # potential difference diff --git a/matscipy/fracture_mechanics/__init__.py b/matscipy/fracture_mechanics/__init__.py index e69de29b..40ff6a26 100644 --- a/matscipy/fracture_mechanics/__init__.py +++ b/matscipy/fracture_mechanics/__init__.py @@ -0,0 +1,4 @@ +""" +Calculation of continuum linear elastic displacement fields near crack tips, +including support for anisotropy in the elastic response. +""" diff --git a/matscipy/neighbours.py b/matscipy/neighbours.py index 876e1fa2..8e36b343 100644 --- a/matscipy/neighbours.py +++ b/matscipy/neighbours.py @@ -22,6 +22,8 @@ # along with this program. If not, see . # +"""Tools for computing and working with local topology of atomic structures.""" + import itertools as it import typing as ts from abc import ABC, abstractmethod @@ -541,14 +543,14 @@ def neighbour_list(quantities, Quantities to compute by the neighbor list algorithm. Each character in this string defines a quantity. They are returned in a tuple of the same order. Possible quantities are - 'i' : first atom index - 'j' : second atom index - 'd' : absolute distance - 'D' : distance vector - 'S' : shift vector (number of cell boundaries crossed by the bond - between atom i and j). With the shift vector S, the - distances D between atoms can be computed from: - D = a.positions[j]-a.positions[i]+S.dot(a.cell) + - ``i`` : first atom index + - ``j`` : second atom index + - ``d`` : absolute distance + - ``D`` : distance vector + - ``S`` : shift vector (number of cell boundaries crossed by the + bond between atom i and j). With the shift vector S, the + distances D between atoms can be computed from: + ``D = a.positions[j]-a.positions[i]+S.dot(a.cell)`` atoms : ase.Atoms Atomic configuration. (Default: None) cutoff : float or dict @@ -580,21 +582,26 @@ def neighbour_list(quantities, Examples -------- Examples assume Atoms object *a* and numpy imported as *np*. - 1. Coordination counting: + + 1. Coordination counting:: + i = neighbor_list('i', a, 1.85) coord = np.bincount(i) - 2. Coordination counting with different cutoffs for each pair of species + 2. Coordination counting with different cutoffs for each pair of species:: + i = neighbor_list('i', a, {('H', 'H'): 1.1, ('C', 'H'): 1.3, ('C', 'C'): 1.85}) coord = np.bincount(i) - 3. Pair distribution function: + 3. Pair distribution function:: + d = neighbor_list('d', a, 10.00) h, bin_edges = np.histogram(d, bins=100) pdf = h/(4*np.pi/3*(bin_edges[1:]**3 - bin_edges[:-1]**3)) * a.get_volume()/len(a) - 4. Pair potential: + 4. Pair potential:: + i, j, d, D = neighbor_list('ijdD', a, 5.0) energy = (-C/d**6).sum() pair_forces = (6*C/d**5 * (D/d).T).T @@ -605,7 +612,8 @@ def neighbour_list(quantities, forces_z = np.bincount(j, weights=pair_forces[:, 2], minlength=len(a)) - \ np.bincount(i, weights=pair_forces[:, 2], minlength=len(a)) - 5. Dynamical matrix for a pair potential stored in a block sparse format: + 5. Dynamical matrix for a pair potential stored in a block sparse format:: + from scipy.sparse import bsr_matrix i, j, dr, abs_dr = neighbor_list('ijDd', atoms) energy = (dr.T / abs_dr).T @@ -624,21 +632,21 @@ def neighbour_list(quantities, shape=(3 * len(a), 3 * len(a))) - i_n, j_n, dr_nc, abs_dr_n = neighbour_list('ijDd', atoms, dict) -e_nc = (dr_nc.T/abs_dr_n).T - D_ncc = -(dde_n * (e_nc.reshape(-1,3,1) * e_nc.reshape(-1,1,3)).T).T - D_ncc += -(de_n/abs_dr_n * (np.eye(3, dtype=e_nc.dtype) - (e_nc.reshape(-1,3,1) * e_nc.reshape(-1,1,3))).T).T + i_n, j_n, dr_nc, abs_dr_n = neighbour_list('ijDd', atoms, dict) + e_nc = (dr_nc.T/abs_dr_n).T + D_ncc = -(dde_n * (e_nc.reshape(-1,3,1) * e_nc.reshape(-1,1,3)).T).T + D_ncc += -(de_n/abs_dr_n * (np.eye(3, dtype=e_nc.dtype) - (e_nc.reshape(-1,3,1) * e_nc.reshape(-1,1,3))).T).T - D = bsr_matrix((D_ncc, j_n, first_i), shape=(3*nat,3*nat)) + D = bsr_matrix((D_ncc, j_n, first_i), shape=(3*nat,3*nat)) - Ddiag_icc = np.empty((nat,3,3)) - for x in range(3): - for y in range(3): - Ddiag_icc[:,x,y] = -np.bincount(i_n, weights = D_ncc[:,x,y]) + Ddiag_icc = np.empty((nat,3,3)) + for x in range(3): + for y in range(3): + Ddiag_icc[:,x,y] = -np.bincount(i_n, weights = D_ncc[:,x,y]) - D += bsr_matrix((Ddiag_icc,np.arange(nat),np.arange(nat+1)), shape=(3*nat,3*nat)) + D += bsr_matrix((Ddiag_icc,np.arange(nat),np.arange(nat+1)), shape=(3*nat,3*nat)) - return D + return D """ if cutoff is None: diff --git a/matscipy/pressurecoupling.py b/matscipy/pressurecoupling.py index d9aa58ce..741a2143 100644 --- a/matscipy/pressurecoupling.py +++ b/matscipy/pressurecoupling.py @@ -1,6 +1,6 @@ # # Copyright 2021 Lars Pastewka (U. Freiburg) -# 2020, 2022 Thomas Reichenbach (Fraunhofer IWM) +# 2020, 2022, 2023 Thomas Reichenbach (Fraunhofer IWM) # # matscipy - Materials science with Python at the atomic-scale # https://github.com/libAtoms/matscipy @@ -24,7 +24,7 @@ Classes to be used with ASE in order to perform pressure relaxation and/or sliding simulations under pressure. -A usage example is found in the example directory. +A usage example can be found in docs/applications/tribology.ipynb. Some parts are based on L. Pastewka, S. Moser, and M. Moseler, Tribol. Lett. 39, 49 (2010) @@ -330,20 +330,20 @@ class SlideLogger(object): Examples -------- 1. For new runs: - log_handle = open(logfn, 'w', 1) # line buffered - logger = SlideLogger(log_handle, ...) - logger.write_header() - integrator.attach(logger) - integrator.run(steps_integrate) + log_handle = open(logfn, 'w', 1) # line buffered\n + logger = SlideLogger(log_handle, ...)\n + logger.write_header()\n + integrator.attach(logger)\n + integrator.run(steps_integrate)\n log_handle.close() 2. For restarts: - with open(logfn, 'r') as log_handle: - step_offset = SlideLog(log_handle).step[-1] - log_handle = open(logfn, 'a', 1) # line buffered append - logger = SlideLogger(log_handle, ..., step_offset=step_offset) - integrator.attach(logger) - integrator.run(steps_integrate) + with open(logfn, 'r') as log_handle:\n + step_offset = SlideLog(log_handle).step[-1]\n + log_handle = open(logfn, 'a', 1) # line buffered append\n + logger = SlideLogger(log_handle, ..., step_offset=step_offset)\n + integrator.attach(logger)\n + integrator.run(steps_integrate)\n log_handle.close() """ diff --git a/paper/.gitignore b/paper/.gitignore new file mode 100644 index 00000000..f1ce48bb --- /dev/null +++ b/paper/.gitignore @@ -0,0 +1,3 @@ +media/ +*.jats +*.pdf diff --git a/paper/compile.sh b/paper/compile.sh new file mode 100755 index 00000000..57140aec --- /dev/null +++ b/paper/compile.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +docker run --rm \ + --volume $PWD:/data \ + --user $(id -u):$(id -g) \ + --env JOURNAL=joss \ + openjournals/inara diff --git a/paper/nl_time.svg b/paper/nl_time.svg new file mode 100644 index 00000000..22eb1239 --- /dev/null +++ b/paper/nl_time.svg @@ -0,0 +1,1026 @@ + + + + + + + + 2023-02-17T11:22:08.824548 + image/svg+xml + + + Matplotlib v3.5.1, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/paper/paper.bib b/paper/paper.bib new file mode 100644 index 00000000..7621c288 --- /dev/null +++ b/paper/paper.bib @@ -0,0 +1,1155 @@ +@article{Larsen2017, + doi = {10.1088/1361-648x/aa680e}, + url = {https://doi.org/10.1088/1361-648x/aa680e}, + year = 2017, + month = {jun}, + publisher = {{IOP} Publishing}, + volume = {29}, + number = {27}, + pages = {273002}, + author = {Ask Hjorth Larsen and Jens J{\o}rgen Mortensen and Jakob Blomqvist and Ivano E Castelli and Rune Christensen and Marcin Du{\l}ak and Jesper Friis and Michael N Groves and Bj{\o}rk Hammer and Cory Hargus and Eric D Hermes and Paul C Jennings and Peter Bjerre Jensen and James Kermode and John R Kitchin and Esben Leonhard Kolsbjerg and Joseph Kubal and Kristen Kaasbjerg and Steen Lysgaard and J{\'{o}}n Bergmann Maronsson and Tristan Maxson and Thomas Olsen and Lars Pastewka and Andrew Peterson and Carsten Rostgaard and Jakob Schi{\o}tz and Ole Schütt and Mikkel Strange and Kristian S Thygesen and Tejs Vegge and Lasse Vilhelmsen and Michael Walter and Zhenhua Zeng and Karsten W Jacobsen}, + title = {The atomic simulation environment{\textemdash}a Python library for working with atoms}, + journal = {Journal of Physics: Condensed Matter}, + abstract = {The atomic simulation environment (ASE) is a software package written in the Python programming language with the aim of setting up, steering, and analyzing atomistic simulations. In ASE, tasks are fully scripted in Python. The powerful syntax of Python combined with the NumPy array library make it possible to perform very complex simulation tasks. For example, a sequence of calculations may be performed with the use of a simple ‘for-loop’ construction. Calculations of energy, forces, stresses and other quantities are performed through interfaces to many external electronic structure codes or force fields using a uniform interface. On top of this calculator interface, ASE provides modules for performing many standard simulation tasks such as structure optimization, molecular dynamics, handling of constraints and performing nudged elastic band calculations.} +} + +@article{AtomMan, + author = {Lucas Hale}, + year = {2022}, + url = {https://github.com/usnistgov/atomman} +} + + +@ARTICLE{Stukowski2009, + title = "Visualization and analysis of atomistic simulation data with + {OVITO--the} Open Visualization Tool", + author = "Stukowski, Alexander", + abstract = "The Open Visualization Tool (OVITO) is a new 3D visualization + software designed for post-processing atomistic data obtained + from molecular dynamics or Monte Carlo simulations. Unique + analysis, editing and animations functions are integrated into + its easy-to-use graphical user interface. The software is + written in object-oriented C++, controllable via Python scripts + and easily extendable through a plug-in interface. It is + distributed as open-source software and can be downloaded from + the website http://ovito.sourceforge.net/.", + journal = "Modell. Simul. Mater. Sci. Eng.", + publisher = "IOP Publishing", + volume = 18, + number = 1, + pages = "015012", + month = dec, + year = 2009, + language = "en", + issn = "0965-0393", + doi = "10.1088/0965-0393/18/1/015012" +} + + +@ARTICLE{Bernstein2009, + title = "Hybrid atomistic simulation methods for materials systems", + author = "Bernstein, Noam and Kermode, J R and Cs{\'a}nyi, G", + abstract = "N 1 , JR 2,3 and G Csanyi CB2 1PZ, UK E-mail: noam. @nrl. navy + review recent progress in the methodology of /classical (QM", + journal = "Rep. Prog. Phys.", + volume = 72, + number = 2, + pages = "026501", + year = 2009, + issn = "0034-4885", + doi = "10.1088/0034-4885/72/2/026501" +} + +@ARTICLE{Musil2019, + title = "Fast and Accurate Uncertainty Estimation in Chemical Machine + Learning", + author = "Musil, F{\'e}lix and Willatt, Michael J and Langovoy, Mikhail A + and Ceriotti, Michele", + abstract = "We present a scheme to obtain an inexpensive and reliable + estimate of the uncertainty associated with the predictions of a + machine-learning model of atomic and molecular properties. The + scheme is based on resampling, with multiple models being + generated based on subsampling of the same training data. The + accuracy of the uncertainty prediction can be benchmarked by + maximum likelihood estimation, which can also be used to correct + for correlations between resampled models and to improve the + performance of the uncertainty estimation by a cross-validation + procedure. In the case of sparse Gaussian Process Regression + models, this resampled estimator can be evaluated at negligible + cost. We demonstrate the reliability of these estimates for the + prediction of molecular and materials energetics and for the + estimation of nuclear chemical shieldings in molecular crystals. + Extension to estimate the uncertainty in energy differences, + forces, or other correlated predictions is straightforward. This + method can be easily applied to other machine-learning schemes + and will be beneficial to make data-driven predictions more + reliable and to facilitate training-set optimization and + active-learning strategies.", + journal = "J. Chem. Theory Comput.", + volume = 15, + number = 2, + pages = "906--915", + month = feb, + year = 2019, + language = "en", + issn = "1549-9618, 1549-9626", + pmid = "30605342", + doi = "10.1021/acs.jctc.8b00959" +} + + +@ARTICLE{Golebiowski2018, + title = "Multiscale simulations of critical interfacial failure in carbon + nanotube-polymer composites", + author = "Go{\l}{\k e}biowski, Jacek R and Kermode, James R and Mostofi, + Arash A and Haynes, Peter D", + abstract = "Computational investigation of interfacial failure in composite + materials is challenging because it is inherently multi-scale: + the bond-breaking processes that occur at the covalently bonded + interface and initiate failure involve quantum mechanical + phenomena, yet the mechanisms by which external stresses are + transferred through the matrix occur on length and time scales + far in excess of anything that can be simulated quantum + mechanically. In this work, we demonstrate and validate an + adaptive quantum mechanics (QM)/molecular mechanics simulation + method that can be used to address these issues and apply it to + study critical failure at a covalently bonded carbon nanotube + (CNT)-polymer interface. In this hybrid approach, the majority + of the system is simulated with a classical forcefield, while + areas of particular interest are identified on-the-fly and + atomic forces in those regions are updated based on QM + calculations. We demonstrate that the hybrid method results are + in excellent agreement with fully QM benchmark simulations and + offers qualitative insights missing from classical simulations. + We use the hybrid approach to show how the chemical structure at + the CNT-polymer interface determines its strength, and we + propose candidate chemistries to guide further experimental work + in this area.", + journal = "J. Chem. Phys.", + publisher = "American Institute of Physics", + volume = 149, + number = 22, + pages = "224102", + month = dec, + year = 2018, + issn = "0021-9606", + doi = "10.1063/1.5035508" +} + + +@ARTICLE{Golebiowski2020, + title = "Atomistic {QM/MM} simulations of the strength of covalent + interfaces in carbon nanotube-polymer composites", + author = "Go{\l}{\k e}biowski, Jacek R and Kermode, James R and Haynes, + Peter D and Mostofi, Arash A", + abstract = "We investigate the failure of carbon-nanotube/polymer composites + by using a recently-developed hybrid + quantum-mechanical/molecular-mechanical (QM/MM) approach to + simulate nanotube pull-out from a cross-linked polyethene matrix. + Our study focuses on the strength and failure modes of + covalently-bonded nanotube-polymer interfaces based on amine, + carbene and carboxyl functional groups and a [2+1] cycloaddition. + We find that the choice of the functional group linking the + polymer matrix to the nanotube determines the effective strength + of the interface, which can be increased by up to 50\% (up to the + limit dictated by the strength of the polymer backbone itself) by + choosing groups with higher interfacial binding energy. We rank + the functional groups presented in this work based on the + strength of the resulting interface and suggest broad guidelines + for the rational design of nanotube functionalisation for + nanotube-polymer composites.", + journal = "Phys. Chem. Chem. Phys.", + month = may, + year = 2020, + language = "en", + issn = "1463-9076, 1463-9084", + pmid = "32421117", + doi = "10.1039/d0cp01841d" +} + + +@article{Grigorev2020, + title = {Hybrid quantum/classical study of hydrogen-decorated screw dislocations in tungsten: Ultrafast pipe diffusion, core reconstruction, and effects on glide mechanism}, + author = {Grigorev, Petr and Swinburne, Thomas D. and Kermode, James R.}, + journal = {Phys. Rev. Materials}, + volume = {4}, + issue = {2}, + pages = {023601}, + numpages = {10}, + year = {2020}, + month = {Feb}, + publisher = {American Physical Society}, + doi = {10.1103/PhysRevMaterials.4.023601}, + url = {https://link.aps.org/doi/10.1103/PhysRevMaterials.4.023601} +} + +@article{Grigorev2023, +title = {Calculation of dislocation binding to helium-vacancy defects in tungsten using hybrid ab initio-machine learning methods}, +journal = {Acta Materialia}, +volume = {247}, +pages = {118734}, +year = {2023}, +issn = {1359-6454}, +doi = {https://doi.org/10.1016/j.actamat.2023.118734}, +url = {https://www.sciencedirect.com/science/article/pii/S1359645423000666}, +author = {Petr Grigorev and Alexandra M. Goryaeva and Mihai-Cosmin Marinica and James R. Kermode and Thomas D. Swinburne}, +keywords = {Plasticity, Dislocations, Tungsten, Helium, Segregation, Density functional theory, QM/MM, Machine learning}, +abstract = {Calculations of dislocation-defect interactions are essential to model metallic strength, but the required system sizes are at or beyond ab initio limits. Current estimates thus have extrapolation or finite size errors that are very challenging to quantify. Hybrid methods offer a solution, embedding small ab initio simulations in an empirical medium. However, current implementations can only match mild elastic deformations at the ab initio boundary. We describe a robust method to employ linear-in-descriptor machine learning potentials as a highly flexible embedding medium, precisely matching dislocation migration pathways whilst keeping at least the elastic properties constant. This advanced coupling allows dislocations to cross the ab initio boundary in fully three dimensional defect geometries. Investigating helium and vacancy segregation to edge and screw dislocations in tungsten, we find long-range relaxations qualitatively change impurity-induced core reconstructions compared to those in short periodic supercells, even when multiple helium atoms are present. We also show that helium-vacancy complexes, considered to be the dominant configuration at low temperatures, have only a very weak binding to screw dislocations. These results are discussed in the context of recent experimental and theoretical studies. More generally, our approach opens a vast range of mechanisms to ab initio investigation and provides new reference data to both validate and improve interatomic potentials.}, +} + +@article{Pastewka2010, + doi = {10.1007/s11249-009-9566-8}, + url = {https://doi.org/10.1007/s11249-009-9566-8}, + year = {2010}, + month = jan, + publisher = {Springer Science and Business Media {LLC}}, + volume = {39}, + number = {1}, + pages = {49--61}, + author = {Lars Pastewka and Stefan Moser and Michael Moseler}, + title = {Atomistic Insights into the Running-in, Lubrication, and Failure of Hydrogenated Diamond-Like Carbon Coatings}, + journal = {Tribology Letters} +} + +@article{StillingerWeber1985, + title={Computer simulation of local order in condensed phases of silicon}, + author={Stillinger, Frank H and Weber, Thomas A}, + journal={Phys. Rev. B}, + volume={31}, + number={8}, + pages={5262}, + year={1985}, + publisher={APS} +} + +@article{Kumagai2007, + title={Development of bond-order potentials that can reproduce the elastic constants and melting point of silicon for classical molecular dynamics simulation}, + author={Kumagai, T and Izumi, S and Hara, S and Sakai, S}, + journal={Computational materials science}, + volume={39}, + number={2}, + pages={457--464}, + year={2007}, + publisher={Elsevier} +} + +@article{Tersoff1986, + title = {New Empirical Model for the Structural Properties of Silicon}, + volume = {56}, + issn = {0031-9007}, + number = {6}, + journal = {Phys. Rev. Lett.}, + author = {Tersoff, J}, + year = {1986}, + pages = {632--635}, + file = {Tersoff 1986 - New Empirical Model for the Structural Properties of Silicon:C\:\\Users\\Lars Pastewka\\Zotero\\storage\\QCIUZDUB\\Tersoff 1986 - New Empirical Model for the Structural Properties of Silicon.pdf:application/pdf}, + optdoi = {10.1103/PhysRevLett.56.632}, +} + +@article{Tersoff1989, + title={Modeling solid-state chemistry: Interatomic potentials for multicomponent systems}, + volume = {39}, + number = {8}, + author={Tersoff, J}, + journal={Phys. Rev. B}, + pages={5566}, + year={1989}, + publisher={APS} +} + +@article{Brenner1990, + title = {Empirical potential for hydrocarbons for use in simulating chemical vapor deposition of diamond films}, + volume = {42}, + number = {15}, + journal = {Phys. Rev. B}, + author = {Brenner, Donald W.}, + year = {1990}, + pages = {9458--9471}, + file = {Brenner 1990 - Empirical potential for hydrocarbons for use in simulating chemical vapor deposition of diamond films:C\:\\Users\\Lars Pastewka\\Zotero\\storage\\Q32QX6R5\\Brenner 1990 - Empirical potential for hydrocarbons for use in simulating chemical vapor deposition of diamond films.pdf:application/pdf}, + optdoi = {10.1103/PhysRevB.42.9458}, +} + +@article{BKS1990, + title={Force fields for silicas and aluminophosphates based on ab initio calculations}, + author={Van Beest, BWH and Kramer, Gert Jan and Van Santen, RA}, + journal={Phys. Rev. Lett.}, + volume={64}, + number={16}, + pages={1955}, + year={1990}, + publisher={APS} +} + +@article{Lennard1931, + title={Cohesion}, + author={Lennard-Jones, John E}, + journal={Proc. Phys. Soc.}, + volume={43}, + number={5}, + pages={461-482}, + year={1931}, + publisher={IOP Publishing} +} + +@ARTICLE{Sih1965, + title = "On cracks in rectilinearly anisotropic bodies", + author = "Sih, G C and Paris, P C and Irwin, G R", + journal = "Int. J. Fract. Mech.", + volume = 1, + number = 3, + pages = "189--203", + month = sep, + year = 1965, + issn = "0020-7268", + doi = "10.1007/BF00186854" +} + +@ARTICLE{Sinclair1975, + title = "The Influence of the Interatomic Force Law and of Kinks on the + Propagation of Brittle Cracks", + author = "Sinclair, J E", + journal = "Philos. Mag.", + volume = 31, + pages = "647--671", + year = 1975, + issn = "1478-6435" +} + +@ARTICLE{Kermode2015, + title = "Low Speed Crack Propagation via Kink Formation and Advance on + the Silicon (110) Cleavage Plane", + author = "Kermode, James R and Gleizer, Anna and Kovel, Guy and Pastewka, + Lars and Cs{\'a}nyi, G{\'a}bor and Sherman, Dov and De Vita, + Alessandro", + journal = "Phys. Rev. Lett.", + publisher = "American Physical Society", + volume = 115, + number = 13, + pages = "135501", + month = sep, + year = 2015, + issn = "0031-9007", + doi = "10.1103/PhysRevLett.115.135501" +} + + +@ARTICLE{Buze2021, + title = "Numerical-continuation-enhanced flexible boundary condition + scheme applied to mode-I and {mode-III} fracture", + author = "Buze, Maciej and Kermode, James R", + journal = "Phys. Rev. E", + publisher = "American Physical Society", + volume = 103, + number = 3, + pages = "033002", + month = mar, + year = 2021, + doi = "10.1103/PhysRevE.103.033002" +} + +@article{Martinez2009, + title = {{{PACKMOL}}: {{A}} Package for Building Initial Configurations for Molecular Dynamics Simulations}, + author = {Martinez, L. and Andrade, R and Birgin, E G and Mart{\'i}nez, J M}, + year = {2009}, + journal = {Journal of Computational Chemistry}, + volume = {30}, + number = {13}, + pages = {2157--2164}, + issn = {01928651}, + doi = {10/chm6f7}, + abstract = {Adequate initial configurations for molecular dynamics simulations consist of arrangements of molecules distributed in space in such a way to approximately represent the system's overall structure. In order that the simulations are not disrupted by large van derWaals repulsive interactions, atoms from different molecules must keep safe pairwise distances. Obtaining such a molecular arrangement can be considered a packing problem: Each type molecule must satisfy spatial constraints related to the geometry of the system, and the distance between atoms of different molecules must be greater than some specified tolerance. We have developed a code able to pack millions of atoms, grouped in arbitrarily complex molecules, inside a variety of three-dimensional regions. The regions may be intersections of spheres, ellipses, cylinders, planes, or boxes. The user must provide only the structure of one molecule of each type and the geometrical constraints that each type of molecule must satisfy. Building complex mixtures, interfaces, solvating biomolecules in water, other solvents, or mixtures of solvents, is straightforward. In addition, different atoms belonging to the same molecule may also be restricted to different spatial regions, in such a way that more ordered molecular arrangements can be built, as micelles, lipid double-layers, etc. The packing time for state-of-the-art molecular dynamics systems varies from a few seconds to a few minutes in a personal computer. The input files are simple and currently compatible with PDB, Tinker, Molden, or Moldy coordinate files. The package is distributed as free software and can be downloaded from http://www.ime.unicamp.br/{$\sim$}martinez/packmol/. \textcopyright}, + arxiv = {NIHMS150003}, + isbn = {1096-987X}, + pmid = {20928852}, + keywords = {initial configuration,MD,micelle}, + annotation = {02301}, +} + +@book{Selberherr1984, + title = {Analysis and simulation of semiconductor devices}, + author = {Selberherr, Siegfried}, + year = {1984}, + publisher = {Springer Science \& Business Media} +} + +@article{Seidl2021, + title = {Molecular {{Simulations}} of {{Electrotunable Lubrication}}: {{Viscosity}} and {{Wall Slip}} in {{Aqueous Electrolytes}}}, + shorttitle = {Molecular {{Simulations}} of {{Electrotunable Lubrication}}}, + author = {Seidl, Christian and H{\"o}rmann, Johannes L. and Pastewka, Lars}, + year = {2021}, + month = jan, + journal = {Tribology Letters}, + volume = {69}, + number = {1}, + pages = {22}, + issn = {1573-2711}, + doi = {10.1007/s11249-020-01395-6}, + abstract = {We study the frictional response of water-lubricated gold electrodes subject to an electrostatic potential difference using molecular dynamics simulations. Contrary to previous studies on electrotunable lubrication that were carried out by fixing the charges, our simulations keep electrodes at fixed electrostatic potential using a variable charge method. For pure water and NaCl solutions, viscosity is independent of the polarization of the electrodes, but wall slip depends on the potential difference. Our findings are in agreement with previous analytical theories of how wall slip is affected by interatomic interactions. The simulations shed light on the role of electrode polarization for wall slip and illustrate a mechanism for controlling friction and nanoscale flow in simple aqueous lubricants.}, + langid = {english}, +} + +@article{Bazant2006, + title = {Current-{{Voltage Relations}} for {{Electrochemical Thin Films}}}, + author = {Bazant, Martin Z. and Chu, Kevin T. and Bayly, B. J.}, + year = {2006}, + month = jul, + journal = {SIAM Journal on Applied Mathematics}, + publisher = {{Society for Industrial and Applied Mathematics}}, + doi = {10.1137/040609938}, + abstract = {The DC response of an electrochemical thin film, such as the separator in a microbattery, is analyzed by solving the Poisson--Nernst--Planck equations, subject to boundary conditions appropriate fo...}, + copyright = {Copyright \textcopyright{} 2005 Society for Industrial and Applied Mathematics}, + langid = {english}, +} + +@ARTICLE{Jorgensen1996, + title = "Development and Testing of the OPLS All-Atom Force Field on + Conformational Energetics and Properties of Organic Liquids", + author = "Jorgensen, William L. and Maxwell, David S. and + Tirado-Rives, Julian", + journal = "Journal of the American Chemical Society", + volume = 118, + number = 45, + pages = "11225-11236", + year = 1996, + doi = "10.1021/ja9621760" +} + +@ARTICLE{Thompson2022, + title = "LAMMPS - a flexible simulation tool for particle-based + materials modeling at the atomic, meso, and continuum scale", + author = "Thompson, Aidan P. and Aktulga, H. Metin and Berger, Richard + and Bolintineanu, Dan S. and Brown, W. Michael and + Crozier, Paul S. and {in 't Veld}, Pieter J. and Kohlmeyer, Axel + and Moore, Stan G. and Nguyen, Trung Dac and Shan, Ray and + Stevens, Mark J. and Tranchida, Julien and Trott, Christian + and Plimpton, Steven J.", + journal = "Computer Physics Communications", + volume = 271, + pages = "108171", + year = 2022, + doi = "10.1016/j.cpc.2021.108171" +} + +@ARTICLE{Mayrhofer2016, + title = "Fluorine-Terminated Diamond Surfaces as Dense Dipole Lattices: + The Electrostatic Origin of Polar Hydrophobicity", + author = "Mayrhofer, Leonhard and Moras, Gianpietro and + Mulakaluri, Narasimham and Rajagopalan, Srinivasan and + Stevens, Paul A. and Moseler, Michael", + journal = "Journal of the American Chemical Society", + volume = 138, + pages = "4018-4028", + year = 2016, + doi = "10.1021/jacs.5b04073" +} + +@ARTICLE{Falk2020, + title = "Nonempirical Free Volume Viscosity Model for Alkane Lubricants + under Severe Pressures", + author = "Falk, Kerstin and Savio, Daniele and Moseler, Michael", + journal = "Phys. Rev. Lett.", + volume = 124, + pages = "105501", + year = 2020, + doi = "10.1103/PhysRevLett.124.10550" +} + +@ARTICLE{Reichenbach2020, + title = "Steric Effects Control Dry Friction of H- and F-Terminated + Carbon Surfaces", + author = "Reichenbach, Thomas and Mayrhofer, Leonhard and + Kuwahara, Takuya and Moseler, Michael and Moras, Gianpietro", + journal = "ACS Applied Materials \& Interfaces", + volume = 12, + pages = "8805-8816", + year = 2020, + doi = "10.1021/acsami.9b18019" +} + +@ARTICLE{vonGoeldel2021, + title = "A Combined Experimental and Atomistic Investigation of PTFE + Double Transfer Film Formation and Lubrication in Rolling + Point Contacts", + author = "von Goeldel, Stephan and Reichenbach, Thomas and + Konig, Florian and Mayrhofer, Leonhard and + Moras, Gianpietro and Jacobs, Georg and + Moseler, Michael", + journal = "Tribology Letters", + volume = 69, + pages = "136", + year = 2021, + doi = "10.1007/s11249-021-01508-9" +} + +@ARTICLE{Falk2022, + title = "Relating Dry Friction to Interdigitation of Surface + Passivation Species: A Molecular Dynamics Study on Amorphous + Carbon", + author = "Falk, Kerstin and Reichenbach, Thomas and Gkagkas, Konstantinos + and Moseler, Michael and Moras, Gianpietro", + journal = "Materials", + volume = 15, + number = 9, + pages = "3247", + year = 2022, + doi = "10.3390/ma15093247" +} + +@book{LoggMardalEtAl2012, + title = {Automated Solution of Differential Equations by the Finite Element Method}, + author = {Anders Logg and Kent-Andre Mardal and Garth N. Wells and others}, + editor = {Anders Logg and Kent-Andre Mardal and Garth N. Wells}, + year = {2012}, + publisher = {Springer}, + doi = {10.1007/978-3-642-23099-8}, + isbn = {978-3-642-23098-1}, +} + +@ARTICLE{Griesser2023glass, + title = "Yielding under compression and the polyamorphic transition in + silicon", + author = "Grie{\ss}er, Jan and Moras, Gianpietro and Pastewka, Lars", + journal = "Phys. Rev. Mater.", + publisher = "American Physical Society", + volume = 7, + number = 5, + pages = "055601", + month = may, + year = 2023 +} + +@ARTICLE{Griesser2023crystal, + title = "Analytic elastic constants in molecular calculations: Finite + strain, non-affine displacements, and many-body interatomic + potentials", + author = "Grie{\ss}er, Jan and Fr{\'e}rot, Lucas and Oldenstaedt, + Jonas A and M{\"u}ser, Martin H and Pastewka, Lars", + abstract = "Elastic constants are among the most fundamental and + important properties of solid materials, which is why they + are routinely characterized in both experiments and + simulations. While conceptually simple, the treatment of + elastic constants is complicated by two factors not yet + having been concurrently discussed: finite-strain and + non-affine, internal displacements. Here, we revisit the + theory behind zero-temperature, finite-strain elastic + constants and extend it to explicitly consider non-affine + displacements. We further present analytical expressions for + second-order derivatives of the potential energy for + two-body and generic many-body interatomic potentials, such + as cluster and empirical bond-order potentials. + Specifically, we revisit the elastic constants of silicon, + silicon carbide and silicon dioxide under hydrostatic + compression and dilatation. Based on existing and new + results, we outline the effect of multiaxial stress states + as opposed to volumetric deformation on the limits of + stability of their crystalline lattices.", + month = feb, + year = 2023, + archivePrefix = "arXiv", + primaryClass = "cond-mat.mtrl-sci", + eprint = "2302.08754" +} + +@ARTICLE{Miller2009, + title = "A unified framework and performance benchmark of fourteen + multiscale atomistic/continuum coupling methods", + author = "Miller, Ronald E and Tadmor, Ellad B", + journal = "Modell. Simul. Mater. Sci. Eng.", + volume = 17, + number = 5, + pages = "053001", + month = jul, + year = 2009 +} + +@ARTICLE{Anciaux2018, + title = "The Coupled {Atomistic/Discrete-Dislocation} method in 3d part I: + Concept and algorithms", + author = "Anciaux, G and Junge, T and Hodapp, M and Cho, J and Molinari, + J-F and Curtin, W A", + abstract = "The Coupled Atomistic/Discrete-Dislocation (CADD) method is a + concurrent multiscale technique that couples atomistic and + discrete dislocation domains with the ability to pass + dislocations seamlessly between domains. CADD has been + demonstrated only in 2d plane-strain problems, for which each + individual dislocation is either entirely atomistic or entirely + discrete. Here, a full 3d implementation of CADD is presented, + with emphasis on the algorithms for handling the description of + dislocation lines that span both atomistic and continuum domains, + so-called hybrid dislocations. The key new features of the method + for 3d are (i) the use of an atomistic template of the + dislocation core structure to transmit the proper atomistic + environment of a continuum dislocation to the atomistic domain + for hybrid dislocations and (ii) a staggered solution procedure + enabling evolution of the hybrid dislocations. The method + naturally requires calibration of discrete-dislocation Peierls + stresses and mobilities to their atomistic values, implementation + of a dislocation detection algorithm to identify atomistic + dislocations, and computation of continuum dislocation + displacement fields that provide boundary conditions for the + atomistic problem. The method is implemented using the atomistic + code LAMMPS and the discrete dislocation code ParaDiS within the + LibMultiscale environment developed by the lead authors, and so + has all the advantages of these widely-used high-performance + open-source codes. Validation and application of CADD-3d are + presented in companion papers.", + journal = "J. Mech. Phys. Solids", + volume = 118, + pages = "152--171", + month = sep, + year = 2018 +} + +% The entry below contains non-ASCII chars that could not be converted +% to a LaTeX equivalent. +@ARTICLE{Anciaux2007, + title = "Simulation multi-{\'e}chelles des solides par une approche + coupl{\'e}e dynamique mol{\'e}culaire/{\'e}l{\'e}ments finis. De + la mod{\'e}lisation {\`a} la simulation haute performance", + author = "Anciaux, G", + abstract = "Cette th{\`e}se porte sur l'{\'e}tude de la simulation des + solides par des m{\'e}thodes de couplage multi-{\'e}chelles + (m{\'e}thode atomique/continue ACM). Dans ce travail de + th{\`e}se, nous abordons …", + publisher = "theses.hal.science", + year = 2007 +} + +@ARTICLE{Campana2006, + title = "Practical Green's function approach to the simulation of elastic + semi-infinite solids", + author = "Campa{\~n}{\'a}, Carlos and M{\"u}ser, Martin H", + journal = "Phys. Rev. B", + volume = 74, + number = 7, + pages = "075420", + year = 2006 +} + +@ARTICLE{Pastewka2012, + title = "Seamless elastic boundaries for atomistic calculations", + author = "Pastewka, Lars and Sharp, Tristan A and Robbins, Mark O", + journal = "Phys. Rev. B", + volume = 86, + pages = "075459", + year = 2012 +} + +@ARTICLE{Falk1998, + title = "Dynamics of viscoplastic deformation in amorphous solids", + author = "Falk, Michael L and Langer, J S", + abstract = "for situations in which the stresses necessarily rise to values + at or 7204 AND 45 . Carlson and A. Batista, 46 S. and A. Liu + unpublished", + journal = "Phys. Rev. E", + volume = 57, + number = 6, + pages = "7192--7205", + month = jun, + year = 1998 +} + +@ARTICLE{Jana2019, + title = "Correlations of non-affine displacements in metallic glasses + through the yield transition", + author = "Jana, Richard and Pastewka, Lars", + abstract = "We study correlations of non-affine displacements during simple + shear deformation of Cu--Zr bulk metallic glasses in molecular + dynamics calculations. In the elastic regime, our calculations + show exponential correlation with a decay length that we + interpret as the size of a shear transformation zone in the + elastic regime. This correlation length becomes system-size + dependent beyond the yield transition as our calculation develops + a shear band, indicative of a diverging length scale. We discuss + these observations in the context of a recent proposition of + yield as a first-order phase transition.", + journal = "J. Phys. Mater.", + volume = 2, + pages = "045006", + month = jul, + year = 2019, + language = "en" +} + +@ARTICLE{Gola2019, + title = "Surface flaws control strain localization in the deformation of + {Cu|Au} nanolaminate pillars", + author = "Gola, Adrien and Zhang, Guang-Ping and Pastewka, Lars and + Schwaiger, Ruth", + abstract = ", The authors carried out matched experiments and molecular + dynamics simulations of the compression of nanopillars prepared + from Cu|Au nanolaminates with up to 25 nm layer thickness. The + stress--strain behaviors obtained from both techniques are in + excellent agreement. Variation in the layer thickness reveals an + increase in the strength with a decreasing layer thickness. + Pillars fail through the formation of shear bands whose + nucleation they trace back to the existence of surface flaws. + This combined approach demonstrates the crucial role of contact + geometry in controlling the deformation mode and suggests that + modulus-matched nanolaminates should be able to suppress strain + localization while maintaining controllable strength.", + journal = "MRS Communications", + volume = 9, + number = 3, + pages = "1067--1071", + year = 2019, + language = "en" +} + +@ARTICLE{Gola2020, + title = "Pattern formation during deformation of metallic nanolaminates", + author = "Gola, Adrien and Schwaiger, Ruth and Gumbsch, Peter and Pastewka, + Lars", + abstract = "We used nonequilibrium molecular dynamics simulations to study + the shear deformation of metallic composites composed of + alternating layers of Cu and Au. Our simulations reveal the + formation of ``vortices'' or ``swirls'' if the bimaterial + interfaces are atomically rough and if none of the \{111\} planes + that accommodate slip in fcc materials are exactly parallel to + this interface. We trace the formation of these patterns back to + grain rotation, induced by hindering dislocations from crossing + the bimaterial interface. The instability is accompanied by shear + softening of the material. These calculations shed light on + recent observations of pattern formation in plastic flow, + mechanical mixing of materials, and the common formation of a + tribomutation layer in tribologically loaded systems.", + journal = "Phys. Rev. Mater.", + volume = 4, + number = 1, + pages = "013603", + month = jan, + year = 2020 +} + +@ARTICLE{Franzblau1991, + title = "Computation of ring statistics for network models of solids", + author = "Franzblau, Deborah S", + journal = "Phys. Rev. B", + volume = 44, + number = 10, + pages = "4925--4930", + month = sep, + year = 1991 +} + +@TECHREPORT{Saad1990, + title = "{SPARSKIT}: A basic tool kit for sparse matrix computations", + author = "Saad, Youcef", + abstract = "Presented here are the main features of a tool package for + manipulating and working with sparse matrices. One of the goals + of the package is to provide basic tools to facilitate the + exchange of software and data between researchers in sparse + matrix computations. The starting point is the Harwell/Boeing + collection of matrices for which the authors provide a number of + tools. Among other things, the package provides programs for + converting data structures, printing simple statistics on a + matrix, plotting a matrix profile, and performing linear algebra + operations with sparse matrices.", + publisher = "ntrs.nasa.gov", + number = "NAS 1.26:185876", + month = may, + year = 1990 +} + +@ARTICLE{Pastewka2008, + title = "Describing bond-breaking processes by reactive potentials: + Importance of an environment-dependent interaction range", + author = "Pastewka, Lars and Pou, Pablo and P{\'e}rez, Rub{\'e}n and + Gumbsch, Peter and Moseler, Michael", + journal = "Phys. Rev. B Condens. Matter", + volume = 78, + number = 16, + pages = "161402(R)", + year = 2008 +} + +@ARTICLE{Jana2019, + title = "Structural and elastic properties of amorphous carbon from + simulated quenching at low rates", + author = "Jana, Richard and Savio, Daniele and Deringer, Volker and + Pastewka, Lars", + abstract = "We generate representative structural models of amorphous carbon + (a-C) from constant-volume quenching from the liquid with + subsequent relaxation of internal stresses in molecular dynamics + simulations using empirical and machine-learning interatomic + potentials. By varying volume and quench rate we generate + structures with a range of density and amorphous morphologies. We + find that all a-C samples show a universal relationship between + hybridization, bulk modulus and density despite having distinctly + different cohesive energies. Differences in cohesive energy are + traced back to slight changes in the distribution of bond-angles + that will likely be linked to thermal stability of these + structures.", + journal = "Modell. Simul. Mater. Sci. Eng.", + volume = 27, + pages = "085009", + year = 2019, + language = "en" +} + +@ARTICLE{Muser2023, + title = "Interatomic potentials: achievements and challenges", + author = "M{\"u}ser, Martin H and Sukhomlinov, Sergey V and Pastewka, Lars", + abstract = "ABSTRACTInteratomic potentials approximate the potential energy + of atoms as a function of their coordinates. Their main + application is the effective simulation of many-atom systems. + Here, we review empirical interatomic potentials designed to + reproduce elastic properties, defect energies, bond breaking, + bond formation, and even redox reactions. We discuss popular + two-body potentials, embedded-atom models for metals, bond-order + potentials for covalently bonded systems, polarizable potentials + including charge-transfer approaches for ionic systems and + quantum-Drude oscillator models mimicking higher-order and + many-body dispersion. Particular emphasis is laid on the + question what constraints ensue from the functional form of a + potential, e.g., in what way Cauchy relations for elastic tensor + elements can be violated and what this entails for the ratio of + defect and cohesive energies, or why the ratio of boiling to + melting temperature tends to be large for potentials describing + metals but small for short-ranged pair potentials. The review is + meant to be pedagogical rather than encyclopedic. This is why we + highlight potentials with functional forms sufficiently simple + to remain amenable to analytical treatments. Our main objective + is to provide a stimulus for how existing approaches can be + advanced or meaningfully combined to extent the scope of + simulations based on empirical potentials.", + journal = "Advances in Physics: X", + publisher = "Taylor \& Francis", + volume = 8, + number = 1, + pages = "2093129", + month = jan, + year = 2023 +} + +@inproceedings{ +Batatia2022mace, +title={{MACE}: Higher Order Equivariant Message Passing Neural Networks for Fast and Accurate Force Fields}, +author={Ilyes Batatia and David Peter Kovacs and Gregor N. C. Simm and Christoph Ortner and Gabor Csanyi}, +booktitle={Advances in Neural Information Processing Systems}, +editor={Alice H. Oh and Alekh Agarwal and Danielle Belgrave and Kyunghyun Cho}, +year={2022}, +url={https://openreview.net/forum?id=YPpSngE-ZU} +} + +@misc{Batatia2022Design, + title = {The Design Space of E(3)-Equivariant Atom-Centered Interatomic Potentials}, + author = {Batatia, Ilyes and Batzner, Simon and Kov{\'a}cs, D{\'a}vid P{\'e}ter and Musaelian, Albert and Simm, Gregor N. C. and Drautz, Ralf and Ortner, Christoph and Kozinsky, Boris and Cs{\'a}nyi, G{\'a}bor}, + year = {2022}, + number = {arXiv:2205.06643}, + eprint = {2205.06643}, + eprinttype = {arxiv}, + doi = {10.48550/arXiv.2205.06643}, + archiveprefix = {arXiv} + } + +% The entry below contains non-ASCII chars that could not be converted +% to a LaTeX equivalent. +@ARTICLE{Peguiron2016-wf, + title = "Activation and mechanochemical breaking of {C−C} bonds initiate + wear of diamond (110) surfaces in contact with silica", + author = "Peguiron, Anke and Moras, Gianpietro and Walter, Michael and + Uetsuka, Hiroshi and Pastewka, Lars and Moseler, Michael", + journal = "Carbon N. Y.", + volume = 98, + pages = "474--483", + year = 2016 +} + +@ARTICLE{Moras2018-lm, + title = "Shear melting of silicon and diamond and the disappearance of the + polyamorphic transition under shear", + author = "Moras, Gianpietro and Klemenz, Andreas and Reichenbach, Thomas and + Gola, Adrien and Uetsuka, Hiroshi and Moseler, Michael and + Pastewka, Lars", + journal = "Phys. Rev. Materials", + volume = 2, + number = 8, + pages = "083601", + month = aug, + year = 2018 +} + +@ARTICLE{Reichenbach2021-pi, + title = "{Solid-Phase} Silicon Homoepitaxy via {Shear-Induced} + Amorphization and Recrystallization", + author = "Reichenbach, Thomas and Moras, Gianpietro and Pastewka, Lars and + Moseler, Michael", + abstract = "We study mechanically induced phase transitions at tribological + interfaces between silicon crystals using reactive molecular + dynamics. The simulations reveal that the interplay between + shear-driven amorphization and recrystallization results in an + amorphous shear interface with constant thickness. Different + shear elastic responses of the two anisotropic crystals can lead + to the migration of the amorphous interface normal to the sliding + plane, causing the crystal with lowest elastic energy density to + grow at the expense of the other one. This triboepitaxial growth + can be achieved by crystal misorientation or exploiting elastic + finite-size effects, enabling the direct deposition of + homoepitaxial silicon nanofilms by a crystalline tip rubbing + against a substrate.", + journal = "Phys. Rev. Lett.", + volume = 127, + number = 12, + pages = "126101", + month = sep, + year = 2021, + language = "en" +} + +% The entry below contains non-ASCII chars that could not be converted +% to a LaTeX equivalent. +@ARTICLE{Moras2011-my, + title = "Formation and oxidation of linear carbon chains and their role + in the wear of carbon materials", + author = "Moras, G and Pastewka, L and Gumbsch, P and Moseler, M", + abstract = "The atomic-scale processes taking place during the sliding of + diamond and diamond-like carbon surfaces are investigated using + classical molecular dynamics simulations. During the …", + journal = "Tribol. Lett.", + publisher = "Springer", + year = 2011 +} + +@ARTICLE{Pastewka2011-rd, + title = "Anisotropic mechanical amorphization drives wear in diamond", + author = "Pastewka, Lars and Moser, Stefan and Gumbsch, Peter and Moseler, + Michael", + journal = "Nat. Mater.", + volume = 10, + number = 1, + pages = "34--38", + month = nov, + year = 2011 +} + +@ARTICLE{Seidl2021-vn, + title = "Molecular Simulations of Electrotunable Lubrication: Viscosity + and Wall Slip in Aqueous Electrolytes", + author = "Seidl, Christian and H{\"o}rmann, Johannes L and Pastewka, Lars", + abstract = "We study the frictional response of water-lubricated gold + electrodes subject to an electrostatic potential difference using + molecular dynamics simulations. Contrary to previous studies on + electrotunable lubrication that were carried out by fixing the + charges, our simulations keep electrodes at fixed electrostatic + potential using a variable charge method. For pure water and NaCl + solutions, viscosity is independent of the polarization of the + electrodes, but wall slip depends on the potential difference. + Our findings are in agreement with previous analytical theories + of how wall slip is affected by interatomic interactions. The + simulations shed light on the role of electrode polarization for + wall slip and illustrate a mechanism for controlling friction and + nanoscale flow in simple aqueous lubricants.", + journal = "Tribol. Lett.", + volume = 69, + number = 1, + pages = "22", + month = jan, + year = 2021, + language = "en" +} + +@ARTICLE{Hormann2023-ml, + title = "Molecular simulations of sliding on {SDS} surfactant films", + author = "H{\"o}rmann, Johannes L and Liu, Chenxu and Meng, Yonggang and + Pastewka, Lars", + abstract = "We use molecular dynamics simulations to study the frictional + response of monolayers of the anionic surfactant sodium dodecyl + sulfate and hemicylindrical aggregates physisorbed on gold. Our + simulations of a sliding spherical asperity reveal the following + two friction regimes: at low loads, the films show Amonton's + friction with a friction force that rises linearly with normal + load, and at high loads, the friction force is independent of the + load as long as no direct solid-solid contact occurs. The + transition between these two regimes happens when a single + molecular layer is confined in the gap between the sliding + bodies. The friction force at high loads on a monolayer rises + monotonically with film density and drops slightly with the + transition to hemicylindrical aggregates. This monotonous + increase of friction force is compatible with a traditional + plowing model of sliding friction. At low loads, the friction + coefficient reaches a minimum at the intermediate surface + concentrations. We attribute this behavior to a competition + between adhesive forces, repulsion of the compressed film, and + the onset of plowing.", + journal = "J. Chem. Phys.", + volume = 158, + number = 24, + month = jun, + year = 2023, + language = "en" +} + +@ARTICLE{Harris2020-it, + title = "Array programming with {NumPy}", + author = "Harris, Charles R and Millman, K Jarrod and van der Walt, + St{\'e}fan J and Gommers, Ralf and Virtanen, Pauli and + Cournapeau, David and Wieser, Eric and Taylor, Julian and Berg, + Sebastian and Smith, Nathaniel J and Kern, Robert and Picus, + Matti and Hoyer, Stephan and van Kerkwijk, Marten H and Brett, + Matthew and Haldane, Allan and del R{\'\i}o, Jaime Fern{\'a}ndez + and Wiebe, Mark and Peterson, Pearu and G{\'e}rard-Marchant, + Pierre and Sheppard, Kevin and Reddy, Tyler and Weckesser, Warren + and Abbasi, Hameer and Gohlke, Christoph and Oliphant, Travis E", + abstract = "Array programming provides a powerful, compact and expressive + syntax for accessing, manipulating and operating on data in + vectors, matrices and higher-dimensional arrays. NumPy is the + primary array programming library for the Python language. It has + an essential role in research analysis pipelines in fields as + diverse as physics, chemistry, astronomy, geoscience, biology, + psychology, materials science, engineering, finance and + economics. For example, in astronomy, NumPy was an important part + of the software stack used in the discovery of gravitational + waves1 and in the first imaging of a black hole2. Here we review + how a few fundamental array concepts lead to a simple and + powerful programming paradigm for organizing, exploring and + analysing scientific data. NumPy is the foundation upon which the + scientific Python ecosystem is constructed. It is so pervasive + that several projects, targeting audiences with specialized + needs, have developed their own NumPy-like interfaces and array + objects. Owing to its central position in the ecosystem, NumPy + increasingly acts as an interoperability layer between such array + computation libraries and, together with its application + programming interface (API), provides a flexible framework to + support the next decade of scientific and industrial analysis.", + journal = "Nature", + volume = 585, + number = 7825, + pages = "357--362", + month = sep, + year = 2020, + language = "en" +} + +@ARTICLE{Virtanen2020-tq, + title = "{SciPy} 1.0: fundamental algorithms for scientific computing in + Python", + author = "Virtanen, Pauli and Gommers, Ralf and Oliphant, Travis E and + Haberland, Matt and Reddy, Tyler and Cournapeau, David and + Burovski, Evgeni and Peterson, Pearu and Weckesser, Warren and + Bright, Jonathan and van der Walt, St{\'e}fan J and Brett, + Matthew and Wilson, Joshua and Millman, K Jarrod and Mayorov, + Nikolay and Nelson, Andrew R J and Jones, Eric and Kern, Robert + and Larson, Eric and Carey, C J and Polat, {\.I}lhan and Feng, Yu + and Moore, Eric W and VanderPlas, Jake and Laxalde, Denis and + Perktold, Josef and Cimrman, Robert and Henriksen, Ian and + Quintero, E A and Harris, Charles R and Archibald, Anne M and + Ribeiro, Ant{\^o}nio H and Pedregosa, Fabian and van Mulbregt, + Paul and {SciPy 1.0 Contributors} and Vijaykumar, Aditya and + Bardelli, Alessandro Pietro and Rothberg, Alex and Hilboll, + Andreas and Kloeckner, Andreas and Scopatz, Anthony and Lee, + Antony and Rokem, Ariel and Woods, C Nathan and Fulton, Chad and + Masson, Charles and H{\"a}ggstr{\"o}m, Christian and Fitzgerald, + Clark and Nicholson, David A and Hagen, David R and Pasechnik, + Dmitrii V and Olivetti, Emanuele and Martin, Eric and Wieser, + Eric and Silva, Fabrice and Lenders, Felix and Wilhelm, Florian + and Young, G and Price, Gavin A and Ingold, Gert-Ludwig and + Allen, Gregory E and Lee, Gregory R and Audren, Herv{\'e} and + Probst, Irvin and Dietrich, J{\"o}rg P and Silterra, Jacob and + Webber, James T and Slavi{\v c}, Janko and Nothman, Joel and + Buchner, Johannes and Kulick, Johannes and Sch{\"o}nberger, + Johannes L and de Miranda Cardoso, Jos{\'e} Vin{\'\i}cius and + Reimer, Joscha and Harrington, Joseph and Rodr{\'\i}guez, Juan + Luis Cano and Nunez-Iglesias, Juan and Kuczynski, Justin and + Tritz, Kevin and Thoma, Martin and Newville, Matthew and + K{\"u}mmerer, Matthias and Bolingbroke, Maximilian and Tartre, + Michael and Pak, Mikhail and Smith, Nathaniel J and Nowaczyk, + Nikolai and Shebanov, Nikolay and Pavlyk, Oleksandr and + Brodtkorb, Per A and Lee, Perry and McGibbon, Robert T and + Feldbauer, Roman and Lewis, Sam and Tygier, Sam and Sievert, + Scott and Vigna, Sebastiano and Peterson, Stefan and More, Surhud + and Pudlik, Tadeusz and Oshima, Takuya and Pingel, Thomas J and + Robitaille, Thomas P and Spura, Thomas and Jones, Thouis R and + Cera, Tim and Leslie, Tim and Zito, Tiziano and Krauss, Tom and + Upadhyay, Utkarsh and Halchenko, Yaroslav O and + V{\'a}zquez-Baeza, Yoshiki", + abstract = "Abstract SciPy is an open-source scientific computing library for + the Python programming language. Since its initial release in + 2001, SciPy has become a de facto standard for leveraging + scientific algorithms in Python, with over 600 unique code + contributors, thousands of dependent packages, over 100,000 + dependent repositories and millions of downloads per year. In + this work, we provide an overview of the capabilities and + development practices of SciPy 1.0 and highlight some recent + technical developments.", + journal = "Nat. Methods", + volume = 17, + number = 3, + pages = "261--272", + month = mar, + year = 2020, + language = "en" +} + +@ARTICLE{Hirel2015-ts, + title = "Atomsk: A tool for manipulating and converting atomic data files", + author = "Hirel, Pierre", + abstract = "We present a libre, Open Source command-line program named + Atomsk, that aims at creating and manipulating atomic systems for + the purposes of ab initio calculations, classical atomistic + calculations, and visualization, in the areas of computational + physics and chemistry. The program can run on GNU/Linux, Apple + Mac OS X, and Microsoft Windows platforms. Many file formats are + supported, allowing for easy conversion of atomic configuration + files. The command-line options allow to construct supercells, + insert point defects (vacancies, interstitials), line defects + (dislocations, cracks), plane defects (stacking faults), as well + as other transformations. Several options can be applied + consecutively, allowing for a comprehensive workflow from a unit + cell to the final atomic system. Some modes allow to construct + complex structures, or to perform specific analysis of atomic + systems. Program summary Program title: Atomsk Catalogue + identifier: AEXM\_v1\_0 Program summary + URL:http://cpc.cs.qub.ac.uk/summaries/AEXM\_v1\_0.html Program + obtainable from: CPC Program Library, Queen's University, + Belfast, N. Ireland Licensing provisions: GNU/GPL version 3 or + any later version No. of lines in distributed program, including + test data, etc.: 61,450 No. of bytes in distributed program, + including test data, etc.: 539,898 Distribution format: tar.gz + Programming language: Fortran 90. Computer: All computers with a + Fortran compiler supporting at least Fortran 90. Operating + system: All operating systems with such a compiler. Some of the + Makefiles and scripts depend on a Unix-like system and need + modification under Windows. RAM: Typically 32 bytes $\times$ N, + where N is the number of particles. Classification: 4.14, 7.1. + External routines: LAPACK Nature of problem: Atomistic + simulations require the generation of atomic data files. Few + software are available to construct atomic systems containing + dislocations, especially in anisotropic media. Solution method: + Atomsk is a unified program that allows to generate, convert and + transform atomic systems for the purposes of ab initio + calculations, classical atomistic simulations, or visualization. + It supports many lattice types, all atom chemical species, and + supports systems described with the ionic core--shell model. It + allows to construct dislocations and analyze them, and perform + post-treatment of simulation output files. Restrictions: no + support for molecular bonds; limit of 2 billions particles. + Unusual features: dislocations in anisotropic media; computation + of the Nye tensor; generation of polycrystal from any type of + lattice; support for ionic core--shell models and analysis of + electric polarization. Additional comments: the program and its + documentation are available at: http://atomsk.univ-lille1.fr + Running time: spans from a fraction of a second to several + minutes depending on the number of particles in the atomic + system, the mode, and the machine performance.", + journal = "Comput. Phys. Commun.", + volume = 197, + pages = "212--219", + month = dec, + year = 2015 +} + +@article{Daw1984, + title = {Embedded-atom method: Derivation and application to impurities, surfaces, and other defects in metals}, + author = {Daw, Murray S. and Baskes, Michael I.}, + journal = {Physical Review B}, + volume = {29}, + issue = {12}, + pages = {6443--6453}, + numpages = {0}, + year = {1984}, + month = {Jun}, + publisher = {American Physical Society}, + doi = {10.1103/PhysRevB.29.6443}, + url = {https://link.aps.org/doi/10.1103/PhysRevB.29.6443} +} diff --git a/paper/paper.md b/paper/paper.md new file mode 100644 index 00000000..f76247fe --- /dev/null +++ b/paper/paper.md @@ -0,0 +1,158 @@ +--- +title: 'matscipy: materials science at the atomic scale with Python' +tags: + - Python + - Material Science + - Atomistic simulations +authors: + - name: Petr Grigorev + orcid: 0000-0002-6409-9092 + equal-contrib: true # (This is how you can denote equal contributions between multiple authors) + affiliation: "1" # (Multiple affiliations must be quoted) + - name: Lucas Frérot + orcid: 0000-0002-4138-1052 + equal-contrib: true # (This is how you can denote equal contributions between multiple authors) + affiliation: 2 + - name: Fraser Birks + orcid: 0009-0008-9117-0393 + affiliation: 3 + - name: Adrien Gola + orcid: 0000-0002-5102-1931 + affiliation: "2,4" + - name: Jacek Golebiowski + orcid: 0000-0001-8053-8318 + affiliation: + - name: Jan Grießer + orcid: 0000-0003-2149-6730 + affiliation: 2 + - name: Johannes L. Hörmann + orcid: 0000-0001-5867-695X + affiliation: 2 + - name: Andreas Klemenz + orcid: 0000-0001-5677-5639 + affiliation: 5 + - name: Gianpietro Moras + orcid: 0000-0002-4623-2881 + affiliation: 5 + - name: Wolfram G. Nöhring + orcid: 0000-0003-4203-755X + affiliation: 2 + - name: Jonas A. Oldenstaedt + orcid: 0000-0002-7475-3019 + affiliation: 2 + - name: Thomas Reichenbach + orcid: 0000-0001-7477-6248 + affiliation: 5 + - name: Lakshmi Shenoy + orcid: 0000-0001-5760-3345 + affiliation: 3 + - name: Michael Walter + orcid: 0000-0001-6679-2491 + affiliation: 8 + - name: Simon Wengert + orcid: 0000-0002-8008-1482 + affiliation: 7 + - name: James R. Kermode + orcid: 0000-0001-6755-6271 + equal-contrib: true # (This is how you can denote equal contributions between multiple authors) + corresponding: true # (This is how to denote the corresponding author) + affiliation: 3 + - name: Lars Pastewka + orcid: 0000-0001-8351-7336 + equal-contrib: true # (This is how you can denote equal contributions between multiple authors) + corresponding: true # (This is how to denote the corresponding author) + affiliation: "2,8" +affiliations: + - name: Aix-Marseille Université, CNRS, CINaM UMR 7325, Campus de Luminy, 13288 Marseille, France + index: 1 + - name: Department of Microsystems Engineering, University of Freiburg, 79110 Freiburg, Germany + index: 2 + - name: Warwick Centre for Predictive Modelling, School of Engineering, University of Warwick, Coventry CV4 7AL, United Kingdom + index: 3 + - name: Institute for Applied Materials, Karlsruhe Institute of Technology, Engelbert-Arnold-Straße 4, 76131 Karlsruhe, Germany + index: 4 + - name: Fraunhofer IWM, MikroTribologie Centrum $\mu$TC, Wöhlerstraße 11, 79108, Freiburg, Germany + index: 5 + - name: Department of Materials, Imperial College London, London SW7 2AZ, UK + index: 6 + - name: Fritz Haber Institute of the Max Planck Society, Faradayweg 4-6, D-14195, Berlin, Germany + index: 7 + - name: Cluster of Excellence livMatS, Freiburg Center for Interactive Materials and Bioinspired Technologies, University of Freiburg, Georges-Köhler-Allee 105, 79110, Freiburg, Germany + index: 8 +date: 07 July 2023 +bibliography: paper.bib +--- + +# Summary + +Behaviour of materials is governed by physical phenomena that occur at an extreme range of length and time scales. Computational modelling requires multiscale approaches. Simulation techniques operating on the atomic scale serve as a foundation for such approaches, providing necessary parameters for upper-scale models. The physical models employed for atomic simulations can vary from electronic structure calculations to empirical force fields. However, construction, manipulation and analysis of atomic systems are independent of the given physical model but dependent on the specific application. `matscipy` implements such tools for applications in materials science, including fracture, plasticity, tribology and electrochemistry. + +# Statement of need + +The Python package `matscipy` contains a set of tools for researchers using atomic-scale models in materials science. In atomic-scale modelling, the primary numerical object is a discrete point in three-dimensional space that represents the position of an individual atom. Simulations are often dynamical, where configurations change over time and each atom carries a velocity. Complexity emerges from the interactions of many atoms, and numerical tools are required for generating initial atomic configurations and for analyzing output of such dynamical simulations, most commonly to connect local geometric arrangements of atoms to physical processes. An example, described in more detail below, is the detection of the tip of a crack that moves through a solid body. + +We never see individual atoms at macroscopic scales. To understand the behaviour of everyday objects, atomic-scale information needs to be transferred to the continuum scale. This is the primary objective of multi-scale modelling. `matscipy` focuses on atomic representations of materials, but implements tools for connecting to continuum descriptions in mechanics and transport theory. Each of the application domains described in the following therefore relies on the computation of continuum fields, that is realized through analytic or numerical solutions. + +There is no other package that we are aware of, which fills the particular niche of the application domains in the next section. The package addresses the boundary between atomic-scale and continuum modelling in materials with particular emphasis on plasticity, fracture and tribology. The `atomman` atomistic manipulation toolkit [@AtomMan] and the `atomsk` package [@Hirel2015-ts] have some overlapping objectives but are restricted to a narrower class of materials systems, principally point defects, stacking faults and dislocations. We target interoperability with the widely used Atomic Simulation Environment (ASE) [@Larsen2017], which offers great flexibility by providing a Python interface to tens of simulation codes implementing different physical models. While ASE's policy is to remain pure Python, we augment some of ASE's functionality with more efficient implementations in C, such as the computation of the neighbour list. +Large scale molecular dynamics (MD) simulations are most efficiently performed with optimized codes such as LAMMPS [@Thompson2022], with `matscipy`'s main contribution being to setup input structures and to post-process output trajectories. + +The central class in ASE is the `Atoms` class, which is a container that stores atomic positions, velocities and other properties. `Calculator`s describe relationships between atoms, and are used for example to compute energies and forces, and can be attached to `Atoms` objects. All other `matscipy` functionality is implemented as functions acting on `Atoms` objects. This is comparable to the design of `numpy` [@Harris2020-it] or `scipy` [@Virtanen2020-tq], that are collections of mathematical functions operating on core `array` container objects. In our experience, separating code into functions and containers lowers the barrier to entry for new users and eases testability of the underyling code base. + +We point out that other generic multi-scale coupling packages exist. Examples of open-source codes are `libmultiscale` [@Anciaux2007;@Anciaux2018], `MultiBench` [@Miller2009] and Green's function molecular dynamics (`GFMD`) [@Campana2006;@Pastewka2012]. These packages target two-way coupling between atomic-scale and continuum formulations, while `matscipy` focuses on constructing atomic domains from continuum information and extracting continuum field from atomic structures. `matscipy` has only limited capabilities for two-way coupling, which makes it significantly easier to use and reduces the barrier to engaging in multi-scale modelling of materials. + +# Application domains + +Within materials science, the package has different application domains: + +- **Elasticity.** Solids respond to small external loads through a reversible elastic response. The strength of the response is characterized by the elastic moduli. `matscipy.elasticity` implements functions for computing elastic moduli from small deformation that consider potential symmetries of the underlying atomic system, in particular for crystals. `matscipy` also implements analytic calculation of elastic moduli for some interatomic potentials, described in more detail below. The computation of elastic moduli is a prerequisite for multi-scale modelling of materials, as they are the most basic parameters of continuum material models. `matscipy` was used to study finite-pressure elastic constants and structural stability in crystals [@Griesser2023crystal] and glasses [@Griesser2023glass]. + +- **Plasticity.** For large loads, solids can respond with irreversible deformation. One form of irreversibility is plasticity, that is carried by extended defects, the dislocations, in crystals. The module `matscipy.dislocation` implements tools for studying structure and movement of dislocations. Construction and analysis of model atomic systems is implemented for compact and dissociated screw, as well as edge dislocations in cubic crystals. The implementation supports ideal straight as well as kinked dislocations. Some of the dislocation functionality requires the `atomman` and/or `OVITO` packages as optional dependencies [@AtomMan;@Stukowski2009]. The module was employed in a study of interaction of impurities with screw dislocations in tungsten [@Grigorev2020;@Grigorev2023]. + +- **Fracture mechanics.** Cracking is the process of generating new surface area by splitting the material apart. The module `matscipy.fracture_mechanics` provides functionality for calculating continuum linear elastic displacement fields near crack tips, including support for anisotropy in the elastic response [@Sih1965]. The module also implements generation of atomic structures that are deformed according to this near-tip field. This functionality has been used to quantify lattice trapping, which is the pinning of cracks due to the discreteness of the atomic lattice, and to compare simulations with experimental measurements of crack speeds in silicon [@Kermode2015]. There is also support for flexible boundary conditions in fracture simulations using the formalism proposed by Sinclair [@Sinclair1975], where the finite atomistic domain is coupled to an infinite elastic continuum. Finally, we provide an extension of this approach to give a flexible boundary scheme that uses numerical continuation to obtain full solution paths for cracks [@Buze2021]. + +- **Tribology.** Tribology is the study of two interfaces sliding relative to one another, as encountered in frictional sliding or adhesion. Molecular dynamics simulations of representative volume elements of tribological interfaces are routinely used to gain insights into the atomistic mechanisms underlying friction and wear. The module `matscipy.pressurecoupling` provides tools to perform such simulations under a constant normal load and sliding velocity. It includes an implementation of the pressure coupling algorithm described by @Pastewka2010. By dynamically adjusting the distance between the two sliding surfaces according to the local pressure, the algorithm ensures mechanical boundary conditions that account for the inertia of the bulk material which is not explicitly included in the simulation. This algorithm was used to study friction [@Seidl2021-vn] and wear [@Pastewka2011-rd;@Moras2011-my;@Peguiron2016-wf;@Moras2018-lm;@Reichenbach2021-pi]. + +- **Electrochemistry.** Electrochemistry describes the motion and spatial distribution of charged atoms and molecules (ions) within an external electric field. Classical treatment of charged systems leads to continuous field that describe mean charge distributions, while true atomic systems consist of discrete particles with fixed charges. The `matscipy.electrochemistry` module provides tools that statistically sample discrete coordinate sets from continuum fields and apply steric corrections [@Martinez2009] to avoid overlap of finite size species. To generate continuum charge distributions, the package also contains a control-volume solver [@Selberherr1984] for the one-dimensional Poisson--Nernst--Planck equations [@Bazant2006], as well as an interface to the finite-element solver `FEniCS` [@LoggMardalEtAl2012]. This was used to study friction in electrochemical environments [@Seidl2021;@Hormann2023-ml]. + +# All-purpose atomic analysis tools + +As well as these domain-specific tools, `matscipy` contains general utility functionality which is widely applicable: + +- **Neighbour list.** An efficient linear-scaling neighbour list implemented in + C delivers orders-of-magnitude faster performance for large systems than + the pure Python implementation in ASE [@Larsen2017], see \autoref{fig:nl_time}. + The neighbour list is stored in a data structure comparable to coordinate (`COO`) sparse matrix storage format [@Saad1990], + where two arrays contain the indices of the neighbouring atoms and further arrays store + distance vectors, absolute distances, and other properties associated with an atomic pair. + This allows compact code for evaluating properties that depend on pairs, such as pair-distribution function or interatomic potential energies and forces. Most of the tools described in the following rely on this neighbour list format. + The neighbour list is becoming widely used for post-processing and structural analysis of the trajectories resulting from molecular dynamics simulations, and even to accelerate next-generation message passing neural networks such as MACE [@Batatia2022mace;@Batatia2022Design]. + +![Neighbour list computation time comparison between ASE and Matscipy implementations.\label{fig:nl_time}](nl_time.svg) + +- **Atomic strain.** Continuum mechanics is formulated in terms of strains, which characterizes the fractional shape changes of small volumes. Strains are typically only well defined if averaged over sufficiently large volumes, and extracting strain fields from atomic-scale calculations is notoriously difficult. `matscipy` implements calculations of strain by observing changes in local atomic neighbourhoods across trajectories. It fits a per-atom displacement gradient that minimizes the error in displacement between two configurations as described by @Falk1998. The error resulting from this fit quantifies the non-affine contribution ot the overall displacement and is known as $D^2_\text{min}$. We used this analysis to quantify local strain in the deformation of crystals [@Gola2019;@Gola2020] and glasses [@Jana2019]. + +- **Radial, spatial and angular correlation functions.** Topological order in atomic-scale systems is often characterized by statistical measures of the local atomic environment. The simplest one is the pair-distribution or radial-distribution function, that gives the probability $g_2(r)$ of finding an atom at distance $r$. For three atoms, we can define a probability of finding a specific angle, yielding the angular correlation functions. `matscipy` has utility function for computing these correlation functions to large distances, including the correlation of arbitrary additional per-atom properties such as per-atom strains. + +- **Ring analysis.** Topological order in network glasses can be characterized by statistics of shortest-path rings [@Franzblau1991]. `matscipy` implements calculations of these rings using a backtracking algorithm in C. We regularly use `matscipy` to charactize shortest-path rings in amorphous carbon [@Pastewka2008;@Jana2019]. + +- **Topology building for non-reactive MD simulations.** Non-reactive force fields for MD simulations consist of non-bonded and bonded interaction terms [@Jorgensen1996]. The latter require an explicit specification of the interatomic bonding topology, i.e. which atoms are involved in bond, angle and dihedral interactions. The module `matscipy.opls` provides efficient tools to generate this topology for an atomic structure based on matscipy’s neighbour list, and then assign the relevant force field parameters to each interaction term. Input and output routines for reading and writing the corresponding control files for LAMMPS [@Thompson2022] are implemented in the module `matscipy.io.opls`. We used this functionality in various studies on tribology, wetting and nanoscale rheology [@Mayrhofer2016;@Falk2020;@Reichenbach2020;@vonGoeldel2021;@Falk2022] + +# Interatomic potentials and other calculators + +Besides generating and analysing atomic-scale configurations, `matscipy` implements specific interatomic potentials [@Muser2023]. The goal here is not to provide the most efficient implementation of computing interatomic forces. We rather aim to provide simple implementations for testing new functional forms, or testing new features such as the computation of derivatives of second order. + +- **Interatomic potentials.** The module `matscipy.calculators` has implementations of classical pair-potentials, Coulomb interactions, the embedded-atom method (EAM) [@Daw1984] and other many-body potentials [e.g. @StillingerWeber1985;@Tersoff1989]. + +- **Second-order derivatives.** The thermodynamic and elastic properties of solid materials are closely connected to the Hessian of the overall system, which contains the second derivatives of the total energy with respect to position and macroscopic strains. `matscipy` implements analytic second-order potential derivatives for pair-potentials [@Lennard1931], EAM potentials [@Daw1984], bond-order potentials [@Kumagai2007;@Tersoff1989;@Brenner1990], cluster potentials [@StillingerWeber1985] and electrostatic interaction [@BKS1990]. +This is achieved through a generic mathematical formulation of the manybody total energy [@Griesser2023b] in `matscipy.calculators.manybody`. +The module `matscipy.numerical` additionally provides routines for the numerical (finite-differences) evaluation of these properties. These analytic second-order derivatives allow a fast and accurate computation of the aforementioned properties in crystals, polymers and amorphous solids, even for unstable configurations where numerical methods are not applicable. + +- **Quantum mechanics/molecular mechanics.** The module `matscipy.calculators.mcfm` implements a generalised force-mixing potential [@Bernstein2009] with support for multiple concurrent QM clusters, named MultiClusterForceMixing (MCFM). It has been applied to model failure of graphene-nanotube composites [@Golebiowski2018;@Golebiowski2020]. + +- **Committee models.**. The module `matscipy.calculators.committee` provides support for committees of interatomic potentials with the same functional form but differing parameters, in order to allow the effect of the uncertainty in parameters on model predictions to be estimated. This is typically used with machine learning interatomic potentials (MLIPs). The implementation follows the approach of [@Musil2019] where the ensemble of models is generated by training models on different subsets of a common overall training database. + +# Acknowledgements + +We thank Arnaud Allera, Manuel Aldegunde, Kristof Bal, James Brixey, Alexander Held, Jan Jansen, Till Junge, Henry Lambert, Punit Patel and Zhilin Zheng for contributions and bug fixes. `matscipy` was partially funded by the Deutsche Forschungsgemeinschaft (projects 258153560, 390951807 and 461911253), the European Research Council (ERC StG 757343), the Engineering and Physical Sciences Research Council (grants EP/P002188/1, EP/R012474/1 and EP/R043612/1) and the Leverhulme Trust (grant RPG-2017-191). + +# References diff --git a/pyproject.toml b/pyproject.toml index dcb05288..42aead92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,8 @@ test = [ docs = [ "sphinx", "sphinx-rtd-theme", + "myst_nb", + "nglview" ] [project.urls] diff --git a/tests/manybody/heterogeneous/.gitignore b/tests/manybody/heterogeneous/.gitignore new file mode 100644 index 00000000..97c86cd2 --- /dev/null +++ b/tests/manybody/heterogeneous/.gitignore @@ -0,0 +1 @@ +log.lammps