{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Scripting with python\n", "\n", "All files in this tutorial can be downloaded here: [pythonScript.zip](pythonScript.zip) and the [jupyter-notebook](python.ipynb) \n", "\n", "This tutorial will cover how scripting with python/jupyter-notebook is done. Therefore, we automatically generate a mesh with variable mesh discretization, as well as running a simulation with variable input parameters. Our Testexample will be a simple cube, where we apply a sinoidal deformation on top.\n", "\n", "After the automatic simulation we will do a short postprocessing and visualize the results.\n", "\n", "## Lets go through the script\n", "First, lets import all the nessecary libaries. Dont forget to specify the path where those libaries are located. In our case they are in the same folder.\n", "\n", "
\n", " Click to see code-snippet " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2\n", "\n", "import sys\n", "sys.path.append(\"./\")\n", "\n", "import matplotlib.pyplot as plt\n", "\n", "import hdf5_tools\n", "import numpy as np\n", "import cfsResultClass as c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "Now in this section, we first define our parameters for the simulation and mesh and generate a mesh-journal file and simulation-input xml based on templates.\n", "We read in the templates and replacing specific strings with our parameters.\n", "\n", "
\n", " Click to see code-snippet " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "steps = 10\n", "deltaT = 1\n", "amplitude = 0.001\n", "interval = 3\n", "\n", "#Write the simulation input out of a template file\n", "\n", "#creating a unique name for this parameter combination\n", "simulationName = f\"sim_Steps{steps}_dT{deltaT}_Amp{amplitude}_Int{interval}\"\n", "\n", "sim_tmp = open(\"sim_tmp.xml\", \"r\")\n", "sim = open(simulationName + \".xml\", \"w\")\n", "for line in sim_tmp.readlines():\n", " if \"STEPS\" in line:\n", " line = line.replace(\"STEPS\", str(steps))\n", " if \"DELTAT\" in line: \n", " line = line.replace(\"DELTAT\",str(deltaT))\n", " if \"AMPLITUDE\" in line:\n", " line = line.replace(\"AMPLITUDE\", str(amplitude))\n", "\n", " sim.writelines(line)\n", "sim_tmp.close()\n", "sim.close()\n", "\n", "#Write the mesh-journal file\n", "jou_tmp = open(\"UnitCube_tmp.jou\", \"r\")\n", "journalName = f\"UnitCube_f{interval}.jou\"\n", "jou = open(journalName, \"w\")\n", "for line in jou_tmp.readlines():\n", " if \"INTERVAL\" in line:\n", " line = line.replace(\"INTERVAL\", str(interval))\n", " jou.writelines(line)\n", "jou_tmp.close()\n", "jou.close()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "One could aswell use a xml-libary to utilize the full potential of the xml-input. This could be realized with the following code snippet:\n", "
\n", " Click to see code-snippet " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import xml.etree.ElementTree as ET\n", "ns = {'cfs': \"http://www.cfs++.org/simulation\"}\n", "#ET.register_namespace('', \"http://www.cfs++.org/simulation\")\n", "\n", "tree = ET.parse(\"sim_tmp.xml\")\n", "root = tree.getroot()\n", "\n", "#Using XPath to reduce code\n", "for numSteps in root.findall(\".//cfs:numSteps\",ns):\n", " numSteps.text = str(steps)\n", "\n", "for dT in root.findall(\".//cfs:deltaT\",ns):\n", " dT.text = str(deltaT)\n", "\n", "for displacement in root.findall(\".//*[@name='S_T']/cfs:comp\",ns):\n", " displacement.set(\"value\",f\"{amplitude}*sin(t)\")\n", "\n", "# Without XPATH the replacement of the amplitude would look like this:\n", "# for load in root.iter('{http://www.cfs++.org/simulation}displacement'):\n", "# if load.get(\"name\") == \"S_T\":\n", "# for child in list(load):\n", "# if child.get(\"dof\")==\"z\":\n", "# child.set(\"value\",f\"{amplitude}*sin(t)\")\n", "\n", "tree.write(\"newXML.xml\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "After generating the journal file and the simulation-xml-input we have to create the mesh and run the simulation. This is done in the next section.\n", "Here we use jupyter-notebook cell magic with `%%capture output` to supress the output of the cell and the ipython command `!` to run commando commands. If you want to see the terminal command, comment the first line out.\n", "\n", "If you are unsure about the local path of your program, just execute the command `type Programname` in the terminal. This command will return the path were the executable is stored.\n", "\n", "After simulation, we delete all created files to not pollute this folder. This is especially important if storage-place is critical.\n", "\n", "
\n", " Click to see code-snippet " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%capture output \n", "# To supress all output of this cell, MUST be ontop of the cell\n", "\n", "#Create mesh, insert your local path\n", "!/opt/programs/Coreform-Cubit-2021.11/bin/coreform_cubit -batch -nojournal -nographics $journalName\n", "\n", "#Simulate, insert your local path to cfs\n", "!/home/alex/Devel/CFS_BIN/EclipseDebugBuild/bin/cfs $simulationName\n", "\n", "#Delete simulation input files, to not pullute folder\n", "!rm *.info.xml\n", "!rm $simulationName*\n", "!rm $journalName*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "After running the simulation we read in the cfs-result with as a `cfsResult`. This is a class whichs automatically reads in all results. However its currently limited to only read 1 sequence step and 1 region at a time, otherwise an error occurs. But this functionallity could be easly added (by you?).\n", "\n", "
\n", " Click to see code-snippet " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Read in the result with the help of cfsResultClass. (Currently this is only limited for 1 sequence step and one region!)\n", "result = c.cfsResult(f\"./results_hdf5/{simulationName}.cfs\",multistep =1, region=None)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "After reading in the result we do a bit of postprocessing and plot the displacement of one node on the bottom and top of the unitcube.\n", "We also display the vonMises-stress of an element which is placed on top of the cube.\n", "\n", "
\n", " Click to see code-snippet " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Read in Values\n", "#Displacement in Z\n", "#u_z[Nt, Node] = value\n", "u_z = result.mechDisplacement.Values.z\n", "#coord_u[Node,xyz]\n", "coord_u = result.mechDisplacement.Values.coord\n", "#Get all indices from nodes which are placed on top and bottom\n", "idx_u_zTop = np.where(coord_u[:,2] == coord_u[:,2].max())[0]\n", "idx_u_zBot = np.where(coord_u[:,2] == coord_u[:,2].min())[0]\n", "\n", "#Get the timesteps\n", "t = hdf5_tools.get_step_values(f\"./results_hdf5/{simulationName}.cfs\")[0]\n", "\n", "#VonMises Stress\n", "#vonMises[Nt, Element] = value\n", "vonMises = result.vonMisesStress.Values.val\n", "coord_vonMises = result.vonMisesStress.Values.coord\n", "#Get all indices from elements which are placed on top\n", "idx_vonMisesTop = np.where(coord_vonMises[:,2] == coord_vonMises[:,2].max())[0]\n", "\n", "# Plot results\n", "fig, ax = plt.subplots(2, figsize = (5,2*3))\n", "\n", "#plot mechDisplacement\n", "\n", "ax[0].plot(t, u_z[:,idx_u_zTop[0]], label = \"One node placed on top\")\n", "ax[0].plot(t, u_z[:,idx_u_zBot[0]], label = \"One node on bottom\")\n", "ax[0].set_ylabel(\"Displacement in m\")\n", "ax[0].set_xlabel(\"Timesteps\")\n", "ax[0].set_title(\"Displacement over timesteps\")\n", "ax[0].legend()\n", "\n", "ax[1].plot(t, vonMises[:,idx_vonMisesTop[0]], label = \"One element placed on top\")\n", "ax[1].set_ylabel(\"vonMises-Stress in N/m²\")\n", "ax[1].set_xlabel(\"Time in s\")\n", "ax[1].set_title(\"VonMises-Stress over time\")\n", "ax[1].legend()\n", "\n", "fig.suptitle(\"Results\", fontsize = 16)\n", "\n", "#Makes plots prettier (eg. labels dont stand in other plots)\n", "plt.tight_layout()\n", "fig.savefig(\"resultPlot.png\", dpi = 100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "![](resultPlot.png)\n", "\n", "## Further suggestions\n", "* Increase the time discretiation by changing `deltaT`, to get a smoother result\n", "* Create a for-loop for different timesteps\n", "* Save those results in a compact dictinary\n", "* Do a convergence study:\n", " - Compare the difference of the solution at different mesh-discretization\n", " - for different time step sizes\n", " \n", "## Additional tutorials\n", "* have a look at this [python-post-processing-tutorial](../../PostProcessing/PythonPostProcessing/README.md)\n", "* have a look at this [python-post-processing-tutorial](../../PostProcessing/PythonPostProcessing/python2.md)" ] }, { "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.8.5" } }, "nbformat": 4, "nbformat_minor": 4 }