{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Create Functional Data\n",
    "Populate a functional attribute with data fitted using the Python `numpy` library."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import libraries and define a polynomial fit function\n",
    "This example populates a functional attribute for *Ultimate Tensile Strength vs Temperature* in one table from a polynomial fit of the individual attributes in another table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "import numpy as np\n",
    "from GRANTA_MIScriptingToolkit import granta as mpy\n",
    "\n",
    "def My4degPolyFitFunc(x, a, b, c, d, e):\n",
    "    return a*np.power(x, 4) + b*np.power(x, 3) + c*np.power(x, 2) + d*x + e"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Specify database and table\n",
    "The source data will come from the *Tensile Statistical Data* table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "mi = mpy.connect('http://localhost/mi_servicelayer', autologon=True)\n",
    "\n",
    "db = mi.get_db(db_key='MI_Training')\n",
    "db.set_unit_system('Metric', absolute_temperatures=True)\n",
    "\n",
    "table = db.get_table('Tensile Statistical Data')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Search for test data\n",
    "Construct a search for records in which both the *Ultimate Tensile Strength* and *Test Temperature* attributes are populated."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "search_criteria = [table.attributes['Ultimate Tensile Strength'].search_criterion(exists=True),\n",
    "                   table.attributes['Test Temperature'].search_criterion(exists=True)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Perform the search and extract the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "results = table.search_for_records_where(search_criteria)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Extract the attribute values from the returned records into x and y values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "table.bulk_fetch(results, attributes=['Test Temperature', 'Ultimate Tensile Strength'])\n",
    "x_values = [r.attributes['Test Temperature'].points[0] for r in results]\n",
    "y_values = [r.attributes['Ultimate Tensile Strength'].points[0] for r in results]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fit the test data\n",
    "Fit a fourth-order polynomial to your x and y data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "coeffs = np.polyfit(x_values, y_values, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate x and y values for the fitted equation, using the function you defined at the start."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_fit = np.linspace(np.amin(x_values), np.amax(x_values), 20)\n",
    "y_fit = My4degPolyFitFunc(x_fit, *coeffs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a record to store the data in \n",
    "The resulting functional data will be written into the *Design Data* table, using the same unit system."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "designdata = db.get_table('Design Data')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a new record to store your functional data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "now = datetime.now().strftime(\"%c\")\n",
    "recordName = 'STK Example 8:{}'.format(now)\n",
    "record = designdata.create_record(recordName, subsets={'Metals'})\n",
    "record.color = 'Green'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Access the (empty) functional attribute, and view its column headers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Y min (Tens. Ult. Stress (L-dir) with Temp. [MPa])',\n",
       " 'Y max (Tens. Ult. Stress (L-dir) with Temp. [MPa])',\n",
       " 'Temperature [K]',\n",
       " 'Time [hr]',\n",
       " 'Other []',\n",
       " 'Data Type Lab []',\n",
       " 'Estimated point?']"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func = record.attributes['Tens. Ult. Stress (L-dir) with Temp.']\n",
    "func.column_headers"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Populate the functional attribute\n",
    "Add the test data to the functional attribute point-by-point, then view the attribute data. Column headers can be\n",
    "omitted if they aren't required to represent the data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[['Y min (Tens. Ult. Stress (L-dir) with Temp. [MPa])',\n",
       "  'Y max (Tens. Ult. Stress (L-dir) with Temp. [MPa])',\n",
       "  'Temperature [K]',\n",
       "  'Time [hr]',\n",
       "  'Other []',\n",
       "  'Data Type Lab []',\n",
       "  'Estimated point?'],\n",
       " [1263.0048828125,\n",
       "  1263.0048828125,\n",
       "  810.9284057617188,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False],\n",
       " [1734.37646484375,\n",
       "  1734.37646484375,\n",
       "  699.8172607421875,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False],\n",
       " [2078.31, 2078.31, 422.0389938964844, None, None, 'Test Data', False],\n",
       " [1848.140014648438,\n",
       "  1848.140014648438,\n",
       "  588.7060546875,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False],\n",
       " [2399.146240234375,\n",
       "  2399.146240234375,\n",
       "  194.2612762451172,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False],\n",
       " [2189.89, 2189.89, 294.2613938964844, None, None, 'Test Data', False]]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for x, y in zip(x_values, y_values):\n",
    "    func.add_point({'Temperature':x, 'y':y, 'Data Type Lab':'Test Data'})\n",
    "func.data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then add the fitted data to the functional attribute point-by-point, and view the attribute data with series number as an extra column."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[['Y min (Tens. Ult. Stress (L-dir) with Temp. [MPa])',\n",
       "  'Y max (Tens. Ult. Stress (L-dir) with Temp. [MPa])',\n",
       "  'Temperature [K]',\n",
       "  'Time [hr]',\n",
       "  'Other []',\n",
       "  'Data Type Lab []',\n",
       "  'Estimated point?',\n",
       "  'Series number'],\n",
       " [1263.0048828125,\n",
       "  1263.0048828125,\n",
       "  810.9284057617188,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False,\n",
       "  1],\n",
       " [1734.37646484375,\n",
       "  1734.37646484375,\n",
       "  699.8172607421875,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False,\n",
       "  1],\n",
       " [2078.31, 2078.31, 422.0389938964844, None, None, 'Test Data', False, 1],\n",
       " [1848.140014648438,\n",
       "  1848.140014648438,\n",
       "  588.7060546875,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False,\n",
       "  1],\n",
       " [2399.146240234375,\n",
       "  2399.146240234375,\n",
       "  194.2612762451172,\n",
       "  None,\n",
       "  None,\n",
       "  'Test Data',\n",
       "  False,\n",
       "  1],\n",
       " [2189.89, 2189.89, 294.2613938964844, None, None, 'Test Data', False, 1],\n",
       " [2392.1796767662504,\n",
       "  2392.1796767662504,\n",
       "  194.2612762451172,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2333.081679240432,\n",
       "  2333.081679240432,\n",
       "  226.71744095651727,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2274.7406635683915,\n",
       "  2274.7406635683915,\n",
       "  259.17360566791734,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2219.003033513455,\n",
       "  2219.003033513455,\n",
       "  291.62977037931745,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2167.220212523074,\n",
       "  2167.220212523074,\n",
       "  324.0859350907175,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2120.248643728819,\n",
       "  2120.248643728819,\n",
       "  356.5420998021176,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2078.449789946381,\n",
       "  2078.449789946381,\n",
       "  388.9982645135177,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2041.690133675573,\n",
       "  2041.690133675573,\n",
       "  421.4544292249178,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [2009.34117710033,\n",
       "  2009.34117710033,\n",
       "  453.91059393631787,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1980.2794420887071,\n",
       "  1980.2794420887071,\n",
       "  486.366758647718,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1952.8864701928815,\n",
       "  1952.8864701928815,\n",
       "  518.822923359118,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1925.0488226491511,\n",
       "  1925.0488226491511,\n",
       "  551.2790880705181,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1894.1580803779348,\n",
       "  1894.1580803779348,\n",
       "  583.7352527819182,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1857.1108439837724,\n",
       "  1857.1108439837724,\n",
       "  616.1914174933183,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1810.3087337553268,\n",
       "  1810.3087337553268,\n",
       "  648.6475822047184,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1749.6583896653788,\n",
       "  1749.6583896653788,\n",
       "  681.1037469161184,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1670.5714713708344,\n",
       "  1670.5714713708344,\n",
       "  713.5599116275185,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1567.9646582127193,\n",
       "  1567.9646582127193,\n",
       "  746.0160763389187,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1436.2596492161788,\n",
       "  1436.2596492161788,\n",
       "  778.4722410503188,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2],\n",
       " [1269.383163090479,\n",
       "  1269.383163090479,\n",
       "  810.9284057617188,\n",
       "  None,\n",
       "  None,\n",
       "  'Fitted Data',\n",
       "  False,\n",
       "  2]]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for x, y in zip(x_fit, y_fit):\n",
    "    func.add_point({'Temperature':x, 'y':y, 'Data Type Lab':'Fitted Data'})\n",
    "func.data_with_series_number"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Adjust the series linestyles (`series_linestyles` is a dictionary, indexed with integers)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1: 'Markers', 2: 'Lines'}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func.series_linestyles[1] = 'Markers'\n",
    "func.series_linestyles[2] = 'Lines'\n",
    "func.series_linestyles"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Write your changes to MI\n",
    "Set the attributes you've modified to update, and write the new record to the server."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<Record long name:STK Example 8:Tue Mar 23 16:15:05 2021>]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "record.set_attributes([func])\n",
    "mi.update([record])"
   ]
  }
 ],
 "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.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
