1949 lines
1.2 MiB
Plaintext
1949 lines
1.2 MiB
Plaintext
|
|
{
|
||
|
|
"cells": [
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "91e4c90f",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"Step 1: Clean out non-model simulation points: gap heights below 5mm"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 252,
|
||
|
|
"id": "e4ff9106",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [],
|
||
|
|
"source": [
|
||
|
|
"import pandas as pd\n",
|
||
|
|
"import numpy as np\n",
|
||
|
|
"import matplotlib.pyplot as plt\n",
|
||
|
|
"plt.rcParams['text.usetex'] = True"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 253,
|
||
|
|
"id": "32946653",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"<class 'pandas.core.frame.DataFrame'>\n",
|
||
|
|
"RangeIndex: 4471 entries, 0 to 4470\n",
|
||
|
|
"Data columns (total 6 columns):\n",
|
||
|
|
" # Column Non-Null Count Dtype \n",
|
||
|
|
"--- ------ -------------- ----- \n",
|
||
|
|
" 0 currL [A] 4471 non-null int64 \n",
|
||
|
|
" 1 currR [A] 4471 non-null int64 \n",
|
||
|
|
" 2 rollDeg [deg] 4471 non-null float64\n",
|
||
|
|
" 3 GapHeight [mm] 4471 non-null float64\n",
|
||
|
|
" 4 YokeForce.Force_z [newton] 4471 non-null float64\n",
|
||
|
|
" 5 YokeTorque.Torque [mNewtonMeter] 4471 non-null float64\n",
|
||
|
|
"dtypes: float64(4), int64(2)\n",
|
||
|
|
"memory usage: 209.7 KB\n",
|
||
|
|
"\n",
|
||
|
|
"After adding mirrored data:\n",
|
||
|
|
"Total rows: 8281\n",
|
||
|
|
"Roll angle range: -4.0° to 4.0°\n",
|
||
|
|
"Unique roll angles: [np.float64(-4.0), np.float64(-3.0), np.float64(-2.0), np.float64(-1.5), np.float64(-1.0), np.float64(-0.5), np.float64(0.0), np.float64(0.5), np.float64(1.0), np.float64(1.5), np.float64(2.0), np.float64(3.0), np.float64(4.0)]\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"magDf = pd.read_csv(\"Ansys Results 12-9.csv\")\n",
|
||
|
|
"magDf.info()\n",
|
||
|
|
"\n",
|
||
|
|
"# Condition: Drop rows where 'GapHeight' is less than 5\n",
|
||
|
|
"condition = magDf['GapHeight [mm]'] < 5\n",
|
||
|
|
"\n",
|
||
|
|
"# Drop the rows that satisfy the condition (i.e., keep rows where the condition is False)\n",
|
||
|
|
"magDf = magDf[~condition]\n",
|
||
|
|
"\n",
|
||
|
|
"# Create mirrored data with negative roll angles\n",
|
||
|
|
"# For non-zero roll angles, create symmetric data by:\n",
|
||
|
|
"# - Negating roll angle\n",
|
||
|
|
"# - Swapping currL and currR\n",
|
||
|
|
"# - Negating torque (force remains the same due to symmetry)\n",
|
||
|
|
"\n",
|
||
|
|
"# Filter for non-zero roll angles\n",
|
||
|
|
"non_zero_roll = magDf[magDf['rollDeg [deg]'] != 0]\n",
|
||
|
|
"\n",
|
||
|
|
"# Create the mirrored rows\n",
|
||
|
|
"mirrored_data = non_zero_roll.copy()\n",
|
||
|
|
"mirrored_data['rollDeg [deg]'] = -non_zero_roll['rollDeg [deg]']\n",
|
||
|
|
"mirrored_data['currL [A]'], mirrored_data['currR [A]'] = non_zero_roll['currR [A]'].values, non_zero_roll['currL [A]'].values\n",
|
||
|
|
"mirrored_data['YokeTorque.Torque [mNewtonMeter]'] = -non_zero_roll['YokeTorque.Torque [mNewtonMeter]']\n",
|
||
|
|
"# Force remains unchanged\n",
|
||
|
|
"\n",
|
||
|
|
"# Combine original and mirrored data\n",
|
||
|
|
"magDf = pd.concat([magDf, mirrored_data], ignore_index=True)\n",
|
||
|
|
"\n",
|
||
|
|
"# Sort by rollDeg for better organization\n",
|
||
|
|
"magDf = magDf.sort_values(['rollDeg [deg]', 'currL [A]', 'currR [A]', 'GapHeight [mm]']).reset_index(drop=True)\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"\\nAfter adding mirrored data:\")\n",
|
||
|
|
"print(f\"Total rows: {len(magDf)}\")\n",
|
||
|
|
"print(f\"Roll angle range: {magDf['rollDeg [deg]'].min():.1f}° to {magDf['rollDeg [deg]'].max():.1f}°\")\n",
|
||
|
|
"print(f\"Unique roll angles: {sorted(magDf['rollDeg [deg]'].unique())}\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 254,
|
||
|
|
"id": "4fe774dd",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"<class 'pandas.core.frame.DataFrame'>\n",
|
||
|
|
"RangeIndex: 8281 entries, 0 to 8280\n",
|
||
|
|
"Data columns (total 6 columns):\n",
|
||
|
|
" # Column Non-Null Count Dtype \n",
|
||
|
|
"--- ------ -------------- ----- \n",
|
||
|
|
" 0 currL [A] 8281 non-null int64 \n",
|
||
|
|
" 1 currR [A] 8281 non-null int64 \n",
|
||
|
|
" 2 rollDeg [deg] 8281 non-null float64\n",
|
||
|
|
" 3 GapHeight [mm] 8281 non-null float64\n",
|
||
|
|
" 4 YokeForce.Force_z [newton] 8281 non-null float64\n",
|
||
|
|
" 5 YokeTorque.Torque [mNewtonMeter] 8281 non-null float64\n",
|
||
|
|
"dtypes: float64(4), int64(2)\n",
|
||
|
|
"memory usage: 388.3 KB\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"magDf.info()"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "ecd1f124",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### After removing non-model rows, we have 4459 rows, which matches the number of simulations conducted in Ansys."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 255,
|
||
|
|
"id": "1a76017e",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 640x480 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"first_subset = magDf.iloc[0:13]\n",
|
||
|
|
"x = first_subset[\"GapHeight [mm]\"]\n",
|
||
|
|
"x = 1/x\n",
|
||
|
|
"y = first_subset[\"YokeForce.Force_z [newton]\"]\n",
|
||
|
|
"plt.plot(x, y)\n",
|
||
|
|
"plt.title(r\"$-15A, -15A, 0^\\circ roll$\")\n",
|
||
|
|
"plt.xlabel(\"Inverse Gap Height (1/mm)\")\n",
|
||
|
|
"plt.ylabel(\"Force on magnetic yoke (N)\")\n",
|
||
|
|
"plt.show()"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 256,
|
||
|
|
"id": "1ba9a1b6",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"<class 'pandas.core.frame.DataFrame'>\n",
|
||
|
|
"RangeIndex: 8281 entries, 0 to 8280\n",
|
||
|
|
"Data columns (total 7 columns):\n",
|
||
|
|
" # Column Non-Null Count Dtype \n",
|
||
|
|
"--- ------ -------------- ----- \n",
|
||
|
|
" 0 currL [A] 8281 non-null int64 \n",
|
||
|
|
" 1 currR [A] 8281 non-null int64 \n",
|
||
|
|
" 2 rollDeg [deg] 8281 non-null float64\n",
|
||
|
|
" 3 GapHeight [mm] 8281 non-null float64\n",
|
||
|
|
" 4 YokeForce.Force_z [newton] 8281 non-null float64\n",
|
||
|
|
" 5 YokeTorque.Torque [mNewtonMeter] 8281 non-null float64\n",
|
||
|
|
" 6 invGap 8281 non-null float64\n",
|
||
|
|
"dtypes: float64(5), int64(2)\n",
|
||
|
|
"memory usage: 453.0 KB\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"text/html": [
|
||
|
|
"<div>\n",
|
||
|
|
"<style scoped>\n",
|
||
|
|
" .dataframe tbody tr th:only-of-type {\n",
|
||
|
|
" vertical-align: middle;\n",
|
||
|
|
" }\n",
|
||
|
|
"\n",
|
||
|
|
" .dataframe tbody tr th {\n",
|
||
|
|
" vertical-align: top;\n",
|
||
|
|
" }\n",
|
||
|
|
"\n",
|
||
|
|
" .dataframe thead th {\n",
|
||
|
|
" text-align: right;\n",
|
||
|
|
" }\n",
|
||
|
|
"</style>\n",
|
||
|
|
"<table border=\"1\" class=\"dataframe\">\n",
|
||
|
|
" <thead>\n",
|
||
|
|
" <tr style=\"text-align: right;\">\n",
|
||
|
|
" <th></th>\n",
|
||
|
|
" <th>currL [A]</th>\n",
|
||
|
|
" <th>currR [A]</th>\n",
|
||
|
|
" <th>rollDeg [deg]</th>\n",
|
||
|
|
" <th>GapHeight [mm]</th>\n",
|
||
|
|
" <th>YokeForce.Force_z [newton]</th>\n",
|
||
|
|
" <th>YokeTorque.Torque [mNewtonMeter]</th>\n",
|
||
|
|
" <th>invGap</th>\n",
|
||
|
|
" </tr>\n",
|
||
|
|
" </thead>\n",
|
||
|
|
" <tbody>\n",
|
||
|
|
" <tr>\n",
|
||
|
|
" <th>0</th>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-4.0</td>\n",
|
||
|
|
" <td>6.0</td>\n",
|
||
|
|
" <td>260.518631</td>\n",
|
||
|
|
" <td>-6976.851677</td>\n",
|
||
|
|
" <td>0.166667</td>\n",
|
||
|
|
" </tr>\n",
|
||
|
|
" <tr>\n",
|
||
|
|
" <th>1</th>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-4.0</td>\n",
|
||
|
|
" <td>8.0</td>\n",
|
||
|
|
" <td>163.529872</td>\n",
|
||
|
|
" <td>-3335.704070</td>\n",
|
||
|
|
" <td>0.125000</td>\n",
|
||
|
|
" </tr>\n",
|
||
|
|
" <tr>\n",
|
||
|
|
" <th>2</th>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-4.0</td>\n",
|
||
|
|
" <td>9.0</td>\n",
|
||
|
|
" <td>136.554807</td>\n",
|
||
|
|
" <td>-2506.094902</td>\n",
|
||
|
|
" <td>0.111111</td>\n",
|
||
|
|
" </tr>\n",
|
||
|
|
" <tr>\n",
|
||
|
|
" <th>3</th>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-4.0</td>\n",
|
||
|
|
" <td>10.0</td>\n",
|
||
|
|
" <td>117.403213</td>\n",
|
||
|
|
" <td>-1959.725693</td>\n",
|
||
|
|
" <td>0.100000</td>\n",
|
||
|
|
" </tr>\n",
|
||
|
|
" <tr>\n",
|
||
|
|
" <th>4</th>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-15</td>\n",
|
||
|
|
" <td>-4.0</td>\n",
|
||
|
|
" <td>10.5</td>\n",
|
||
|
|
" <td>109.107025</td>\n",
|
||
|
|
" <td>-1759.467304</td>\n",
|
||
|
|
" <td>0.095238</td>\n",
|
||
|
|
" </tr>\n",
|
||
|
|
" </tbody>\n",
|
||
|
|
"</table>\n",
|
||
|
|
"</div>"
|
||
|
|
],
|
||
|
|
"text/plain": [
|
||
|
|
" currL [A] currR [A] rollDeg [deg] GapHeight [mm] \\\n",
|
||
|
|
"0 -15 -15 -4.0 6.0 \n",
|
||
|
|
"1 -15 -15 -4.0 8.0 \n",
|
||
|
|
"2 -15 -15 -4.0 9.0 \n",
|
||
|
|
"3 -15 -15 -4.0 10.0 \n",
|
||
|
|
"4 -15 -15 -4.0 10.5 \n",
|
||
|
|
"\n",
|
||
|
|
" YokeForce.Force_z [newton] YokeTorque.Torque [mNewtonMeter] invGap \n",
|
||
|
|
"0 260.518631 -6976.851677 0.166667 \n",
|
||
|
|
"1 163.529872 -3335.704070 0.125000 \n",
|
||
|
|
"2 136.554807 -2506.094902 0.111111 \n",
|
||
|
|
"3 117.403213 -1959.725693 0.100000 \n",
|
||
|
|
"4 109.107025 -1759.467304 0.095238 "
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"execution_count": 256,
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "execute_result"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# sooooo it looks like we can invert the entire gap height column and see how that works out...\n",
|
||
|
|
"magDf[\"invGap\"] = magDf[\"GapHeight [mm]\"].transform(lambda x: 1/x)\n",
|
||
|
|
"magDf.info()\n",
|
||
|
|
"magDf.head()"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "9cd13fd5",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"## Let's try fitting a polynomial to this..."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 257,
|
||
|
|
"id": "bd16a120",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Force Coeffs: [ 8.65048568e-02 2.83478670e-01 2.86948934e-01 1.08940596e-08\n",
|
||
|
|
" 3.53295630e+02 -1.79173891e-03 -1.72965676e-03 -2.24271788e-02\n",
|
||
|
|
" -1.14467613e+01 -2.16073339e-03 2.24271784e-02 -1.16266321e+01\n",
|
||
|
|
" 3.17540502e-01 -3.27454733e-07 6.56934794e+03 -4.22076726e-05\n",
|
||
|
|
" -1.06778321e-05 -2.15844466e-04 1.18908940e-01 -7.64607867e-06\n",
|
||
|
|
" -5.48829375e-11 1.01686170e-01 -1.01983867e-02 1.54885337e-01\n",
|
||
|
|
" -1.03778353e+02 -4.02599103e-05 2.15844465e-04 1.29958895e-01\n",
|
||
|
|
" -1.01470998e-02 -1.54885338e-01 -1.00615042e+02 -2.27704400e-11\n",
|
||
|
|
" 2.06864029e+00 4.12237426e-06 -4.19530517e+04 -3.84320174e-07\n",
|
||
|
|
" 3.91430910e-07 -7.41838448e-07 7.93047110e-04 -4.85822966e-07\n",
|
||
|
|
" -2.53746404e-06 1.17129763e-04 -2.29814334e-04 4.03023202e-04\n",
|
||
|
|
" 2.97296750e-01 -1.05010723e-07 2.53746950e-06 -3.78165612e-06\n",
|
||
|
|
" -4.82390439e-05 4.89741581e-11 -5.68285020e-01 3.49652511e-03\n",
|
||
|
|
" 4.38290920e-01 -8.84140413e+00 7.71042933e+02 1.32237801e-07\n",
|
||
|
|
" 7.41836402e-07 7.27510011e-04 -2.31026421e-04 -4.03023164e-04\n",
|
||
|
|
" 1.91281528e-01 -3.49652511e-03 4.35555848e-01 8.84140413e+00\n",
|
||
|
|
" 7.49444073e+02 -3.20760957e-02 8.68958239e-11 -1.33481028e+02\n",
|
||
|
|
" -2.57808938e-05 7.71176016e+04 3.63953720e-08 1.21055024e-08\n",
|
||
|
|
" 1.38326698e-07 -2.26843438e-05 2.02887129e-08 -1.26534241e-07\n",
|
||
|
|
" -5.30120246e-06 8.63665264e-08 4.12729349e-05 -8.00925426e-04\n",
|
||
|
|
" 2.31829311e-08 -7.81597009e-14 1.06948576e-05 -7.75308990e-07\n",
|
||
|
|
" 3.44198871e-05 7.75195649e-04 9.67991934e-06 3.94281512e-03\n",
|
||
|
|
" 1.52654026e-01 -5.85408095e-01 2.66528630e-08 1.26534701e-07\n",
|
||
|
|
" -1.16266392e-07 -8.31898546e-07 -3.44199276e-05 1.37334985e-03\n",
|
||
|
|
" 1.13686838e-12 7.56972819e-04 -1.51062274e-10 1.67361829e+00\n",
|
||
|
|
" -1.53624143e-04 -5.22167175e-02 -4.26268983e+00 -1.86825486e+01\n",
|
||
|
|
" -1.80981192e+03 3.91170545e-08 -1.38327096e-07 -2.90007810e-05\n",
|
||
|
|
" 5.72623549e-08 -4.12728969e-05 -4.53297517e-04 -9.67991608e-06\n",
|
||
|
|
" 3.95070565e-03 -1.52654026e-01 -2.35783092e-01 -1.49754215e-04\n",
|
||
|
|
" 5.22167175e-02 -4.24980327e+00 1.86825486e+01 -1.75997408e+03\n",
|
||
|
|
" 8.10018719e-13 4.47231699e-01 -6.74644618e-10 1.08183457e+03\n",
|
||
|
|
" 6.28164204e-05 4.14731647e+04]\n",
|
||
|
|
"Torque Coeffs: [-8.37822526e-01 8.73852003e+00 -8.81670159e+00 -6.05820760e+01\n",
|
||
|
|
" -9.82723477e+01 -8.14346585e-02 -8.55576963e-03 -1.26586329e+00\n",
|
||
|
|
" -2.28782708e+02 7.92025711e-02 -1.26586329e+00 2.32329851e+02\n",
|
||
|
|
" 1.59724629e-01 2.40322166e+02 1.91664391e+03 -1.60379303e-03\n",
|
||
|
|
" 1.33688477e-03 -1.09883433e-02 4.99001700e+00 -1.13086438e-03\n",
|
||
|
|
" -5.01223108e-04 2.95246424e-01 -5.53027214e-01 1.40436063e+01\n",
|
||
|
|
" -7.51330186e+03 1.58513566e-03 -1.09883429e-02 -5.01157074e+00\n",
|
||
|
|
" 5.39147361e-01 1.40436062e+01 7.47720716e+03 8.04333341e+00\n",
|
||
|
|
" -9.27394128e-01 5.61703427e+04 -1.36591927e+04 -8.85042438e-05\n",
|
||
|
|
" 5.78298075e-05 -3.32036247e-04 1.42290760e-02 9.30853480e-06\n",
|
||
|
|
" -1.81943202e-04 4.52449917e-03 -1.03937257e-02 -1.18018286e-01\n",
|
||
|
|
" 1.97297585e+01 -5.50803617e-05 -1.81943184e-04 -3.44844254e-03\n",
|
||
|
|
" 5.10219152e-05 -1.04187718e-01 -3.02785086e+00 2.03017272e-01\n",
|
||
|
|
" 2.26755126e+01 -3.31637756e+02 4.95131779e+04 9.86641321e-05\n",
|
||
|
|
" -3.32036235e-04 -1.38739663e-02 1.04393194e-02 -1.18018286e-01\n",
|
||
|
|
" -1.90316042e+01 2.03017272e-01 -2.25845774e+01 -3.31637756e+02\n",
|
||
|
|
" -4.94386583e+04 -4.28912237e-03 -2.85648016e+02 -2.71181178e+00\n",
|
||
|
|
" -4.00600711e+05 2.87297159e+04 1.94951433e-06 -5.18825195e-06\n",
|
||
|
|
" -7.73369698e-06 5.36213855e-04 1.10845889e-06 -1.84292367e-06\n",
|
||
|
|
" -4.65904477e-04 6.42868090e-06 3.08080330e-03 -1.09698315e-03\n",
|
||
|
|
" -1.62664310e-06 6.42939733e-06 -1.00144164e-04 -2.50117739e-06\n",
|
||
|
|
" 1.71620505e-03 -2.14976339e-02 7.20307350e-04 1.76411729e-01\n",
|
||
|
|
" 8.07086563e+00 -3.68279938e+01 4.55521462e-06 -1.84292912e-06\n",
|
||
|
|
" 4.37142445e-04 -1.60120928e-06 1.71620478e-03 1.99692775e-02\n",
|
||
|
|
" 1.48517188e-04 -1.19886032e-03 1.78969361e+00 9.92388781e+00\n",
|
||
|
|
" -7.19411831e-03 -2.98153202e+00 -2.04344115e+02 -1.80247522e+03\n",
|
||
|
|
" -1.11849627e+05 -1.66016310e-06 -7.73369888e-06 -7.23357540e-04\n",
|
||
|
|
" -4.16891285e-06 3.08080338e-03 -6.84014931e-03 7.20307358e-04\n",
|
||
|
|
" -1.75664646e-01 8.07086563e+00 3.49676904e+01 7.76960002e-03\n",
|
||
|
|
" -2.98153202e+00 2.03737293e+02 -1.80247522e+03 1.12167508e+05\n",
|
||
|
|
" 4.69772057e-02 -2.00825694e-02 2.13341632e+03 5.37309129e+01\n",
|
||
|
|
" 1.24121750e+06 1.52681310e+04]\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"from sklearn.preprocessing import PolynomialFeatures\n",
|
||
|
|
"from sklearn.linear_model import LinearRegression\n",
|
||
|
|
"\n",
|
||
|
|
"# 1. Load your Ansys CSV - using invGap for modeling\n",
|
||
|
|
"X = magDf[['currL [A]', 'currR [A]', 'rollDeg [deg]', 'invGap']]\n",
|
||
|
|
"y = magDf[['YokeForce.Force_z [newton]', 'YokeTorque.Torque [mNewtonMeter]']]\n",
|
||
|
|
"\n",
|
||
|
|
"# 2. Create Features (e.g. z^2, z^3, z*IL, etc.)\n",
|
||
|
|
"poly = PolynomialFeatures(degree=5) \n",
|
||
|
|
"X_poly = poly.fit_transform(X)\n",
|
||
|
|
"\n",
|
||
|
|
"# 3. Fit\n",
|
||
|
|
"model = LinearRegression()\n",
|
||
|
|
"model.fit(X_poly, y)\n",
|
||
|
|
"\n",
|
||
|
|
"# 4. Extract Equation\n",
|
||
|
|
"print(\"Force Coeffs:\", model.coef_[0])\n",
|
||
|
|
"print(\"Torque Coeffs:\", model.coef_[1])"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "dd6861d0",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"## Compare Model Predictions with Raw Data"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 258,
|
||
|
|
"id": "dc79d9fe",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1400x500 with 2 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Force R² Score: 0.999873\n",
|
||
|
|
"Torque R² Score: 0.999661\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Get predictions from the model\n",
|
||
|
|
"y_pred = model.predict(X_poly)\n",
|
||
|
|
"\n",
|
||
|
|
"# Extract force and torque predictions\n",
|
||
|
|
"force_pred = y_pred[:, 0]\n",
|
||
|
|
"torque_pred = y_pred[:, 1]\n",
|
||
|
|
"\n",
|
||
|
|
"# Extract actual values\n",
|
||
|
|
"force_actual = y.iloc[:, 0].values\n",
|
||
|
|
"torque_actual = y.iloc[:, 1].values\n",
|
||
|
|
"\n",
|
||
|
|
"# Create comparison plots\n",
|
||
|
|
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot 1: Force comparison\n",
|
||
|
|
"axes[0].scatter(force_actual, force_pred, alpha=0.5, s=10)\n",
|
||
|
|
"axes[0].plot([force_actual.min(), force_actual.max()], \n",
|
||
|
|
" [force_actual.min(), force_actual.max()], \n",
|
||
|
|
" 'r--', linewidth=2, label='Perfect Fit')\n",
|
||
|
|
"axes[0].set_xlabel('Actual Force (N)', fontsize=12)\n",
|
||
|
|
"axes[0].set_ylabel('Predicted Force (N)', fontsize=12)\n",
|
||
|
|
"axes[0].set_title('Force: Predicted vs Actual', fontsize=14)\n",
|
||
|
|
"axes[0].legend()\n",
|
||
|
|
"axes[0].grid(True, alpha=0.3)\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot 2: Torque comparison\n",
|
||
|
|
"axes[1].scatter(torque_actual, torque_pred, alpha=0.5, s=10)\n",
|
||
|
|
"axes[1].plot([torque_actual.min(), torque_actual.max()], \n",
|
||
|
|
" [torque_actual.min(), torque_actual.max()], \n",
|
||
|
|
" 'r--', linewidth=2, label='Perfect Fit')\n",
|
||
|
|
"axes[1].set_xlabel('Actual Torque (mN·m)', fontsize=12)\n",
|
||
|
|
"axes[1].set_ylabel('Predicted Torque (mN·m)', fontsize=12)\n",
|
||
|
|
"axes[1].set_title('Torque: Predicted vs Actual', fontsize=14)\n",
|
||
|
|
"axes[1].legend()\n",
|
||
|
|
"axes[1].grid(True, alpha=0.3)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"# Calculate R² scores\n",
|
||
|
|
"from sklearn.metrics import r2_score\n",
|
||
|
|
"print(f\"Force R² Score: {r2_score(force_actual, force_pred):.6f}\")\n",
|
||
|
|
"print(f\"Torque R² Score: {r2_score(torque_actual, torque_pred):.6f}\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "5a5d0634",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Visualize a specific subset (like the first 13 rows)"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 259,
|
||
|
|
"id": "837a814f",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1000x600 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# For the first subset (same as you plotted earlier)\n",
|
||
|
|
"subset_indices = range(0, 13)\n",
|
||
|
|
"x_subset = magDf.iloc[subset_indices][\"GapHeight [mm]\"]\n",
|
||
|
|
"y_actual_subset = magDf.iloc[subset_indices][\"YokeForce.Force_z [newton]\"]\n",
|
||
|
|
"y_pred_subset = force_pred[subset_indices]\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot actual vs predicted for this specific condition\n",
|
||
|
|
"plt.figure(figsize=(10, 6))\n",
|
||
|
|
"plt.plot(x_subset, y_actual_subset, 'o-', label='Actual (Ansys)', linewidth=2, markersize=8)\n",
|
||
|
|
"plt.plot(x_subset, y_pred_subset, 's--', label='Predicted (Model)', linewidth=2, markersize=6)\n",
|
||
|
|
"plt.title(r\"$-15A, -15A, 0^\\circ$ roll: Model vs Ansys\", fontsize=14)\n",
|
||
|
|
"plt.xlabel(\"Gap Height (mm)\", fontsize=12)\n",
|
||
|
|
"plt.ylabel(\"Force on magnetic yoke (N)\", fontsize=12)\n",
|
||
|
|
"plt.legend(fontsize=11)\n",
|
||
|
|
"plt.grid(True, alpha=0.3)\n",
|
||
|
|
"plt.show()"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "b46ce772",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"## Better Interpolation Methods\n",
|
||
|
|
"\n",
|
||
|
|
"Let's improve interpolation accuracy without manually guessing polynomial order."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "a019f402",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Method 1: Cross-Validation to Find Best Polynomial Degree\n",
|
||
|
|
"\n",
|
||
|
|
"Automatically test different polynomial degrees and find the one with best validation score."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 260,
|
||
|
|
"id": "fd12d56b",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Degree 1: Train R² = 0.752592, Test R² = 0.757407\n",
|
||
|
|
"Degree 2: Train R² = 0.965022, Test R² = 0.965732\n",
|
||
|
|
"Degree 3: Train R² = 0.993618, Test R² = 0.993764\n",
|
||
|
|
"Degree 4: Train R² = 0.998598, Test R² = 0.998637\n",
|
||
|
|
"Degree 5: Train R² = 0.999764, Test R² = 0.999752\n",
|
||
|
|
"Degree 6: Train R² = 0.999932, Test R² = 0.999927\n",
|
||
|
|
"Degree 6: Train R² = 0.999932, Test R² = 0.999927\n",
|
||
|
|
"Degree 7: Train R² = 0.999950, Test R² = 0.999947\n",
|
||
|
|
"Degree 7: Train R² = 0.999950, Test R² = 0.999947\n",
|
||
|
|
"Degree 8: Train R² = 0.999889, Test R² = 0.999802\n",
|
||
|
|
"Degree 8: Train R² = 0.999889, Test R² = 0.999802\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1000x600 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"\n",
|
||
|
|
"Best polynomial degree: 7\n",
|
||
|
|
"Best test R² score: 0.999947\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"from sklearn.model_selection import cross_val_score\n",
|
||
|
|
"from sklearn.pipeline import Pipeline\n",
|
||
|
|
"\n",
|
||
|
|
"# Split data for proper validation\n",
|
||
|
|
"from sklearn.model_selection import train_test_split\n",
|
||
|
|
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
|
||
|
|
"\n",
|
||
|
|
"# Test polynomial degrees from 1 to 8\n",
|
||
|
|
"degrees = range(1, 9)\n",
|
||
|
|
"train_scores_force = []\n",
|
||
|
|
"test_scores_force = []\n",
|
||
|
|
"train_scores_torque = []\n",
|
||
|
|
"test_scores_torque = []\n",
|
||
|
|
"\n",
|
||
|
|
"for degree in degrees:\n",
|
||
|
|
" # Create pipeline\n",
|
||
|
|
" pipe = Pipeline([\n",
|
||
|
|
" ('poly', PolynomialFeatures(degree=degree)),\n",
|
||
|
|
" ('model', LinearRegression())\n",
|
||
|
|
" ])\n",
|
||
|
|
" \n",
|
||
|
|
" # Fit on training data\n",
|
||
|
|
" pipe.fit(X_train, y_train)\n",
|
||
|
|
" \n",
|
||
|
|
" # Score on both sets\n",
|
||
|
|
" train_score = pipe.score(X_train, y_train)\n",
|
||
|
|
" test_score = pipe.score(X_test, y_test)\n",
|
||
|
|
" \n",
|
||
|
|
" # Store scores (using overall R² for multi-output)\n",
|
||
|
|
" train_scores_force.append(train_score)\n",
|
||
|
|
" test_scores_force.append(test_score)\n",
|
||
|
|
" \n",
|
||
|
|
" print(f\"Degree {degree}: Train R² = {train_score:.6f}, Test R² = {test_score:.6f}\")\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot the results\n",
|
||
|
|
"plt.figure(figsize=(10, 6))\n",
|
||
|
|
"plt.plot(degrees, train_scores_force, 'o-', label='Training Score', linewidth=2, markersize=8)\n",
|
||
|
|
"plt.plot(degrees, test_scores_force, 's-', label='Test Score', linewidth=2, markersize=8)\n",
|
||
|
|
"plt.xlabel('Polynomial Degree', fontsize=12)\n",
|
||
|
|
"plt.ylabel('R² Score', fontsize=12)\n",
|
||
|
|
"plt.title('Model Performance vs Polynomial Degree', fontsize=14)\n",
|
||
|
|
"plt.legend(fontsize=11)\n",
|
||
|
|
"plt.grid(True, alpha=0.3)\n",
|
||
|
|
"plt.xticks(degrees)\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"# Find best degree\n",
|
||
|
|
"best_degree = degrees[np.argmax(test_scores_force)]\n",
|
||
|
|
"print(f\"\\nBest polynomial degree: {best_degree}\")\n",
|
||
|
|
"print(f\"Best test R² score: {max(test_scores_force):.6f}\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "86aaa7f5",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Visualize Best Polynomial Function vs Ansys Data\n",
|
||
|
|
"\n",
|
||
|
|
"Plot the continuous polynomial function to check for overfitting/oscillations between data points."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 261,
|
||
|
|
"id": "8270efa4",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [],
|
||
|
|
"source": [
|
||
|
|
"best_degree = 6 # Helps with aliasing outside of recorded data..."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 262,
|
||
|
|
"id": "d94aa4c1",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Using polynomial degree: 6\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1200x700 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Checking for overfitting indicators:\n",
|
||
|
|
"Number of actual data points in this condition: 13\n",
|
||
|
|
"Gap spacing: 1.50 mm average\n",
|
||
|
|
"Polynomial curve resolution: 500 points over 25.0 mm range\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Train best degree polynomial on full dataset\n",
|
||
|
|
"poly_best = PolynomialFeatures(degree=best_degree)\n",
|
||
|
|
"X_poly_best = poly_best.fit_transform(X)\n",
|
||
|
|
"model_best = LinearRegression()\n",
|
||
|
|
"model_best.fit(X_poly_best, y)\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Using polynomial degree: {best_degree}\")\n",
|
||
|
|
"\n",
|
||
|
|
"# Select a specific operating condition to visualize\n",
|
||
|
|
"# Using the same condition as earlier: -15A, -15A, 0° roll\n",
|
||
|
|
"test_condition = magDf.iloc[0:13].copy()\n",
|
||
|
|
"currL_val = test_condition['currL [A]'].iloc[0]\n",
|
||
|
|
"currR_val = test_condition['currR [A]'].iloc[0]\n",
|
||
|
|
"roll_val = test_condition['rollDeg [deg]'].iloc[0]\n",
|
||
|
|
"\n",
|
||
|
|
"# Create very fine grid for gap heights to see polynomial behavior between points\n",
|
||
|
|
"gap_very_fine = np.linspace(5, 30, 500) # 500 points for smooth curve\n",
|
||
|
|
"X_fine = pd.DataFrame({\n",
|
||
|
|
" 'currL [A]': [currL_val] * 500,\n",
|
||
|
|
" 'currR [A]': [currR_val] * 500,\n",
|
||
|
|
" 'rollDeg [deg]': [roll_val] * 500,\n",
|
||
|
|
" 'invGap': 1/gap_very_fine # Use inverse gap height for modeling\n",
|
||
|
|
"})\n",
|
||
|
|
"X_fine_poly = poly_best.transform(X_fine)\n",
|
||
|
|
"y_fine_pred = model_best.predict(X_fine_poly)\n",
|
||
|
|
"\n",
|
||
|
|
"# Extract actual Ansys data for this condition\n",
|
||
|
|
"gap_actual = test_condition['GapHeight [mm]'].values\n",
|
||
|
|
"force_actual = test_condition['YokeForce.Force_z [newton]'].values\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot\n",
|
||
|
|
"fig, ax = plt.subplots(1, 1, figsize=(12, 7))\n",
|
||
|
|
"\n",
|
||
|
|
"ax.plot(gap_very_fine, y_fine_pred[:, 0], '-', linewidth=2.5, \n",
|
||
|
|
" label=f'Degree {best_degree} Polynomial Function', color='#2ca02c', alpha=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot actual Ansys data points\n",
|
||
|
|
"ax.scatter(gap_actual, force_actual, s=120, marker='o', \n",
|
||
|
|
" color='#d62728', edgecolors='black', linewidths=1.5,\n",
|
||
|
|
" label='Ansys Simulation Data', zorder=5)\n",
|
||
|
|
"\n",
|
||
|
|
"ax.set_ylabel('Force (N)', fontsize=13)\n",
|
||
|
|
"ax.set_title(f'Degree {best_degree} Polynomial vs Ansys Data (currL={currL_val}A, currR={currR_val}A, roll={roll_val}°)', \n",
|
||
|
|
" fontsize=14, fontweight='bold')\n",
|
||
|
|
"ax.legend(fontsize=12, loc='best')\n",
|
||
|
|
"ax.grid(True, alpha=0.3)\n",
|
||
|
|
"\n",
|
||
|
|
"# Add vertical lines at data points to highlight gaps\n",
|
||
|
|
"for gap_point in gap_actual:\n",
|
||
|
|
" ax.axvline(gap_point, color='gray', linestyle=':', alpha=0.3, linewidth=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"# Check for excessive oscillations by looking at derivative\n",
|
||
|
|
"print(\"Checking for overfitting indicators:\")\n",
|
||
|
|
"print(f\"Number of actual data points in this condition: {len(gap_actual)}\")\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Gap spacing: {np.diff(gap_actual).mean():.2f} mm average\")\n",
|
||
|
|
"print(f\"Polynomial curve resolution: 500 points over {gap_very_fine.max() - gap_very_fine.min():.1f} mm range\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "8e4487e0",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Compare Multiple Conditions Side-by-Side\n",
|
||
|
|
"\n",
|
||
|
|
"Let's check several different operating conditions to ensure consistent behavior."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 263,
|
||
|
|
"id": "74d6e98f",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABjUAAAS1CAYAAADjrFn4AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs/X1wW9l5J/h/L0iKeqFIEBT10t1qt8Bud2zHjg1SbsfxxC8i7XGcTe+4QfVuTXbjikfkuuza3XI2ZDSp2q3e3+wqZCZVuzX2xKR6Zu2ajackMjPOzqQzMdFx3uzYLRFuO7Zju5tQv+qFEkmQFCVRJHF/f8C4AkgCwn0u8ODg8Pup6mqBwME557kPL87hwbnXcV3XBRERERERERERERERkeFCtW4AERERERERERERERFRObioQUREREREREREREREdYGLGkREREREREREREREVBe4qEFERERERERERERERHWBixpERERERERERERERFQXuKhBRERERERERERERER1gYsaRERERERERERERERUF7ioQUREREREREREREREdYGLGkRERGS9dDoNx3G2/a+7uxvj4+O1bmJdKBbH9vZ29PX1IZlMit97fHwcjuMglUpVsMX6xsfH0d3dLYpFpWOQOz7Dw8P3fW17ezscx0F/f7+oLr9tNylOm6XTaQwODnox6erqwuDgINLpdFXq0xYk9tWSa1MuZ7u6utDf349EIqHelu7ubnR3d29pW7nxMjG+RERERLbhogYRERHtGL29vZiZmfH+m5iYQE9PDwYHB9HX11fr5qnI/cG2q6vL++Oh3z8c5sdxenoaZ8+eBZD9Y+Do6Gg1ml03ZmZmkEwmMT8/X+umeCYnJ0s+n0gk1P9gb2KcACCZTOLYsWNIJBIYGBjAxMQE4vE4zp8/b80fqk2KfTqdRl9fHwYHB9HT04OJiQmMjY0hHo8jkUhgYmKi1k30HS+T4ktERERkq8ZaN4CIiIhISzQaRTQaLXgcj8fR39+Pvr4+dHd3Y3p6uoYtrK5UKoW+vj6Ew2GMjIwgGo0ilUohEon4ep/NcYzFYojH4xgcHMTw8DB6e3sRi8Uq3fy6MDIygpGRkVo3wxOLxZBMJpFMJosek4mJCe91lTY5OYloNLqlbtPiBGQXNLq7u9Hb24uJiQmEw2EAQDwex+nTp3HixAl0d3djZmam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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1600x1200 with 4 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Visual inspection guide:\n",
|
||
|
|
"✓ Look for smooth curves between data points (good)\n",
|
||
|
|
"✗ Look for wild oscillations or wiggles between points (overfitting)\n",
|
||
|
|
"✓ Polynomial should interpolate naturally without excessive curvature\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Select 4 different conditions to visualize\n",
|
||
|
|
"# Each condition has 13 gap height measurements (one complete parameter set)\n",
|
||
|
|
"condition_indices = [\n",
|
||
|
|
" (0, 13, '-15A, -15A, 0°'), # First condition\n",
|
||
|
|
" (13, 26, '-15A, -15A, 0.5°'), # Same currents, different roll\n",
|
||
|
|
" (91, 104, '-15A, -10A, 0°'), # Different right current\n",
|
||
|
|
" (182, 195, '-15A, -10A, 1°') # Different right current and roll\n",
|
||
|
|
"]\n",
|
||
|
|
"\n",
|
||
|
|
"fig, axes = plt.subplots(2, 2, figsize=(16, 12))\n",
|
||
|
|
"axes = axes.flatten()\n",
|
||
|
|
"\n",
|
||
|
|
"for idx, (start, end, label) in enumerate(condition_indices):\n",
|
||
|
|
" ax = axes[idx]\n",
|
||
|
|
" \n",
|
||
|
|
" # Get condition data\n",
|
||
|
|
" condition_data = magDf.iloc[start:end]\n",
|
||
|
|
" currL = condition_data['currL [A]'].iloc[0]\n",
|
||
|
|
" currR = condition_data['currR [A]'].iloc[0]\n",
|
||
|
|
" roll = condition_data['rollDeg [deg]'].iloc[0]\n",
|
||
|
|
" \n",
|
||
|
|
" # Create fine grid\n",
|
||
|
|
" gap_fine = np.linspace(5, 30, 500)\n",
|
||
|
|
" X_condition_fine = pd.DataFrame({\n",
|
||
|
|
" 'currL [A]': [currL] * 500,\n",
|
||
|
|
" 'currR [A]': [currR] * 500,\n",
|
||
|
|
" 'rollDeg [deg]': [roll] * 500,\n",
|
||
|
|
" 'invGap': 1/gap_fine # Use inverse gap height for modeling\n",
|
||
|
|
" })\n",
|
||
|
|
" \n",
|
||
|
|
" # Predict with best degree polynomial\n",
|
||
|
|
" X_condition_poly = poly_best.transform(X_condition_fine)\n",
|
||
|
|
" y_condition_pred = model_best.predict(X_condition_poly)\n",
|
||
|
|
" \n",
|
||
|
|
" # Plot polynomial curve\n",
|
||
|
|
" ax.plot(gap_fine, y_condition_pred[:, 0], '-', linewidth=2.5, \n",
|
||
|
|
" color='#2ca02c', alpha=0.8, label=f'Degree {best_degree} Polynomial')\n",
|
||
|
|
" \n",
|
||
|
|
" # Plot actual data\n",
|
||
|
|
" ax.scatter(condition_data['GapHeight [mm]'], \n",
|
||
|
|
" condition_data['YokeForce.Force_z [newton]'],\n",
|
||
|
|
" s=100, marker='o', color='#d62728', \n",
|
||
|
|
" edgecolors='black', linewidths=1.5,\n",
|
||
|
|
" label='Ansys Data', zorder=5)\n",
|
||
|
|
" \n",
|
||
|
|
" # Formatting\n",
|
||
|
|
" ax.set_xlabel('Gap Height (mm)', fontsize=11)\n",
|
||
|
|
" ax.set_ylabel('Force (N)', fontsize=11)\n",
|
||
|
|
" ax.set_title(f'currL={currL:.0f}A, currR={currR:.0f}A, roll={roll:.0f}°', \n",
|
||
|
|
" fontsize=12, fontweight='bold')\n",
|
||
|
|
" ax.legend(fontsize=10, loc='best')\n",
|
||
|
|
" ax.grid(True, alpha=0.3)\n",
|
||
|
|
" \n",
|
||
|
|
" # Add vertical lines at data points\n",
|
||
|
|
" for gap_point in condition_data['GapHeight [mm]'].values:\n",
|
||
|
|
" ax.axvline(gap_point, color='gray', linestyle=':', alpha=0.2, linewidth=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.suptitle(f'Degree {best_degree} Polynomial: Multiple Operating Conditions', \n",
|
||
|
|
" fontsize=15, fontweight='bold', y=1.00)\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"print(\"Visual inspection guide:\")\n",
|
||
|
|
"print(\"✓ Look for smooth curves between data points (good)\")\n",
|
||
|
|
"print(\"✗ Look for wild oscillations or wiggles between points (overfitting)\")\n",
|
||
|
|
"print(\"✓ Polynomial should interpolate naturally without excessive curvature\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "81a46ab5",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Find Worst-Performing Segment\n",
|
||
|
|
"\n",
|
||
|
|
"Let's identify which parameter set has the worst R² score to showcase model performance even in challenging cases."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 264,
|
||
|
|
"id": "35f4438e",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Total segments analyzed: 637\n",
|
||
|
|
"\n",
|
||
|
|
"Worst performing segment:\n",
|
||
|
|
" Rows: 1261 to 1274\n",
|
||
|
|
" Parameters: currL=15A, currR=15A, roll=-3.0°\n",
|
||
|
|
" R² Score: 0.999302\n",
|
||
|
|
"\n",
|
||
|
|
"Best performing segment R²: 0.999992\n",
|
||
|
|
"Mean R² across all segments: 0.999934\n",
|
||
|
|
"Median R² across all segments: 0.999953\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Calculate R² score for each 13-row segment (each parameter set)\n",
|
||
|
|
"num_segments = len(magDf) // 13\n",
|
||
|
|
"segment_scores = []\n",
|
||
|
|
"segment_info = []\n",
|
||
|
|
"\n",
|
||
|
|
"for i in range(num_segments):\n",
|
||
|
|
" start_idx = i * 13\n",
|
||
|
|
" end_idx = start_idx + 13\n",
|
||
|
|
" \n",
|
||
|
|
" # Get actual and predicted values for this segment\n",
|
||
|
|
" segment_actual = y.iloc[start_idx:end_idx, 0].values # Force only\n",
|
||
|
|
" segment_pred = model_best.predict(poly_best.transform(X.iloc[start_idx:end_idx]))[:, 0]\n",
|
||
|
|
" \n",
|
||
|
|
" # Calculate R² for this segment\n",
|
||
|
|
" segment_r2 = r2_score(segment_actual, segment_pred)\n",
|
||
|
|
" segment_scores.append(segment_r2)\n",
|
||
|
|
" \n",
|
||
|
|
" # Store segment info\n",
|
||
|
|
" segment_data = magDf.iloc[start_idx:end_idx]\n",
|
||
|
|
" currL = segment_data['currL [A]'].iloc[0]\n",
|
||
|
|
" currR = segment_data['currR [A]'].iloc[0]\n",
|
||
|
|
" roll = segment_data['rollDeg [deg]'].iloc[0]\n",
|
||
|
|
" segment_info.append({\n",
|
||
|
|
" 'start': start_idx,\n",
|
||
|
|
" 'end': end_idx,\n",
|
||
|
|
" 'currL': currL,\n",
|
||
|
|
" 'currR': currR,\n",
|
||
|
|
" 'roll': roll,\n",
|
||
|
|
" 'r2': segment_r2\n",
|
||
|
|
" })\n",
|
||
|
|
"\n",
|
||
|
|
"# Find worst performing segment\n",
|
||
|
|
"worst_idx = np.argmin(segment_scores)\n",
|
||
|
|
"worst_segment = segment_info[worst_idx]\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Total segments analyzed: {num_segments}\")\n",
|
||
|
|
"print(f\"\\nWorst performing segment:\")\n",
|
||
|
|
"print(f\" Rows: {worst_segment['start']} to {worst_segment['end']}\")\n",
|
||
|
|
"print(f\" Parameters: currL={worst_segment['currL']:.0f}A, currR={worst_segment['currR']:.0f}A, roll={worst_segment['roll']:.1f}°\")\n",
|
||
|
|
"print(f\" R² Score: {worst_segment['r2']:.6f}\")\n",
|
||
|
|
"print(f\"\\nBest performing segment R²: {max(segment_scores):.6f}\")\n",
|
||
|
|
"print(f\"Mean R² across all segments: {np.mean(segment_scores):.6f}\")\n",
|
||
|
|
"print(f\"Median R² across all segments: {np.median(segment_scores):.6f}\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "080f954b",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Visualize Worst-Performing Segment\n",
|
||
|
|
"\n",
|
||
|
|
"Even our worst case shows excellent model performance!"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 265,
|
||
|
|
"id": "6ceaf2b2",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1200x700 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Even in the worst case, the model achieves R² = 0.999302\n",
|
||
|
|
"This demonstrates excellent interpolation performance across all operating conditions!\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Get worst segment data\n",
|
||
|
|
"worst_data = magDf.iloc[worst_segment['start']:worst_segment['end']]\n",
|
||
|
|
"worst_currL = worst_segment['currL']\n",
|
||
|
|
"worst_currR = worst_segment['currR']\n",
|
||
|
|
"worst_roll = worst_segment['roll']\n",
|
||
|
|
"worst_r2 = worst_segment['r2']\n",
|
||
|
|
"\n",
|
||
|
|
"# Create fine grid for this worst segment\n",
|
||
|
|
"gap_fine_worst = np.linspace(5, 30, 500)\n",
|
||
|
|
"X_worst_fine = pd.DataFrame({\n",
|
||
|
|
" 'currL [A]': [worst_currL] * 500,\n",
|
||
|
|
" 'currR [A]': [worst_currR] * 500,\n",
|
||
|
|
" 'rollDeg [deg]': [worst_roll] * 500,\n",
|
||
|
|
" 'invGap': 1/gap_fine_worst\n",
|
||
|
|
"})\n",
|
||
|
|
"\n",
|
||
|
|
"# Get predictions\n",
|
||
|
|
"X_worst_poly = poly_best.transform(X_worst_fine)\n",
|
||
|
|
"y_worst_pred = model_best.predict(X_worst_poly)\n",
|
||
|
|
"\n",
|
||
|
|
"# Extract actual data\n",
|
||
|
|
"gap_worst_actual = worst_data['GapHeight [mm]'].values\n",
|
||
|
|
"force_worst_actual = worst_data['YokeForce.Force_z [newton]'].values\n",
|
||
|
|
"\n",
|
||
|
|
"# Create the plot\n",
|
||
|
|
"fig, ax = plt.subplots(1, 1, figsize=(12, 7))\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot polynomial curve\n",
|
||
|
|
"ax.plot(gap_fine_worst, y_worst_pred[:, 0], '-', linewidth=2.5, \n",
|
||
|
|
" label=f'Degree {best_degree} Polynomial', color='#2ca02c', alpha=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot actual data points\n",
|
||
|
|
"ax.scatter(gap_worst_actual, force_worst_actual, s=120, marker='o', \n",
|
||
|
|
" color='#d62728', edgecolors='black', linewidths=1.5,\n",
|
||
|
|
" label='Ansys Data', zorder=5)\n",
|
||
|
|
"\n",
|
||
|
|
"# Formatting\n",
|
||
|
|
"ax.set_xlabel('Gap Height (mm)', fontsize=13)\n",
|
||
|
|
"ax.set_ylabel('Force (N)', fontsize=13)\n",
|
||
|
|
"ax.set_title(f'Worst Case Performance (R² = {worst_r2:.6f}) - currL={worst_currL:.0f}A, currR={worst_currR:.0f}A, roll={worst_roll:.1f}°', \n",
|
||
|
|
" fontsize=14, fontweight='bold')\n",
|
||
|
|
"ax.legend(fontsize=12, loc='best')\n",
|
||
|
|
"ax.grid(True, alpha=0.3)\n",
|
||
|
|
"\n",
|
||
|
|
"# Add vertical lines at data points\n",
|
||
|
|
"for gap_point in gap_worst_actual:\n",
|
||
|
|
" ax.axvline(gap_point, color='gray', linestyle=':', alpha=0.3, linewidth=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Even in the worst case, the model achieves R² = {worst_r2:.6f}\")\n",
|
||
|
|
"print(f\"This demonstrates excellent interpolation performance across all operating conditions!\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "a8f24086",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"## Torque Prediction Analysis\n",
|
||
|
|
"\n",
|
||
|
|
"Now let's examine torque predictions. Torque is influenced by multiple variables:\n",
|
||
|
|
"- **Roll angle** (primary driver of torque asymmetry)\n",
|
||
|
|
"- **Current imbalance** (currL vs currR difference)\n",
|
||
|
|
"- **Gap height** (via invGap feature)\n",
|
||
|
|
"\n",
|
||
|
|
"We'll visualize torque accuracy by sweeping across roll angles first, then examining current variations."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "d896f5a7",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Overall Torque Prediction Accuracy\n",
|
||
|
|
"\n",
|
||
|
|
"First, let's check the overall R² score for torque predictions across all data."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 266,
|
||
|
|
"id": "c120048d",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1000x800 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Overall Torque R² Score: 0.999895\n",
|
||
|
|
"Overall Force R² Score: 0.999967\n",
|
||
|
|
"\n",
|
||
|
|
"Torque prediction is excellent!\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Calculate overall torque prediction accuracy\n",
|
||
|
|
"y_pred_full = model_best.predict(X_poly_best)\n",
|
||
|
|
"torque_actual_full = y.iloc[:, 1].values\n",
|
||
|
|
"torque_pred_full = y_pred_full[:, 1]\n",
|
||
|
|
"\n",
|
||
|
|
"torque_r2_overall = r2_score(torque_actual_full, torque_pred_full)\n",
|
||
|
|
"\n",
|
||
|
|
"# Create scatter plot for torque\n",
|
||
|
|
"fig, ax = plt.subplots(1, 1, figsize=(10, 8))\n",
|
||
|
|
"\n",
|
||
|
|
"ax.scatter(torque_actual_full, torque_pred_full, alpha=0.4, s=15, color='#1f77b4')\n",
|
||
|
|
"ax.plot([torque_actual_full.min(), torque_actual_full.max()], \n",
|
||
|
|
" [torque_actual_full.min(), torque_actual_full.max()], \n",
|
||
|
|
" 'r--', linewidth=2, label='Perfect Fit')\n",
|
||
|
|
"\n",
|
||
|
|
"ax.set_xlabel('Actual Torque (mN·m)', fontsize=13)\n",
|
||
|
|
"ax.set_ylabel('Predicted Torque (mN·m)', fontsize=13)\n",
|
||
|
|
"ax.set_title(f'Torque: Predicted vs Actual (R² = {torque_r2_overall:.6f})', \n",
|
||
|
|
" fontsize=14, fontweight='bold')\n",
|
||
|
|
"ax.legend(fontsize=12)\n",
|
||
|
|
"ax.grid(True, alpha=0.3)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Overall Torque R² Score: {torque_r2_overall:.6f}\")\n",
|
||
|
|
"print(f\"Overall Force R² Score: {r2_score(y.iloc[:, 0].values, y_pred_full[:, 0]):.6f}\")\n",
|
||
|
|
"print(f\"\\nTorque prediction is {'excellent' if torque_r2_overall > 0.99 else 'good' if torque_r2_overall > 0.95 else 'moderate'}!\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "1dd6a624",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Torque vs Roll Angle (Fixed Currents, Multiple Gap Heights)\n",
|
||
|
|
"\n",
|
||
|
|
"Examine how torque varies with roll angle for different gap heights. Roll angle is the primary driver of torque asymmetry."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 267,
|
||
|
|
"id": "70b9b4e1",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1600x1200 with 4 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Showing torque variation with roll angle at 4 different gap heights.\n",
|
||
|
|
"Gap height affects the magnitude of torque but the relationship with roll angle remains consistent.\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Select 4 different gap heights to visualize\n",
|
||
|
|
"# Using currL = -15A, currR = -15A configuration\n",
|
||
|
|
"gap_heights_to_plot = [8, 10, 15, 21] # mm\n",
|
||
|
|
"\n",
|
||
|
|
"fig, axes = plt.subplots(2, 2, figsize=(16, 12))\n",
|
||
|
|
"axes = axes.flatten()\n",
|
||
|
|
"\n",
|
||
|
|
"for idx, gap_height in enumerate(gap_heights_to_plot):\n",
|
||
|
|
" ax = axes[idx]\n",
|
||
|
|
" \n",
|
||
|
|
" # Fixed current configuration\n",
|
||
|
|
" currL = -15\n",
|
||
|
|
" currR = -15\n",
|
||
|
|
" \n",
|
||
|
|
" # Get actual data for this gap height\n",
|
||
|
|
" gap_data = magDf[(magDf['GapHeight [mm]'] == gap_height) & \n",
|
||
|
|
" (magDf['currL [A]'] == currL) & \n",
|
||
|
|
" (magDf['currR [A]'] == currR)]\n",
|
||
|
|
" \n",
|
||
|
|
" # Create fine grid for smooth curve across roll angles\n",
|
||
|
|
" roll_fine = np.linspace(-5, 5.0, 500)\n",
|
||
|
|
" X_condition_fine = pd.DataFrame({\n",
|
||
|
|
" 'currL [A]': [currL] * 500,\n",
|
||
|
|
" 'currR [A]': [currR] * 500,\n",
|
||
|
|
" 'rollDeg [deg]': roll_fine,\n",
|
||
|
|
" 'invGap': [1/gap_height] * 500\n",
|
||
|
|
" })\n",
|
||
|
|
" \n",
|
||
|
|
" # Predict with best degree polynomial\n",
|
||
|
|
" X_condition_poly = poly_best.transform(X_condition_fine)\n",
|
||
|
|
" y_condition_pred = model_best.predict(X_condition_poly)\n",
|
||
|
|
" \n",
|
||
|
|
" # Plot polynomial curve for TORQUE (index 1)\n",
|
||
|
|
" ax.plot(roll_fine, y_condition_pred[:, 1], '-', linewidth=2.5, \n",
|
||
|
|
" color='#ff7f0e', alpha=0.8, label=f'Degree {best_degree} Polynomial')\n",
|
||
|
|
" \n",
|
||
|
|
" # Plot actual torque data\n",
|
||
|
|
" ax.scatter(gap_data['rollDeg [deg]'], \n",
|
||
|
|
" gap_data['YokeTorque.Torque [mNewtonMeter]'],\n",
|
||
|
|
" s=100, marker='o', color='#9467bd', \n",
|
||
|
|
" edgecolors='black', linewidths=1.5,\n",
|
||
|
|
" label='Ansys Data', zorder=5)\n",
|
||
|
|
" \n",
|
||
|
|
" # Formatting\n",
|
||
|
|
" ax.set_xlabel('Roll Angle (deg)', fontsize=11)\n",
|
||
|
|
" ax.set_ylabel('Torque (mN·m)', fontsize=11)\n",
|
||
|
|
" ax.set_title(f'Gap Height = {gap_height} mm (currL={currL:.0f}A, currR={currR:.0f}A)', \n",
|
||
|
|
" fontsize=12, fontweight='bold')\n",
|
||
|
|
" ax.legend(fontsize=10, loc='best')\n",
|
||
|
|
" ax.grid(True, alpha=0.3)\n",
|
||
|
|
" \n",
|
||
|
|
" # Add vertical lines at actual roll angle data points\n",
|
||
|
|
" for roll_point in gap_data['rollDeg [deg]'].unique():\n",
|
||
|
|
" ax.axvline(roll_point, color='gray', linestyle=':', alpha=0.2, linewidth=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.suptitle(f'Torque vs Roll Angle at Different Gap Heights (Degree {best_degree} Polynomial)', \n",
|
||
|
|
" fontsize=15, fontweight='bold', y=1.00)\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Showing torque variation with roll angle at {len(gap_heights_to_plot)} different gap heights.\")\n",
|
||
|
|
"print(\"Gap height affects the magnitude of torque but the relationship with roll angle remains consistent.\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "02d02793",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Torque vs Current Imbalance (Fixed Roll Angle, Multiple Gap Heights)\n",
|
||
|
|
"\n",
|
||
|
|
"Now examine how torque varies with current imbalance at different gap heights for a fixed roll angle."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 268,
|
||
|
|
"id": "3b8d7f6a",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1600x1200 with 4 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Showing torque variation with current imbalance at 4 different gap heights.\n",
|
||
|
|
"All data uses currL = -15A with varying currR to create different imbalances.\n",
|
||
|
|
"Current imbalance (currR - currL) affects torque magnitude and direction.\n",
|
||
|
|
"Positive imbalance (currR > currL) and negative imbalance (currR < currL) produce opposite torques.\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Select 4 different gap heights to visualize\n",
|
||
|
|
"# Using roll = 0.5 degrees (small but non-zero torque)\n",
|
||
|
|
"gap_heights_to_plot = [8, 10, 15, 21] # mm\n",
|
||
|
|
"roll_target = 0.5\n",
|
||
|
|
"\n",
|
||
|
|
"# Use a fixed reference current for currL\n",
|
||
|
|
"currL_ref = -15\n",
|
||
|
|
"\n",
|
||
|
|
"fig, axes = plt.subplots(2, 2, figsize=(16, 12))\n",
|
||
|
|
"axes = axes.flatten()\n",
|
||
|
|
"\n",
|
||
|
|
"for idx, gap_height in enumerate(gap_heights_to_plot):\n",
|
||
|
|
" ax = axes[idx]\n",
|
||
|
|
" \n",
|
||
|
|
" # Get actual data for this gap height, roll angle, AND fixed currL\n",
|
||
|
|
" gap_roll_data = magDf[(magDf['GapHeight [mm]'] == gap_height) & \n",
|
||
|
|
" (magDf['rollDeg [deg]'] == roll_target) &\n",
|
||
|
|
" (magDf['currL [A]'] == currL_ref)]\n",
|
||
|
|
" \n",
|
||
|
|
" # Calculate imbalance for actual data points (all have same currL now)\n",
|
||
|
|
" actual_imbalances = []\n",
|
||
|
|
" actual_torques = []\n",
|
||
|
|
" for _, row in gap_roll_data.iterrows():\n",
|
||
|
|
" imbalance = row['currR [A]'] - row['currL [A]']\n",
|
||
|
|
" torque = row['YokeTorque.Torque [mNewtonMeter]']\n",
|
||
|
|
" actual_imbalances.append(imbalance)\n",
|
||
|
|
" actual_torques.append(torque)\n",
|
||
|
|
" \n",
|
||
|
|
" # Create fine grid for smooth curve across current imbalances\n",
|
||
|
|
" # Range from -10A to +10A imbalance (currR - currL)\n",
|
||
|
|
" imbalance_fine = np.linspace(0, 35, 500)\n",
|
||
|
|
" \n",
|
||
|
|
" # Vary currR while keeping currL fixed\n",
|
||
|
|
" currR_fine = currL_ref + imbalance_fine\n",
|
||
|
|
" \n",
|
||
|
|
" X_condition_fine = pd.DataFrame({\n",
|
||
|
|
" 'currL [A]': [currL_ref] * 500,\n",
|
||
|
|
" 'currR [A]': currR_fine,\n",
|
||
|
|
" 'rollDeg [deg]': [roll_target] * 500,\n",
|
||
|
|
" 'invGap': [1/gap_height] * 500\n",
|
||
|
|
" })\n",
|
||
|
|
" \n",
|
||
|
|
" # Predict with best degree polynomial\n",
|
||
|
|
" X_condition_poly = poly_best.transform(X_condition_fine)\n",
|
||
|
|
" y_condition_pred = model_best.predict(X_condition_poly)\n",
|
||
|
|
" \n",
|
||
|
|
" # Plot polynomial curve for TORQUE (index 1)\n",
|
||
|
|
" ax.plot(imbalance_fine, y_condition_pred[:, 1], '-', linewidth=2.5, \n",
|
||
|
|
" color='#ff7f0e', alpha=0.8, label=f'Degree {best_degree} Polynomial')\n",
|
||
|
|
" \n",
|
||
|
|
" # Plot actual torque data (now only one point per imbalance value)\n",
|
||
|
|
" ax.scatter(actual_imbalances, actual_torques,\n",
|
||
|
|
" s=100, marker='o', color='#9467bd', \n",
|
||
|
|
" edgecolors='black', linewidths=1.5,\n",
|
||
|
|
" label='Ansys Data', zorder=5)\n",
|
||
|
|
" \n",
|
||
|
|
" # Formatting\n",
|
||
|
|
" ax.set_xlabel('Current Imbalance (currR - currL) [A]', fontsize=11)\n",
|
||
|
|
" ax.set_ylabel('Torque (mN·m)', fontsize=11)\n",
|
||
|
|
" ax.set_title(f'Gap Height = {gap_height} mm (currL = {currL_ref}A, roll = {roll_target}°)', \n",
|
||
|
|
" fontsize=12, fontweight='bold')\n",
|
||
|
|
" ax.legend(fontsize=10, loc='best')\n",
|
||
|
|
" ax.grid(True, alpha=0.3)\n",
|
||
|
|
" ax.axhline(0, color='black', linestyle='-', linewidth=0.5, alpha=0.3)\n",
|
||
|
|
" ax.axvline(0, color='black', linestyle='-', linewidth=0.5, alpha=0.3)\n",
|
||
|
|
" \n",
|
||
|
|
" # Add vertical lines at actual imbalance data points\n",
|
||
|
|
" for imb in set(actual_imbalances):\n",
|
||
|
|
" ax.axvline(imb, color='gray', linestyle=':', alpha=0.2, linewidth=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.suptitle(f'Torque vs Current Imbalance at Different Gap Heights (currL = {currL_ref}A, Roll = {roll_target}°)', \n",
|
||
|
|
" fontsize=15, fontweight='bold', y=1.00)\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Showing torque variation with current imbalance at {len(gap_heights_to_plot)} different gap heights.\")\n",
|
||
|
|
"print(f\"All data uses currL = {currL_ref}A with varying currR to create different imbalances.\")\n",
|
||
|
|
"print(f\"Current imbalance (currR - currL) affects torque magnitude and direction.\")\n",
|
||
|
|
"print(f\"Positive imbalance (currR > currL) and negative imbalance (currR < currL) produce opposite torques.\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "d71ac3cc",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Torque Segment-Wise Performance\n",
|
||
|
|
"\n",
|
||
|
|
"Calculate R² scores for torque predictions across all segments, similar to what we did for force."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 269,
|
||
|
|
"id": "902343b2",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Torque Prediction Analysis (across 637 segments):\n",
|
||
|
|
"\n",
|
||
|
|
"Best R² Score: 0.999964\n",
|
||
|
|
" Parameters: currL=15A, currR=-15A, roll=-1.5°\n",
|
||
|
|
"\n",
|
||
|
|
"Worst R² Score: -0.638049\n",
|
||
|
|
" Parameters: currL=-10A, currR=-10A, roll=0.0°\n",
|
||
|
|
"\n",
|
||
|
|
"Mean R²: 0.981564\n",
|
||
|
|
"Median R²: 0.999392\n",
|
||
|
|
"Std Dev: 0.124378\n",
|
||
|
|
"Std Dev: 0.124378\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1000x600 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Calculate R² score for torque in each 13-row segment\n",
|
||
|
|
"torque_segment_scores = []\n",
|
||
|
|
"torque_segment_info = []\n",
|
||
|
|
"\n",
|
||
|
|
"for i in range(num_segments):\n",
|
||
|
|
" start_idx = i * 13\n",
|
||
|
|
" end_idx = start_idx + 13\n",
|
||
|
|
" \n",
|
||
|
|
" # Get actual and predicted torque values for this segment\n",
|
||
|
|
" segment_actual_torque = y.iloc[start_idx:end_idx, 1].values # Torque column\n",
|
||
|
|
" segment_pred_torque = model_best.predict(poly_best.transform(X.iloc[start_idx:end_idx]))[:, 1]\n",
|
||
|
|
" \n",
|
||
|
|
" # Calculate R² for this segment\n",
|
||
|
|
" segment_r2_torque = r2_score(segment_actual_torque, segment_pred_torque)\n",
|
||
|
|
" torque_segment_scores.append(segment_r2_torque)\n",
|
||
|
|
" \n",
|
||
|
|
" # Store segment info\n",
|
||
|
|
" segment_data = magDf.iloc[start_idx:end_idx]\n",
|
||
|
|
" currL = segment_data['currL [A]'].iloc[0]\n",
|
||
|
|
" currR = segment_data['currR [A]'].iloc[0]\n",
|
||
|
|
" roll = segment_data['rollDeg [deg]'].iloc[0]\n",
|
||
|
|
" torque_segment_info.append({\n",
|
||
|
|
" 'start': start_idx,\n",
|
||
|
|
" 'end': end_idx,\n",
|
||
|
|
" 'currL': currL,\n",
|
||
|
|
" 'currR': currR,\n",
|
||
|
|
" 'roll': roll,\n",
|
||
|
|
" 'r2': segment_r2_torque\n",
|
||
|
|
" })\n",
|
||
|
|
"\n",
|
||
|
|
"# Find worst and best performing segments for torque\n",
|
||
|
|
"worst_torque_idx = np.argmin(torque_segment_scores)\n",
|
||
|
|
"best_torque_idx = np.argmax(torque_segment_scores)\n",
|
||
|
|
"worst_torque_segment = torque_segment_info[worst_torque_idx]\n",
|
||
|
|
"best_torque_segment = torque_segment_info[best_torque_idx]\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Torque Prediction Analysis (across {num_segments} segments):\")\n",
|
||
|
|
"print(f\"\\nBest R² Score: {max(torque_segment_scores):.6f}\")\n",
|
||
|
|
"print(f\" Parameters: currL={best_torque_segment['currL']:.0f}A, currR={best_torque_segment['currR']:.0f}A, roll={best_torque_segment['roll']:.1f}°\")\n",
|
||
|
|
"print(f\"\\nWorst R² Score: {min(torque_segment_scores):.6f}\")\n",
|
||
|
|
"print(f\" Parameters: currL={worst_torque_segment['currL']:.0f}A, currR={worst_torque_segment['currR']:.0f}A, roll={worst_torque_segment['roll']:.1f}°\")\n",
|
||
|
|
"print(f\"\\nMean R²: {np.mean(torque_segment_scores):.6f}\")\n",
|
||
|
|
"print(f\"Median R²: {np.median(torque_segment_scores):.6f}\")\n",
|
||
|
|
"print(f\"Std Dev: {np.std(torque_segment_scores):.6f}\")\n",
|
||
|
|
"\n",
|
||
|
|
"# Create histogram of torque R² scores\n",
|
||
|
|
"fig, ax = plt.subplots(1, 1, figsize=(10, 6))\n",
|
||
|
|
"ax.hist(torque_segment_scores, bins=30, edgecolor='black', alpha=0.7, color='#9467bd')\n",
|
||
|
|
"ax.axvline(np.mean(torque_segment_scores), color='red', linestyle='--', linewidth=2, label=f'Mean = {np.mean(torque_segment_scores):.6f}')\n",
|
||
|
|
"ax.axvline(np.median(torque_segment_scores), color='orange', linestyle='--', linewidth=2, label=f'Median = {np.median(torque_segment_scores):.6f}')\n",
|
||
|
|
"ax.set_xlabel('R² Score', fontsize=12)\n",
|
||
|
|
"ax.set_ylabel('Number of Segments', fontsize=12)\n",
|
||
|
|
"ax.set_title('Distribution of Torque R² Scores Across All Parameter Segments', fontsize=14, fontweight='bold')\n",
|
||
|
|
"ax.legend(fontsize=11)\n",
|
||
|
|
"ax.grid(True, alpha=0.3, axis='y')\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "5bffc9a9",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Worst-Case Torque Prediction Visualization\n",
|
||
|
|
"\n",
|
||
|
|
"Let's visualize the worst-performing torque segment to assess model robustness."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 270,
|
||
|
|
"id": "9d5580f3",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1200x700 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Worst-case torque prediction achieves R² = -0.638049\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Get worst torque segment data\n",
|
||
|
|
"worst_torque_data = magDf.iloc[worst_torque_segment['start']:worst_torque_segment['end']]\n",
|
||
|
|
"worst_torque_currL = worst_torque_segment['currL']\n",
|
||
|
|
"worst_torque_currR = worst_torque_segment['currR']\n",
|
||
|
|
"worst_torque_roll = worst_torque_segment['roll']\n",
|
||
|
|
"worst_torque_r2 = worst_torque_segment['r2']\n",
|
||
|
|
"\n",
|
||
|
|
"# Create fine grid for this worst segment\n",
|
||
|
|
"gap_fine_worst_torque = np.linspace(2, 30, 500)\n",
|
||
|
|
"X_worst_torque_fine = pd.DataFrame({\n",
|
||
|
|
" 'currL [A]': [worst_torque_currL] * 500,\n",
|
||
|
|
" 'currR [A]': [worst_torque_currR] * 500,\n",
|
||
|
|
" 'rollDeg [deg]': [worst_torque_roll] * 500,\n",
|
||
|
|
" 'invGap': 1/gap_fine_worst_torque\n",
|
||
|
|
"})\n",
|
||
|
|
"\n",
|
||
|
|
"# Get predictions\n",
|
||
|
|
"X_worst_torque_poly = poly_best.transform(X_worst_torque_fine)\n",
|
||
|
|
"y_worst_torque_pred = model_best.predict(X_worst_torque_poly)\n",
|
||
|
|
"\n",
|
||
|
|
"# Extract actual data\n",
|
||
|
|
"gap_worst_torque_actual = worst_torque_data['GapHeight [mm]'].values\n",
|
||
|
|
"torque_worst_actual = worst_torque_data['YokeTorque.Torque [mNewtonMeter]'].values\n",
|
||
|
|
"\n",
|
||
|
|
"# Create the plot\n",
|
||
|
|
"fig, ax = plt.subplots(1, 1, figsize=(12, 7))\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot polynomial curve for TORQUE\n",
|
||
|
|
"ax.plot(gap_fine_worst_torque, y_worst_torque_pred[:, 1], '-', linewidth=2.5, \n",
|
||
|
|
" label=f'Degree {best_degree} Polynomial', color='#ff7f0e', alpha=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"# Plot actual data points\n",
|
||
|
|
"ax.scatter(gap_worst_torque_actual, torque_worst_actual, s=120, marker='o', \n",
|
||
|
|
" color='#9467bd', edgecolors='black', linewidths=1.5,\n",
|
||
|
|
" label='Ansys Data', zorder=5)\n",
|
||
|
|
"\n",
|
||
|
|
"# Formatting\n",
|
||
|
|
"ax.set_xlabel('Gap Height (mm)', fontsize=13)\n",
|
||
|
|
"ax.set_ylabel('Torque (mN·m)', fontsize=13)\n",
|
||
|
|
"ax.set_title(f'Worst Torque Case (R² = {worst_torque_r2:.6f}) - currL={worst_torque_currL:.0f}A, currR={worst_torque_currR:.0f}A, roll={worst_torque_roll:.1f}°', \n",
|
||
|
|
" fontsize=14, fontweight='bold')\n",
|
||
|
|
"ax.legend(fontsize=12, loc='best')\n",
|
||
|
|
"ax.grid(True, alpha=0.3)\n",
|
||
|
|
"\n",
|
||
|
|
"# Add vertical lines at data points\n",
|
||
|
|
"for gap_point in gap_worst_torque_actual:\n",
|
||
|
|
" ax.axvline(gap_point, color='gray', linestyle=':', alpha=0.3, linewidth=0.8)\n",
|
||
|
|
"\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"Worst-case torque prediction achieves R² = {worst_torque_r2:.6f}\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "c63aca1e",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### We can see here that at roll angle and current differential of 0 (when torque will be very close to 0):\n",
|
||
|
|
"\n",
|
||
|
|
"There is significant overfitting to noise, but within the operating range of 6 - 24 mm, there is never more variation than there would be with simple linear interpolation.\n",
|
||
|
|
"\n",
|
||
|
|
"Hopefully this means we are ok"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "1e1c384f",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Export Model Summary\n",
|
||
|
|
"\n",
|
||
|
|
"Display key model information before exporting."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 271,
|
||
|
|
"id": "778f58df",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"============================================================\n",
|
||
|
|
"MODEL EXPORT SUMMARY\n",
|
||
|
|
"============================================================\n",
|
||
|
|
"\n",
|
||
|
|
"Model Type: Polynomial Regression\n",
|
||
|
|
"Polynomial Degree: 6\n",
|
||
|
|
"Number of Features: 4\n",
|
||
|
|
"Number of Polynomial Terms: 210\n",
|
||
|
|
"\n",
|
||
|
|
"Input Features:\n",
|
||
|
|
" - currL [A]\n",
|
||
|
|
" - currR [A]\n",
|
||
|
|
" - rollDeg [deg]\n",
|
||
|
|
" - invGap (1/GapHeight)\n",
|
||
|
|
"\n",
|
||
|
|
"Outputs:\n",
|
||
|
|
" - YokeForce.Force_z [newton]\n",
|
||
|
|
" - YokeTorque.Torque [mNewtonMeter]\n",
|
||
|
|
"\n",
|
||
|
|
"Model Performance:\n",
|
||
|
|
" Force R² Score: 0.999967\n",
|
||
|
|
" Torque R² Score: 0.999895\n",
|
||
|
|
"\n",
|
||
|
|
"Model Coefficients Shape:\n",
|
||
|
|
" Force coefficients: (210,)\n",
|
||
|
|
" Torque coefficients: (210,)\n",
|
||
|
|
"============================================================\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"print(\"=\" * 60)\n",
|
||
|
|
"print(\"MODEL EXPORT SUMMARY\")\n",
|
||
|
|
"print(\"=\" * 60)\n",
|
||
|
|
"print(f\"\\nModel Type: Polynomial Regression\")\n",
|
||
|
|
"print(f\"Polynomial Degree: {best_degree}\")\n",
|
||
|
|
"print(f\"Number of Features: {poly_best.n_features_in_}\")\n",
|
||
|
|
"print(f\"Number of Polynomial Terms: {poly_best.n_output_features_}\")\n",
|
||
|
|
"print(f\"\\nInput Features:\")\n",
|
||
|
|
"print(\" - currL [A]\")\n",
|
||
|
|
"print(\" - currR [A]\")\n",
|
||
|
|
"print(\" - rollDeg [deg]\")\n",
|
||
|
|
"print(\" - invGap (1/GapHeight)\")\n",
|
||
|
|
"print(f\"\\nOutputs:\")\n",
|
||
|
|
"print(\" - YokeForce.Force_z [newton]\")\n",
|
||
|
|
"print(\" - YokeTorque.Torque [mNewtonMeter]\")\n",
|
||
|
|
"print(f\"\\nModel Performance:\")\n",
|
||
|
|
"print(f\" Force R² Score: {r2_score(y.iloc[:, 0].values, y_pred_full[:, 0]):.6f}\")\n",
|
||
|
|
"print(f\" Torque R² Score: {torque_r2_overall:.6f}\")\n",
|
||
|
|
"print(f\"\\nModel Coefficients Shape:\")\n",
|
||
|
|
"print(f\" Force coefficients: {model_best.coef_[0].shape}\")\n",
|
||
|
|
"print(f\" Torque coefficients: {model_best.coef_[1].shape}\")\n",
|
||
|
|
"print(\"=\" * 60)"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "88208b13",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Create Standalone Python Predictor Class\n",
|
||
|
|
"\n",
|
||
|
|
"Generate a self-contained Python module that can be imported into your simulator without scikit-learn dependencies."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 272,
|
||
|
|
"id": "edb239e1",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"✓ Saved standalone predictor: maglev_predictor.py\n",
|
||
|
|
"\n",
|
||
|
|
"This module:\n",
|
||
|
|
" - Has NO scikit-learn dependency (only numpy)\n",
|
||
|
|
" - Can be imported directly into your simulator\n",
|
||
|
|
" - Includes both single and batch prediction methods\n",
|
||
|
|
" - Implements polynomial feature generation internally\n",
|
||
|
|
"\n",
|
||
|
|
"To use:\n",
|
||
|
|
"```python\n",
|
||
|
|
"from maglev_predictor import MaglevPredictor\n",
|
||
|
|
"predictor = MaglevPredictor()\n",
|
||
|
|
"force, torque = predictor.predict(currL=-15, currR=-15, roll=1.0, gap_height=10.0)\n",
|
||
|
|
"```\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Generate standalone predictor class\n",
|
||
|
|
"predictor_code = f'''\"\"\"\n",
|
||
|
|
"Magnetic Levitation Force and Torque Predictor\n",
|
||
|
|
"Generated from polynomial regression model (degree {best_degree})\n",
|
||
|
|
"\n",
|
||
|
|
"Performance:\n",
|
||
|
|
" - Force R²: {r2_score(y.iloc[:, 0].values, y_pred_full[:, 0]):.6f}\n",
|
||
|
|
" - Torque R²: {torque_r2_overall:.6f}\n",
|
||
|
|
"\n",
|
||
|
|
"Usage:\n",
|
||
|
|
" predictor = MaglevPredictor()\n",
|
||
|
|
" force, torque = predictor.predict(currL=-15, currR=-15, roll=1.0, gap_height=10.0)\n",
|
||
|
|
"\"\"\"\n",
|
||
|
|
"\n",
|
||
|
|
"import numpy as np\n",
|
||
|
|
"from itertools import combinations_with_replacement\n",
|
||
|
|
"\n",
|
||
|
|
"class MaglevPredictor:\n",
|
||
|
|
" def __init__(self):\n",
|
||
|
|
" \"\"\"Initialize the magnetic levitation force/torque predictor.\"\"\"\n",
|
||
|
|
" self.degree = {best_degree}\n",
|
||
|
|
" self.n_features = 4 # currL, currR, roll, invGap\n",
|
||
|
|
" \n",
|
||
|
|
" # Force model coefficients\n",
|
||
|
|
" self.force_intercept = {model_best.intercept_[0]}\n",
|
||
|
|
" self.force_coef = np.array({model_best.coef_[0].tolist()})\n",
|
||
|
|
" \n",
|
||
|
|
" # Torque model coefficients \n",
|
||
|
|
" self.torque_intercept = {model_best.intercept_[1]}\n",
|
||
|
|
" self.torque_coef = np.array({model_best.coef_[1].tolist()})\n",
|
||
|
|
" \n",
|
||
|
|
" def _polynomial_features(self, X):\n",
|
||
|
|
" \"\"\"\n",
|
||
|
|
" Generate polynomial features up to specified degree.\n",
|
||
|
|
" Mimics sklearn's PolynomialFeatures with include_bias=True.\n",
|
||
|
|
" \n",
|
||
|
|
" Args:\n",
|
||
|
|
" X: numpy array of shape (n_samples, 4) with [currL, currR, roll, invGap]\n",
|
||
|
|
" \n",
|
||
|
|
" Returns:\n",
|
||
|
|
" Polynomial features array\n",
|
||
|
|
" \"\"\"\n",
|
||
|
|
" n_samples = X.shape[0]\n",
|
||
|
|
" \n",
|
||
|
|
" # Start with bias term (column of ones)\n",
|
||
|
|
" features = [np.ones(n_samples)]\n",
|
||
|
|
" \n",
|
||
|
|
" # Add original features\n",
|
||
|
|
" for i in range(self.n_features):\n",
|
||
|
|
" features.append(X[:, i])\n",
|
||
|
|
" \n",
|
||
|
|
" # Add polynomial combinations\n",
|
||
|
|
" for deg in range(2, self.degree + 1):\n",
|
||
|
|
" for combo in combinations_with_replacement(range(self.n_features), deg):\n",
|
||
|
|
" term = np.ones(n_samples)\n",
|
||
|
|
" for idx in combo:\n",
|
||
|
|
" term *= X[:, idx]\n",
|
||
|
|
" features.append(term)\n",
|
||
|
|
" \n",
|
||
|
|
" return np.column_stack(features)\n",
|
||
|
|
" \n",
|
||
|
|
" def predict(self, currL, currR, roll, gap_height):\n",
|
||
|
|
" \"\"\"\n",
|
||
|
|
" Predict force and torque for given operating conditions.\n",
|
||
|
|
" \n",
|
||
|
|
" Args:\n",
|
||
|
|
" currL: Left coil current in Amps\n",
|
||
|
|
" currR: Right coil current in Amps\n",
|
||
|
|
" roll: Roll angle in degrees\n",
|
||
|
|
" gap_height: Gap height in mm\n",
|
||
|
|
" \n",
|
||
|
|
" Returns:\n",
|
||
|
|
" tuple: (force [N], torque [mN·m])\n",
|
||
|
|
" \"\"\"\n",
|
||
|
|
" # Compute inverse gap (critical transformation!)\n",
|
||
|
|
" invGap = 1.0 / gap_height\n",
|
||
|
|
" \n",
|
||
|
|
" # Create input array\n",
|
||
|
|
" X = np.array([[currL, currR, roll, invGap]])\n",
|
||
|
|
" \n",
|
||
|
|
" # Generate polynomial features\n",
|
||
|
|
" X_poly = self._polynomial_features(X)\n",
|
||
|
|
" \n",
|
||
|
|
" # Compute predictions\n",
|
||
|
|
" force = self.force_intercept + np.dot(X_poly, self.force_coef)[0]\n",
|
||
|
|
" torque = self.torque_intercept + np.dot(X_poly, self.torque_coef)[0]\n",
|
||
|
|
" \n",
|
||
|
|
" return force, torque\n",
|
||
|
|
" \n",
|
||
|
|
" def predict_batch(self, currL_array, currR_array, roll_array, gap_height_array):\n",
|
||
|
|
" \"\"\"\n",
|
||
|
|
" Predict force and torque for multiple operating conditions.\n",
|
||
|
|
" \n",
|
||
|
|
" Args:\n",
|
||
|
|
" currL_array: Array of left coil currents [A]\n",
|
||
|
|
" currR_array: Array of right coil currents [A]\n",
|
||
|
|
" roll_array: Array of roll angles [deg]\n",
|
||
|
|
" gap_height_array: Array of gap heights [mm]\n",
|
||
|
|
" \n",
|
||
|
|
" Returns:\n",
|
||
|
|
" tuple: (force_array [N], torque_array [mN·m])\n",
|
||
|
|
" \"\"\"\n",
|
||
|
|
" # Convert to numpy arrays\n",
|
||
|
|
" currL_array = np.asarray(currL_array)\n",
|
||
|
|
" currR_array = np.asarray(currR_array)\n",
|
||
|
|
" roll_array = np.asarray(roll_array)\n",
|
||
|
|
" gap_height_array = np.asarray(gap_height_array)\n",
|
||
|
|
" \n",
|
||
|
|
" # Compute inverse gaps\n",
|
||
|
|
" invGap_array = 1.0 / gap_height_array\n",
|
||
|
|
" \n",
|
||
|
|
" # Stack into feature matrix\n",
|
||
|
|
" X = np.column_stack([currL_array, currR_array, roll_array, invGap_array])\n",
|
||
|
|
" \n",
|
||
|
|
" # Generate polynomial features\n",
|
||
|
|
" X_poly = self._polynomial_features(X)\n",
|
||
|
|
" \n",
|
||
|
|
" # Compute predictions\n",
|
||
|
|
" force_array = self.force_intercept + np.dot(X_poly, self.force_coef)\n",
|
||
|
|
" torque_array = self.torque_intercept + np.dot(X_poly, self.torque_coef)\n",
|
||
|
|
" \n",
|
||
|
|
" return force_array, torque_array\n",
|
||
|
|
"\n",
|
||
|
|
"\n",
|
||
|
|
"if __name__ == \"__main__\":\n",
|
||
|
|
" # Example usage\n",
|
||
|
|
" predictor = MaglevPredictor()\n",
|
||
|
|
" \n",
|
||
|
|
" # Single prediction\n",
|
||
|
|
" force, torque = predictor.predict(currL=-15, currR=-15, roll=1.0, gap_height=10.0)\n",
|
||
|
|
" print(f\"Single prediction:\")\n",
|
||
|
|
" print(f\" Force: {{force:.2f}} N\")\n",
|
||
|
|
" print(f\" Torque: {{torque:.2f}} mN·m\")\n",
|
||
|
|
" \n",
|
||
|
|
" # Batch prediction\n",
|
||
|
|
" currL = np.array([-15, -15, -10])\n",
|
||
|
|
" currR = np.array([-15, -10, -10])\n",
|
||
|
|
" roll = np.array([0, 0.5, 1.0])\n",
|
||
|
|
" gap = np.array([10, 12, 15])\n",
|
||
|
|
" \n",
|
||
|
|
" forces, torques = predictor.predict_batch(currL, currR, roll, gap)\n",
|
||
|
|
" print(f\"\\\\nBatch prediction:\")\n",
|
||
|
|
" for i in range(len(forces)):\n",
|
||
|
|
" print(f\" Condition {{i+1}}: Force={{forces[i]:.2f}} N, Torque={{torques[i]:.2f}} mN·m\")\n",
|
||
|
|
"'''\n",
|
||
|
|
"\n",
|
||
|
|
"# Save to file\n",
|
||
|
|
"predictor_filename = 'maglev_predictor.py'\n",
|
||
|
|
"with open(predictor_filename, 'w') as f:\n",
|
||
|
|
" f.write(predictor_code)\n",
|
||
|
|
"\n",
|
||
|
|
"print(f\"✓ Saved standalone predictor: {predictor_filename}\")\n",
|
||
|
|
"print(f\"\\nThis module:\")\n",
|
||
|
|
"print(f\" - Has NO scikit-learn dependency (only numpy)\")\n",
|
||
|
|
"print(f\" - Can be imported directly into your simulator\")\n",
|
||
|
|
"print(f\" - Includes both single and batch prediction methods\")\n",
|
||
|
|
"print(f\" - Implements polynomial feature generation internally\")\n",
|
||
|
|
"print(f\"\\nTo use:\")\n",
|
||
|
|
"print(f\"```python\")\n",
|
||
|
|
"print(f\"from maglev_predictor import MaglevPredictor\")\n",
|
||
|
|
"print(f\"predictor = MaglevPredictor()\")\n",
|
||
|
|
"print(f\"force, torque = predictor.predict(currL=-15, currR=-15, roll=1.0, gap_height=10.0)\")\n",
|
||
|
|
"print(f\"```\")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"id": "e4f40fb4",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"### Test the Exported Predictor\n",
|
||
|
|
"\n",
|
||
|
|
"Verify the standalone predictor produces identical results to the original model."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 273,
|
||
|
|
"id": "4a1ddacd",
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Validation: Comparing Standalone Predictor vs Original Model\n",
|
||
|
|
"================================================================================\n",
|
||
|
|
"\n",
|
||
|
|
"Test Case 1: currL=-15A, currR=-15A, roll=0.0°, gap=10.0mm\n",
|
||
|
|
" Force: Standalone= 103.3304 N | Original= 103.3304 N | Diff=0.00e+00\n",
|
||
|
|
" Torque: Standalone= -0.8561 mN·m | Original= -0.8561 mN·m | Diff=2.08e-13\n",
|
||
|
|
"\n",
|
||
|
|
"Test Case 2: currL=-15A, currR=-15A, roll=1.0°, gap=10.0mm\n",
|
||
|
|
" Force: Standalone= 104.0520 N | Original= 104.0520 N | Diff=1.14e-13\n",
|
||
|
|
" Torque: Standalone= 450.3916 mN·m | Original= 450.3916 mN·m | Diff=3.41e-13\n",
|
||
|
|
"\n",
|
||
|
|
"Test Case 3: currL=-15A, currR=-10A, roll=0.5°, gap=12.0mm\n",
|
||
|
|
" Force: Standalone= 75.0401 N | Original= 75.0401 N | Diff=5.68e-14\n",
|
||
|
|
" Torque: Standalone= 390.7023 mN·m | Original= 390.7023 mN·m | Diff=1.71e-13\n",
|
||
|
|
"\n",
|
||
|
|
"Test Case 4: currL=-10A, currR=-10A, roll=2.0°, gap=15.0mm\n",
|
||
|
|
" Force: Standalone= 50.4344 N | Original= 50.4344 N | Diff=1.42e-14\n",
|
||
|
|
" Torque: Standalone= 303.7401 mN·m | Original= 303.7401 mN·m | Diff=0.00e+00\n",
|
||
|
|
"\n",
|
||
|
|
"================================================================================\n",
|
||
|
|
"✓ All tests passed! Standalone predictor matches original model perfectly.\n",
|
||
|
|
"\n",
|
||
|
|
"The standalone predictor is ready for integration into your simulator!\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Import the standalone predictor (reload to get latest version)\n",
|
||
|
|
"import importlib\n",
|
||
|
|
"import maglev_predictor\n",
|
||
|
|
"importlib.reload(maglev_predictor)\n",
|
||
|
|
"from maglev_predictor import MaglevPredictor\n",
|
||
|
|
"\n",
|
||
|
|
"# Create predictor instance\n",
|
||
|
|
"standalone_predictor = MaglevPredictor()\n",
|
||
|
|
"\n",
|
||
|
|
"# Test cases\n",
|
||
|
|
"test_cases = [\n",
|
||
|
|
" {'currL': -15, 'currR': -15, 'roll': 0.0, 'gap': 10.0},\n",
|
||
|
|
" {'currL': -15, 'currR': -15, 'roll': 1.0, 'gap': 10.0},\n",
|
||
|
|
" {'currL': -15, 'currR': -10, 'roll': 0.5, 'gap': 12.0},\n",
|
||
|
|
" {'currL': -10, 'currR': -10, 'roll': 2.0, 'gap': 15.0},\n",
|
||
|
|
"]\n",
|
||
|
|
"\n",
|
||
|
|
"print(\"Validation: Comparing Standalone Predictor vs Original Model\")\n",
|
||
|
|
"print(\"=\" * 80)\n",
|
||
|
|
"\n",
|
||
|
|
"for i, case in enumerate(test_cases):\n",
|
||
|
|
" # Standalone predictor\n",
|
||
|
|
" force_standalone, torque_standalone = standalone_predictor.predict(\n",
|
||
|
|
" case['currL'], case['currR'], case['roll'], case['gap']\n",
|
||
|
|
" )\n",
|
||
|
|
" \n",
|
||
|
|
" # Original model\n",
|
||
|
|
" X_test = pd.DataFrame({\n",
|
||
|
|
" 'currL [A]': [case['currL']],\n",
|
||
|
|
" 'currR [A]': [case['currR']],\n",
|
||
|
|
" 'rollDeg [deg]': [case['roll']],\n",
|
||
|
|
" 'invGap': [1/case['gap']]\n",
|
||
|
|
" })\n",
|
||
|
|
" X_test_poly = poly_best.transform(X_test)\n",
|
||
|
|
" y_test_pred = model_best.predict(X_test_poly)\n",
|
||
|
|
" force_original = y_test_pred[0, 0]\n",
|
||
|
|
" torque_original = y_test_pred[0, 1]\n",
|
||
|
|
" \n",
|
||
|
|
" # Compare\n",
|
||
|
|
" force_diff = abs(force_standalone - force_original)\n",
|
||
|
|
" torque_diff = abs(torque_standalone - torque_original)\n",
|
||
|
|
" \n",
|
||
|
|
" print(f\"\\nTest Case {i+1}: currL={case['currL']}A, currR={case['currR']}A, \" + \n",
|
||
|
|
" f\"roll={case['roll']}°, gap={case['gap']}mm\")\n",
|
||
|
|
" print(f\" Force: Standalone={force_standalone:9.4f} N | Original={force_original:9.4f} N | Diff={force_diff:.2e}\")\n",
|
||
|
|
" print(f\" Torque: Standalone={torque_standalone:9.4f} mN·m | Original={torque_original:9.4f} mN·m | Diff={torque_diff:.2e}\")\n",
|
||
|
|
" \n",
|
||
|
|
" # Verify match (should be essentially identical, accounting for floating point)\n",
|
||
|
|
" assert force_diff < 1e-8, f\"Force mismatch in test case {i+1}\"\n",
|
||
|
|
" assert torque_diff < 1e-8, f\"Torque mismatch in test case {i+1}\"\n",
|
||
|
|
"\n",
|
||
|
|
"print(\"\\n\" + \"=\" * 80)\n",
|
||
|
|
"print(\"✓ All tests passed! Standalone predictor matches original model perfectly.\")\n",
|
||
|
|
"print(\"\\nThe standalone predictor is ready for integration into your simulator!\")"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"metadata": {
|
||
|
|
"kernelspec": {
|
||
|
|
"display_name": "base",
|
||
|
|
"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.13.5"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"nbformat": 4,
|
||
|
|
"nbformat_minor": 5
|
||
|
|
}
|