{ "cells": [ { "cell_type": "markdown", "id": "31a94bbd-34ef-404b-a9be-4f3a216a8c3c", "metadata": {}, "source": [ "# Generating structured meshes\n", "\n", "In most cases, POD can be done directly on a SEM mesh. This of course allows to use derivatice routines present in this framwework to further produce reduced order models.\n", "\n", "In some particular cases, however, having an structured mesh might be beneficial. For this, we give some tools in this notebook.\n", "\n", "This example might have some overlapping topics with example 4.1" ] }, { "cell_type": "markdown", "id": "f9ad6337-6ba4-47c7-b687-0bf119a5b637", "metadata": {}, "source": [ "#### Import general modules" ] }, { "cell_type": "code", "execution_count": 1, "id": "8b4ca2be-46e8-443b-ab44-6d1a60abfec4", "metadata": {}, "outputs": [], "source": [ "# Import required modules\n", "from mpi4py import MPI #equivalent to the use of MPI_init() in C\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "# Get mpi info\n", "comm = MPI.COMM_WORLD\n", "\n", "# This example is designed to work in one rank only\n", "if comm.Get_size() > 1:\n", " raise ValueError(\"This example is designed to run with one rank only\")" ] }, { "cell_type": "markdown", "id": "1702ba96-7854-4a2f-89d2-7f2b2aaa61b1", "metadata": {}, "source": [ "#### Import modules from pysemtools" ] }, { "cell_type": "code", "execution_count": 2, "id": "09a4efe8-3557-4ff7-850e-0a26c8937bd6", "metadata": {}, "outputs": [], "source": [ "from pysemtools.io.ppymech.neksuite import pynekread, pynekwrite" ] }, { "cell_type": "markdown", "id": "63764358", "metadata": {}, "source": [ "## Create an structured mesh\n", "\n", " It is as simple as writing using numpy meshgrid functionality\n", "\n", " In this case we want to get a cylinder" ] }, { "cell_type": "code", "execution_count": 3, "id": "ab0f45ca", "metadata": {}, "outputs": [], "source": [ "import pysemtools.interpolation.pointclouds as pcs\n", "\n", "# Generate the bounding box of the points\n", "r_bbox = [0, 0.05]\n", "th_bbox = [0, 2*np.pi]\n", "z_bbox = [0 , 1]\n", "nx = 30\n", "ny = 30\n", "nz = 80\n", "\n", "# Generate the 1D mesh\n", "r_1d = pcs.generate_1d_arrays(r_bbox, nx, mode=\"equal\")\n", "th_1d = pcs.generate_1d_arrays(th_bbox, ny, mode=\"equal\")\n", "z_1d = pcs.generate_1d_arrays(z_bbox, nz, mode=\"equal\", gain=1)\n", "\n", "# Generate differetials (dr, dth, dz)\n", "dr_1d = pcs.generate_1d_diff(r_1d)\n", "dth_1d = pcs.generate_1d_diff(th_1d, periodic=True) # This is needed to give the same weight to the first and last points as for the other ones. Needed if fourier transform will be applied.\n", "dz_1d = pcs.generate_1d_diff(z_1d)\n", "\n", "# Generate a 3D mesh\n", "r, th, z = np.meshgrid(r_1d, th_1d, z_1d, indexing='ij')\n", "# Generate 3D differentials\n", "dr, dth, dz = np.meshgrid(dr_1d, dth_1d, dz_1d, indexing='ij')\n", "\n", "# Generate xy coordinates, which are needed for probes\n", "x = r*np.cos(th)\n", "y = r*np.sin(th)" ] }, { "cell_type": "markdown", "id": "878b45c8", "metadata": {}, "source": [ "### Mass matrix\n", "\n", "To perform POD, we will need a mass matrix. For this we must find a way to combine the coordinates to get the volume of our domain\n", "\n", "In our case, because we have cylinder, we now that the volume is calculated by:\n", "$$\n", "V = \\int_{z_0}^{z_1} \\int_{\\theta_0}^{\\theta_1} \\int_{r_0}^{r_1} r \\, dr \\, d\\theta \\, dz\n", "$$\n", "\n", "Therefore, our mass matrix will simply be the terms that are multiplied in the integral:\n", "$$\n", "B = r \\, dr \\, d\\theta \\, dz\n", "$$\n", "\n", "If one wants the area for a given angle, then the integral to use is.\n", "$$\n", "A = \\int_{z_0}^{z_1} \\int_{r_0}^{r_1} dr \\, dz\n", "$$" ] }, { "cell_type": "code", "execution_count": null, "id": "c073bec0", "metadata": {}, "outputs": [], "source": [ "# For volume\n", "B = r * dr * dth * dz\n", "# For area given each angle slice\n", "A = dr * dz\n", "\n", "# Verify that the volume of the cylinder is correct\n", "print(np.sum(B))\n", "print(np.sum(A[:,0,:]))\n", "print(r_bbox[1]**2*np.pi*z_bbox[1])\n", "print(r_bbox[1]*z_bbox[1])" ] }, { "cell_type": "markdown", "id": "59f80417", "metadata": {}, "source": [ "Another alternative is to of course interpolate the mass matrix of a SEM mesh into this set of points, if you have it. But we leave that for another time" ] }, { "cell_type": "markdown", "id": "4a6d83d9", "metadata": {}, "source": [ "## Writing the data\n", "\n", "If one wishes to use this information in the probes module, then the xyz coordinates as a list will be needed. So one can create that as well:" ] }, { "cell_type": "code", "execution_count": 5, "id": "d479a5ce", "metadata": {}, "outputs": [], "source": [ "import pysemtools.interpolation.utils as interp_utils\n", "\n", "xyz = interp_utils.transform_from_array_to_list(nx,ny,nz,[x, y, z])" ] }, { "cell_type": "markdown", "id": "32d7bd9e", "metadata": {}, "source": [ "### Writing as csv\n", "\n", "One option is to write everything as a CSV file. In this instance, the data needs to be arrayed as a list already." ] }, { "cell_type": "code", "execution_count": 6, "id": "b4473b2d", "metadata": {}, "outputs": [], "source": [ "fname = 'points.csv'\n", "with open(fname, 'w') as f:\n", " for i in range((xyz.shape[0])):\n", " f.write(f\"{xyz[i][0]},{xyz[i][1]},{xyz[i][2]}\\n\")" ] }, { "cell_type": "markdown", "id": "6a843355", "metadata": {}, "source": [ "### Writing in HDF5\n", "\n", "Another flexible option is to write an HDF5 file. In this case one can include an array list xyz, but also more info." ] }, { "cell_type": "code", "execution_count": 7, "id": "91d3e19e", "metadata": {}, "outputs": [], "source": [ "import h5py\n", "\n", "fname = 'points.hdf5'\n", "with h5py.File(fname, 'w') as f:\n", "\n", " # Create a header\n", " f.attrs['nx'] = nx\n", " f.attrs['ny'] = ny\n", " f.attrs['nz'] = nz\n", " #f.attrs['probe_list_key'] = 'xyz'\n", "\n", " # Include data sets\n", " f.create_dataset('x', data=x)\n", " f.create_dataset('y', data=y)\n", " f.create_dataset('z', data=z)\n", " f.create_dataset('r', data=r)\n", " f.create_dataset('th', data=th)\n", " f.create_dataset('mass', data=B)\n", " f.create_dataset('mass_area', data=A)\n", " #f.create_dataset('xyz', data=xyz)" ] }, { "cell_type": "markdown", "id": "be887e26", "metadata": {}, "source": [ "the interpolation functions will try to find the keyword probe_list_key and use the associated value as probes.\n", "\n", "If the probe_key_list does not exist, it will try to use the key xyz by default. If this one also does not exist, it will assemble the list form the x, y, z values" ] }, { "cell_type": "code", "execution_count": null, "id": "bdee5cb5", "metadata": {}, "outputs": [], "source": [ "print(np.allclose(B[:,0,:], B[:,1,:]))\n", "print(np.allclose(B[:,2,:], B[:,1,:]))\n", "print(np.allclose(B[:,0,:], B[:,-1,:]))\n", "print(np.sum(B[:,0,:])*ny)" ] } ], "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.10.14" } }, "nbformat": 4, "nbformat_minor": 5 }