{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Selecting which parameters to fit" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For complex simulations, there may be a lot of parameters; [a simple argon simulation](../../../tutorials/Argon-a-to-z.ipynb) has two parameters, and later on in this guide we will see a water simulation with a total of 8 - this only gets bigger as the molecules and forces become more complex.\n", "\n", "Thus, a user may only want to refine a subset of all their parameters. In MDMC this can be done in two ways:" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Fixing a Parameter\n", "\n", "`Parameter` objects can be fixed. This can either be set when they are initialised (created) or changed for an existing `Parameter`. By default, an initialised `Parameter` is **not** fixed." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from MDMC.MD.parameters import Parameter\n", "charge = Parameter(value=0.5, name='charge', fixed=True, unit='e')" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Attempting to change a fixed parameter produces a warning and does **not** change the parameter." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "charge.value = 12\n", "print(f\"The value of the charge parameter is: {charge.value}\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For parameters which already exist (for example, a parameter created by a potential), the parameter can be fixed by setting their `fixed` attribute to `True`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sigma = Parameter(1.0, name='sigma', unit='Ang')\n", "print('Is sigma fixed: {}'.format(sigma.fixed))\n", "sigma.fixed = True\n", "print('Is sigma fixed: {}'.format(sigma.fixed))" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Filtering parameters\n", "\n", "Only parameters that are passed to `Control` (as `fit_parameters`) will be refined. While it is simplest to pass all parameters in a `Universe` to `Control`, it is also possible to filter out a subset. To demonstrate this, we use a universe filled with water under a SPCE force field, as used in [running a simulation](running-a-simulation.ipynb)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from MDMC.MD import *\n", "\n", "universe = Universe(dimensions=21.75, constraint_algorithm=Shake(1e-4, 100), electrostatic_solver=PPPM(accuracy=1e-5))\n", "H1 = Atom('H')\n", "H2 = Atom('H', position=(0., 1.63298, 0.))\n", "O = Atom('O', position=(0., 0.81649, 0.57736))\n", "H_coulombic = Coulombic(atoms=[H1, H2], cutoff=10.)\n", "O_coulombic = Coulombic(atoms=O, cutoff=10.)\n", "water_mol = Molecule(position=(0, 0, 0),\n", " velocity=(0, 0, 0),\n", " atoms=[H1, H2, O],\n", " interactions=[Bond((H1, O), (H2, O), constrained=True),\n", " BondAngle(H1, O, H2, constrained=True)],\n", " name='water')\n", "universe.fill(water_mol, num_density=0.03356718472021752)\n", "O_dispersion = Dispersion(universe, [O.atom_type, O.atom_type], cutoff=10., vdw_tail_correction=True)\n", "universe.add_force_field('SPCE')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are 8 parameters in the universe:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(universe.parameters)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So while all 8 parameters can be passed when initiliasing `Control`, they can also be filtered. `Parameters` objects have a number of convenience methods to assist with this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "parameters = universe.parameters\n", "help(parameters)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For example, if only the charge parameters should be refined, `parameters` could be filtered by name:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "charges = parameters.filter_name('charge')\n", "print(charges)\n", "\n", "# charges is a Parameters object\n", "print('\\nThe class of charges is: {}'.format(type(charges)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As each filter returns a `Parameters` object, filters can be chained together. For example, to find the potential strengths of all bonds:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bond_potential_strengths = parameters.filter_name('potential_strength').filter_interaction('Bond')\n", "print(bond_potential_strengths)\n", "\n", "# These operations are commutative\n", "print('\\nThe order these methods are'\n", " ' applied does not matter: {}'.format(bond_potential_strengths\n", " == parameters.filter_interaction('Bond').filter_name('potential_strength')))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to filter parameters based on the properties of the atoms to which they apply. For instance, we can filter the SPCE parameters so that only parameters of interactions on H atoms are shown:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "H_parameters = parameters.filter_atom_attribute('name', 'H')\n", "print(H_parameters)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Finally there is also a more flexible method (`Parameters.filter`) which can be used in conjunction with any function to filter the parameters by some predicate function (a function which takes a parameter and returns True or False according to what is 'filtered out'). This is most easily done with a [lambda function](https://docs.python.org/3/howto/functional.html#small-functions-and-the-lambda-expression) but a regular function can also be used." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def is_length(parameter):\n", " \"\"\"A function which tells you whether the parameter measures length or not.\"\"\"\n", " return parameter.unit == 'Ang'\n", "\n", "length_parameters = parameters.filter(is_length)\n", "print(length_parameters)\n", "\n", "# For those more familiar with Python, this can also be done using a lambda\n", "lambda_length_parameters = parameters.filter(lambda p: p.unit == 'Ang')\n", "print('\\nThe same filter can be achieved using lambdas: {}'.format(lambda_length_parameters == length_parameters))" ] } ], "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.9.6" } }, "nbformat": 4, "nbformat_minor": 2 }