diff --git a/CMakeLists.txt b/CMakeLists.txt index c44d8e64ff..8915140e03 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,6 @@ LIST(APPEND PROCESS_SRCS heat_transport_variables.f90 buildings_variables.f90 water_usage_variables.f90 - constraint_equations.f90 constants.f90 build_variables.f90 current_drive_variables.f90 @@ -96,7 +95,6 @@ LIST(APPEND PROCESS_SRCS structure_variables.f90 output.f90 init_module.f90 - error_handling.f90 global_variables.f90 constraint_variables.f90 vacuum_variables.f90 diff --git a/documentation/proc-pages/usage/troubleshooting.md b/documentation/proc-pages/usage/troubleshooting.md index 5495c79280..e458a55913 100644 --- a/documentation/proc-pages/usage/troubleshooting.md +++ b/documentation/proc-pages/usage/troubleshooting.md @@ -5,30 +5,10 @@ produce infeasible results; that is, the code will not find a consistent set of The highly non-linear nature of the numerics of PROCESS is the reason for this difficulty, and it often requires a great deal of painstaking adjustment of the input file to overcome. -

Error handling

- In general, errors detected during a run are handled in a consistent manner, with the code -producing useful diagnostic messages to help the user understand what has happened. - -There are three levels of errors and warnings that may occur: - -* **Level 1** -- An *informational* message is produced under certain conditions, for example if - the code modified the user's input choice for some reason. -* **Level 2** -- A *warning* message is produced if a non-fatal situation has occurred that may - result in an output case that is inaccurate or unreliable in some way. -* **Level 3** -- An *error* message will occur is a severe of fatal error has occurred and the program cannot continue. - -These messages are printed on the screen during the course of a run, and those still active at the -final (feasible or unfeasible) solution point are also written to the end of the output file -(messages encountered during the iteration process are not copied to the output file, as the -convergence to a valid solution might resolve some of the warnings produced earlier in the -solution process). - -The `error_status` variable returns the highest security level that has been encountered (or zero -if no abnormal conditions have been found); of a severe error (level 3) is flagged at any point the -program is terminated immediately. The final message number encountered during a run is returned via -output variable `error_id` . In addition, with certain messages, a number of diagnostic values may -also be given; these can be used to provide extra diagnostic information if the source code is available +producing useful diagnostic messages to help the user understand what has happened. In the case of an +unrecoverable error, PROCESS will fail with a Python exception. If the error is recoverable, a warning +will be logged that is written to the terminal and output file at the end of the run. ### General problems diff --git a/examples/single_model_evaluation.ipynb b/examples/single_model_evaluation.ipynb index 0d0f4891a0..d92c9ee1b5 100644 --- a/examples/single_model_evaluation.ipynb +++ b/examples/single_model_evaluation.ipynb @@ -72,14 +72,14 @@ " **************************************************************************************************************\n", " \n", " Version : 3.1.0\n", - " Git Tag : v3.1.0-338-g074d5b12e\n", - " Git Branch : 3437-individual-model-running-examples\n", - " Date : 03/04/2025 UTC\n", - " Time : 09:00\n", - " User : jon\n", - " Computer : jon-Precision-3560\n", - " Directory : /home/jon/code/process/examples\n", - " Input : /home/jon/code/process/examples/data/large_tokamak_once_through_IN.DAT\n", + " Git Tag : v3.1.0-365-g32d03883\n", + " Git Branch : convert-constraints-f90\n", + " Date : 24/04/2025 UTC\n", + " Time : 08:40\n", + " User : ubuntu\n", + " Computer : nunn04\n", + " Directory : /home/ubuntu/PROCESS/examples\n", + " Input : /home/ubuntu/PROCESS/examples/data/large_tokamak_once_through_IN.DAT\n", " Run title : generic large tokamak\n", " Run type : Reactor concept design: Pulsed tokamak model model, (c) UK Atomic Energy Authority\n", " \n", @@ -100,12 +100,20 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/jon/code/process/process/init.py:47: UserWarning: tmargmin_cs and tmargmin should not both be specified in IN.DAT tmargmin_cs has been ignored\n", + "/home/ubuntu/PROCESS/process/init.py:77: UserWarning: tmargmin_cs and tmargmin should not both be specified in IN.DAT tmargmin_cs has been ignored\n", " check_process(inputs)\n", + "/home/ubuntu/PROCESS/process/iteration_variables.py:357: UserWarning: String j_cs_flat_top_end of length 17 is trying to initiate as b' ' with length14. String string will be truncated!\n", + " fortran.numerics.name_xc[i] = string_to_f2py_compatible(\n", + "/home/ubuntu/PROCESS/process/iteration_variables.py:357: UserWarning: String f_j_cs_start_pulse_end_flat_top of length 31 is trying to initiate as b' ' with length14. String string will be truncated!\n", + " fortran.numerics.name_xc[i] = string_to_f2py_compatible(\n", + "/home/ubuntu/PROCESS/process/iteration_variables.py:357: UserWarning: String dr_tf_nose_case of length 15 is trying to initiate as b' ' with length14. String string will be truncated!\n", + " fortran.numerics.name_xc[i] = string_to_f2py_compatible(\n", + "/home/ubuntu/PROCESS/process/iteration_variables.py:357: UserWarning: String f_nd_alpha_electron of length 19 is trying to initiate as b' ' with length14. String string will be truncated!\n", + " fortran.numerics.name_xc[i] = string_to_f2py_compatible(\n", "process.pfcoil - WARNING - Ratio of central solenoid overall current density at beginning of flat-top / end of flat-top > 1 (|f_j_cs_start_end_flat_top| > 1)\n", - "/home/jon/code/process/process/costs.py:2799: RuntimeWarning: invalid value encountered in sqrt\n", + "/home/ubuntu/PROCESS/process/costs.py:2801: RuntimeWarning: invalid value encountered in sqrt\n", " annoam = cost_variables.ucoam[cost_variables.lsa - 1] * np.sqrt(\n", - "/home/jon/code/process/process/costs.py:2868: RuntimeWarning: invalid value encountered in sqrt\n", + "/home/ubuntu/PROCESS/process/costs.py:2870: RuntimeWarning: invalid value encountered in sqrt\n", " annwst = cost_variables.ucwst[cost_variables.lsa - 1] * np.sqrt(\n", "process.power - ERROR - ERROR Negative stored energy in poloidal field\n" ] @@ -270,6 +278,9 @@ "metadata": {}, "outputs": [], "source": [ + "import process.constraints\n", + "\n", + "\n", "def run_impurities(w_imp_fracs):\n", " \"\"\"Calculate responses to W impurities.\"\"\"\n", " n = w_imp_fracs.shape[0]\n", @@ -285,7 +296,8 @@ " single_run.models.physics.physics()\n", "\n", " # Evaluate constraint equation 15 (L-H threshold constraint)\n", - " con15_value, _, _, _, _ = process.fortran.constraints.constraint_eqn_015()\n", + " con15_equation = process.constraints.ConstraintManager.get_constraint(15).result\n", + " con15_value = con15_equation().cc\n", "\n", " # Need to copy values\n", " p_plasma_rad_mw[i] = process.fortran.physics_variables.p_plasma_rad_mw.item()\n", @@ -309,7 +321,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 7, @@ -318,7 +330,7 @@ }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -370,7 +382,7 @@ }, { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAHHCAYAAABTMjf2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABy1ElEQVR4nO3deViU1dsH8O+AMKBsIruiuAOhopgoLrjgkoZL5p4LmZZ7oqZmhmi5lAtaqGWplZpmqblFP0VJRVJzJxVTUUzBBRUUFHA47x+8MznODMzADDDM93NdXDVnnuWeZwbn5pxzP0cihBAgIiIiMkFmZR0AERERUVlhIkREREQmi4kQERERmSwmQkRERGSymAgRERGRyWIiRERERCaLiRARERGZLCZCREREZLKYCBEREZHJYiJkZEaMGAEvL6+yDqNEvLy8MGLEiFI51w8//ABvb29YWFjAwcGhVM5ZlOvXr0MikWD9+vVlHUqpKs333ZjFxcVBIpHg559/LutQABgmnjlz5kAikWi1rUQiwZw5c/R27oqkLP4t+emnn+Do6IgnT54Y9DwtW7bEBx98YNBzyDERMoCffvoJEokE27dvV3muSZMmkEgkOHjwoMpzNWvWRFBQUGmEWK5cuHABc+bMwfXr1/V63EuXLmHEiBGoW7cu1qxZg6+//lqvxy/Kpk2bEBUVVarnNFXZ2dmYM2cO4uLitNq+JF/u69evh0QiwV9//aX2+fbt28PPz6/I4/DzYRrK6/us6+8MAMhkMkRERGDChAmwsbFRtP/vf//DyJEj4efnB3Nzc41/rMsTN3U/mzdvVtp2+vTpiI6ORlpaWnFenk4qGfwMJqhNmzYAgCNHjqBPnz6K9szMTCQmJqJSpUqIj49Hhw4dFM/dvHkTN2/exMCBAws99po1a5Cfn2+YwEtJUlISzMz+y8EvXLiAyMhItG/fXq+9XXFxccjPz8fy5ctRr149vR1XW5s2bUJiYiLef/99pfZatWrh6dOnsLCwKPWYytLL77s+ZWdnIzIyEkBBImIMNH0+qGIx5Ptckn9LivM7s2vXLiQlJWH06NFK7Zs2bcKWLVvQrFkzeHh4FHmcQYMGoXv37kptrVq1Unrcq1cv2NnZYeXKlZg7d65W8RUXEyED8PDwQO3atXHkyBGl9oSEBAgh0K9fP5Xn5I/lSZQmxvrlKYTAs2fPYG1tDalUWirnvHv3LgAUOST2YmylQSKRwMrKqlTOVZ6U1vtu6rKzs1G5cuWyDoOK4dmzZ7C0tNT6D4bS/rdk3bp1aN26NapXr67UPn/+fKxZswYWFhZ4/fXXkZiYWOhxmjVrhrfeeqvQbczMzPDmm2/i+++/R2RkpNZDqcXBoTEDadOmDU6fPo2nT58q2uLj4/HKK6/gtddew59//qnUsxMfHw+JRILWrVsXetyX5wjJuxoXL16M6Oho1KlTB5UrV0aXLl1w8+ZNCCEwb9481KhRA9bW1ujVqxcePHigdEwvLy+8/vrr+N///gd/f39YWVnB19cX27ZtU9pO07i+fKjgxaEt+TF///13NG/eHNbW1vjqq68Uz8nniqxfvx79+vUDAHTo0EHRTRoXF4fhw4fDyckJeXl5Kufs0qULGjZsqPE6eXl5ISIiAgDg7OysNM+gsNjWrVuHjh07wsXFBVKpFL6+vli1apXac/z2228IDg6Gra0t7Ozs8Oqrr2LTpk0ACv7C2rNnD27cuKF4TfL3TdO4/oEDB9C2bVtUqVIFDg4O6NWrFy5evKi0jfw9uHLlCkaMGAEHBwfY29sjLCwM2dnZGq+H3OHDh9GvXz/UrFkTUqkUnp6emDx5stLnVG7r1q3w9fWFlZUV/Pz8sH37drVz1BYvXoygoCBUq1YN1tbWCAgIUDvk9PIcIfnnJj4+HuHh4XB2dkaVKlXQp08f3Lt3T2nfv/76C127doWTkxOsra1Ru3ZtvP3224rr6ezsDACKfzDL+7ySwj4fcvn5+fj0009Ro0YNWFlZoVOnTrhy5YrKcfz8/HDy5Em0a9cOlStXxocffggAyMnJQUREBOrVq6d4rz/44APk5OQoHWPfvn1o06YNHBwcYGNjg4YNGyqOoWs8QMHnJiAgANbW1nBycsJbb72FW7duFXlNcnJyMHnyZDg7O8PW1hY9e/bEv//+W+R+cs+ePcOcOXPQoEEDWFlZwd3dHW+88QauXr2q2CYrKwtTpkyBp6cnpFIpGjZsiMWLF0MIoXQsiUSC8ePHY8eOHfDz84NUKsUrr7yCmJgYpe0eP36M999/H15eXpBKpXBxcUHnzp1x6tQpAIW/z/Lh2c2bN+Ojjz5C9erVUblyZWRmZuLBgweYOnUqGjVqBBsbG9jZ2eG1117D2bNnlc6v7t+SESNGwMbGBrdu3ULv3r1hY2MDZ2dnTJ06FTKZTLGfrr8zz549Q0xMDEJCQlSe8/Dw0PmP9KysLOTm5ha6TefOnXHjxg2cOXNGp2Prij1CBtKmTRv88MMPOHbsmKLbMT4+HkFBQQgKCkJGRgYSExPRuHFjxXPe3t6oVq1asc63ceNG5ObmYsKECXjw4AE+++wz9O/fHx07dkRcXBymT5+OK1eu4IsvvsDUqVOxdu1apf3/+ecfDBgwAO+99x6GDx+OdevWoV+/foiJiUHnzp2LFVNSUhIGDRqEd999F6NGjVKbuLRr1w4TJ07EihUr8OGHH8LHxwcA4OPjg6FDh+L777/H77//jtdff12xT1paGg4cOKBIdNSJiorC999/j+3bt2PVqlWwsbFRXOvCYlu1ahVeeeUV9OzZE5UqVcKuXbswduxY5OfnY9y4cYr9169fj7fffhuvvPIKZs6cCQcHB5w+fRoxMTEYPHgwZs2ahYyMDPz7779YtmwZACiNqb9s//79eO2111CnTh3MmTMHT58+xRdffIHWrVvj1KlTKl+S/fv3R+3atbFgwQKcOnUK33zzDVxcXLBo0aJC3pGCL6ns7GyMGTMG1apVw/Hjx/HFF1/g33//xdatWxXb7dmzBwMGDECjRo2wYMECPHz4ECNHjlT5SxAAli9fjp49e2LIkCHIzc3F5s2b0a9fP+zevRs9evQoNB4AmDBhAqpWrYqIiAhcv34dUVFRGD9+PLZs2QKgoGevS5cucHZ2xowZM+Dg4IDr168rEnVnZ2esWrUKY8aMQZ8+ffDGG28AgNL7bQgZGRm4f/++Sru6xP1l2nw+Fi5cCDMzM0ydOhUZGRn47LPPMGTIEBw7dkxpu/T0dLz22msYOHAg3nrrLbi6uiI/Px89e/bEkSNHMHr0aPj4+OD8+fNYtmwZLl++jB07dgAA/v77b7z++uto3Lgx5s6dC6lUiitXriA+Pl4lZm3iWb9+PcLCwvDqq69iwYIFuHPnDpYvX474+HicPn260N7Zd955Bxs2bMDgwYMRFBSEAwcOaPX5AQrmrrz++uuIjY3FwIEDMWnSJDx+/Bj79u1DYmIi6tatCyEEevbsiYMHD2LkyJHw9/fH77//jmnTpuHWrVuK90HuyJEj2LZtG8aOHQtbW1usWLECffv2RUpKiuLf6ffeew8///wzxo8fD19fX6Snp+PIkSO4ePEimjVrptX7PG/ePFhaWmLq1KnIycmBpaUlLly4gB07dqBfv36oXbs27ty5g6+++grBwcG4cOFCkcNPMpkMXbt2RWBgIBYvXoz9+/djyZIlqFu3LsaMGVOs35mTJ08iNzcXzZo10+o9KUxkZCSmTZsGiUSCgIAAfPrpp+jSpYvKdgEBAQAKvh+bNm1a4vNqJMgg/v77bwFAzJs3TwghRF5enqhSpYr47rvvhBBCuLq6iujoaCGEEJmZmcLc3FyMGjWqyOMOHz5c1KpVS/E4OTlZABDOzs7i0aNHivaZM2cKAKJJkyYiLy9P0T5o0CBhaWkpnj17pmirVauWACB++eUXRVtGRoZwd3cXTZs2VbRFREQIdR+ZdevWCQAiOTlZ5ZgxMTEq29eqVUsMHz5c8Xjr1q0CgDh48KDSdjKZTNSoUUMMGDBAqX3p0qVCIpGIa9euqV6gF8jjvXfvnsr5NcWWnZ2t0ta1a1dRp04dxeNHjx4JW1tbERgYKJ4+faq0bX5+vuL/e/ToofReycnfs3Xr1ina/P39hYuLi0hPT1e0nT17VpiZmYlhw4apvKa3335b6Zh9+vQR1apVUzmXNq9vwYIFQiKRiBs3bijaGjVqJGrUqCEeP36saIuLixMAVF7Ty8fMzc0Vfn5+omPHjkrtL7/v8s9NSEiI0nWbPHmyMDc3V3yet2/fLgCIEydOaHxd9+7dEwBERESExm1edPDgQQFAbN26VavtXySPu7CfV155pcjjaPp8yGPz8fEROTk5ivbly5cLAOL8+fOKtuDgYAFArF69WukYP/zwgzAzMxOHDx9Wal+9erUAIOLj44UQQixbtkzt70hx4snNzRUuLi7Cz89P6fdi9+7dAoD4+OOPFW0v/1ty5swZAUCMHTtW6dyDBw/W6n1du3atACCWLl2q8pz8s7Vjxw4BQHzyySdKz7/55ptCIpGIK1euKNoACEtLS6W2s2fPCgDiiy++ULTZ29uLcePGFRpbUe9znTp1VH6Hnj17JmQymVJbcnKykEqlYu7cuUptL/9bMnz4cAFAaTshhGjatKkICAhQPNb1d+abb75R+fypo+n1CiHEjRs3RJcuXcSqVavEzp07RVRUlKhZs6YwMzMTu3fvVruPpaWlGDNmjFYxFheHxgzEx8cH1apVU8z9OXv2LLKyshRVYUFBQYq/uhISEiCTyYqcH1SYfv36wd7eXvE4MDAQAPDWW2+hUqVKSu25ubkqXdUeHh5KE7vt7OwwbNgwnD59utiz9mvXro2uXbsWa1+gYIx4yJAh2LlzJx4/fqxo37hxI4KCglC7du1iH1tTbC/OE5L/xR8cHIxr164hIyMDQMFQwuPHjzFjxgyV8fnijGOnpqbizJkzGDFiBBwdHRXtjRs3RufOnbF3716Vfd577z2lx23btkV6ejoyMzMLPdeLry8rKwv3799HUFAQhBA4ffo0AOD27ds4f/48hg0bpvTXa3BwMBo1alToMR8+fIiMjAy0bdtWMTxQlNGjRytdt7Zt20Imk+HGjRsA/pvjtXv3bq16W0pLdHQ09u3bp/Kjr56osLAwWFpaKh63bdsWAHDt2jWl7aRSKcLCwpTatm7dCh8fH3h7e+P+/fuKn44dOwKAompVfm1//fXXIoswiornr7/+wt27dzF27Fil34sePXrA29sbe/bs0Xhs+Wd84sSJSu3aTjD+5Zdf4OTkhAkTJqg8J/9s7d27F+bm5irnmDJlCoQQ+O2335TaQ0JCULduXcXjxo0bw87OTun6Ozg44NixY7h9+7ZWcaozfPhwlfmJUqlUMU9IJpMhPT1dMWyp7e+Vun8jXv7s6CI9PR0AULVq1WIfo2bNmvj999/x3nvvITQ0FJMmTcLp06fh7OyMKVOmqN2natWqante9YmJkIFIJBIEBQUp5gLFx8fDxcVFUb30YiIk/29JEqGaNWsqPZYnRZ6enmrbHz58qNRer149lS/xBg0aAECxy9pLkqjIDRs2DE+fPlXciiApKQknT57E0KFDS3RcTbHFx8cjJCREMU/H2dlZMV9CngjJ5xxoUyKtDfkXvrqhQx8fH9y/fx9ZWVlK7S+/3/J/nF5+X1+WkpKiSLjkcweCg4MB/Pf65PGoq7RT17Z79260bNkSVlZWcHR0VHS7y49XlKJeS3BwMPr27YvIyEg4OTmhV69eWLduncpcF32SyWRIS0tT+nl5PkOLFi0QEhKi8lOSL4oXafseV69eXSlBAQqGuv/++284Ozsr/ch/p+WFBAMGDEDr1q3xzjvvwNXVFQMHDsRPP/2kNikqKp7CPsfe3t6K59W5ceMGzMzMlBIPTcdS5+rVq2jYsKHSH33qzuHh4QFbW1uldvlw/Mvxvfx6gYLX/OL1/+yzz5CYmAhPT0+0aNECc+bM0TnZUPdvUX5+PpYtW4b69etDKpXCyckJzs7OOHfunFa/V1ZWVoo5QJpiLy7x0nyqknJ0dERYWBiSkpLUzgkTQhh0ojTARMig2rRpg4yMDJw/f14xP0guKCgIN27cwK1bt3DkyBF4eHigTp06xT6Xubm5Tu3F+TBr+jDKJ+C9TB9VWL6+vggICMCGDRsAABs2bIClpSX69+9fouOqi+3q1avo1KkT7t+/j6VLl2LPnj3Yt28fJk+eDADl6rYFxXlfZTIZOnfujD179mD69OnYsWMH9u3bp5hoWZzXd/jwYfTs2RNWVlZYuXIl9u7di3379mHw4MFaf8aKei3y+/0kJCRg/PjxuHXrFt5++20EBAQY7KZuN2/ehLu7u9LP0aNHDXIuTbR9j9V9lvPz89GoUSO1PVb79u3D2LFjFfseOnQI+/fvx9ChQ3Hu3DkMGDAAnTt3Vvm91ue/JcZAm9fbv39/XLt2DV988QU8PDzw+eef45VXXlHpXSqMuvdv/vz5CA8PR7t27bBhwwb8/vvv2LdvH1555RWtfk81xV4S8nlR+kimXib/g/3lQh4AePToEZycnPR+zhdxsrQBvXg/ofj4eKVu3oCAAEilUsTFxeHYsWMq91QobVeuXFHJvC9fvgwAiom68r8AHz16pDTpsbC/9LRRVLY/bNgwhIeHIzU1FZs2bUKPHj309lf3i3bt2oWcnBzs3LlT6a/Bl29+Kf+rNTExsdD7E2n7V0ytWrUAFPR2vezSpUtwcnJClSpVtDpWYc6fP4/Lly/ju+++w7BhwxTt+/btUxuPuoqgl9t++eUXWFlZ4ffff1cqj1+3bl2J431Zy5Yt0bJlS3z66afYtGkThgwZgs2bN+Odd97R+1+Mbm5uKtelSZMmej2HIf/KrVu3Ls6ePYtOnToVeR4zMzN06tQJnTp1wtKlSzF//nzMmjULBw8eVFshpMmLn2P5EJxcUlKS4nlN++bn5yt6dl7cTxt169bFsWPHkJeXp7F6qVatWti/fz8eP36s1Ct06dIlpfh15e7ujrFjx2Ls2LG4e/cumjVrhk8//RSvvfYagOK9zz///DM6dOiAb7/9Vqldn0mBrnF5e3sDAJKTk9UOkZeEvBft5V6sW7duITc3V9FrZyjsETKg5s2bw8rKChs3bsStW7eUeoSkUimaNWuG6OhoZGVllWhYTB9u376tdCfszMxMfP/99/D394ebmxuA/xKAQ4cOKbbLysrCd999V6Jzy7/kHz16pPb5QYMGQSKRYNKkSbh27VqR958oLvlfUS/+xZeRkaHypd6lSxfY2tpiwYIFePbsmdJzL+5bpUoVrbqx3d3d4e/vj++++07pGiQmJuJ///uf3pJkda9PCIHly5crbefh4QE/Pz98//33Sj0uf/zxB86fP69yTIlEotR7cP36dUVVkj48fPhQpdfB398fABTDY/L75mj6DOnKysrKYENectp+Poqjf//+uHXrFtasWaPy3NOnTxVDrer+An/52mqrefPmcHFxwerVq5X2/e2333Dx4sVCK8DkScOKFSuU2rW9I3Pfvn1x//59fPnllyrPyT873bt3h0wmU9lm2bJlkEgkihi0JZPJVN4/FxcXeHh4KL3+4rzP5ubmKp/5rVu3anUbAm3p+jsTEBAAS0tLjXdU18bLt8UACpKdtWvXonHjxnB3d1d67uTJkwBg8BUX2CNkQJaWlnj11Vdx+PBhSKVSRSmgXFBQEJYsWQKgZPOD9KFBgwYYOXIkTpw4AVdXV6xduxZ37txRSgK6dOmCmjVrYuTIkZg2bRrMzc2xdu1aODs7IyUlpdjn9vf3h7m5ORYtWoSMjAxIpVLFvXyAgr8SunXrhq1bt8LBwUHrklpddenSBZaWlggNDcW7776LJ0+eYM2aNXBxcUFqaqpiOzs7OyxbtgzvvPMOXn31VQwePBhVq1bF2bNnkZ2drUgMAwICsGXLFoSHh+PVV1+FjY0NQkND1Z77888/x2uvvYZWrVph5MiRivJ5e3t7vd0Px9vbG3Xr1sXUqVNx69Yt2NnZ4ZdfflHb1T1//nz06tULrVu3RlhYGB4+fIgvv/wSfn5+SslRjx49sHTpUnTr1g2DBw/G3bt3ER0djXr16uHcuXN6ifu7777DypUr0adPH9StWxePHz/GmjVrYGdnp0gSra2t4evriy1btqBBgwZwdHSEn59fkfO4fvnlF0WPwIuGDx+uMr9O33T5fOhq6NCh+Omnn/Dee+/h4MGDaN26NWQyGS5duoSffvpJcQ+tuXPn4tChQ+jRowdq1aqFu3fvYuXKlahRo4bO/yZZWFhg0aJFCAsLQ3BwMAYNGqQon/fy8lIMMavj7++PQYMGYeXKlcjIyEBQUBBiY2PV9kqqM2zYMHz//fcIDw/H8ePH0bZtW2RlZWH//v0YO3YsevXqhdDQUHTo0AGzZs3C9evX0aRJE/zvf//Dr7/+ivfff19lflJRHj9+jBo1auDNN99EkyZNYGNjg/379+PEiROKf9eB4r3Pr7/+OubOnYuwsDAEBQXh/Pnz2LhxY4mmT7xM198ZKysrdOnSBfv371e50/O5c+ewc+dOAAW9xhkZGfjkk08AFPSkyl/vBx98oJiC4OHhgevXr+Orr75CVlaWyh9kQEFvdc2aNQ1bOg+wfN7Q5GXsQUFBKs9t27ZNABC2trbi+fPnWh1PU/n8559/rrSdpvJgeenvi6XItWrVEj169BC///67aNy4sZBKpcLb21ttafHJkydFYGCgsLS0FDVr1hRLly7VWD7fo0cPta/h5TJqIYRYs2aNqFOnjjA3N1dbSv/TTz8JAGL06NGFXB1lhZXPa4pt586donHjxsLKykp4eXmJRYsWKUpzX3x98m2DgoKEtbW1sLOzEy1atBA//vij4vknT56IwYMHCwcHB6Wyc3Ulr0IIsX//ftG6dWvF8UJDQ8WFCxe0ek3q3gN1Lly4IEJCQoSNjY1wcnISo0aNUpQFvxzP5s2bhbe3t5BKpcLPz0/s3LlT9O3bV3h7eytt9+2334r69esrPjfr1q1Te6sFTeXzL5fFyz+78s/AqVOnxKBBg0TNmjWFVCoVLi4u4vXXXxd//fWX0n5Hjx4VAQEBwtLSssiyYPk5NP28XHb+Ik1xywUHB2tVPq/p86Hpd1fd56awc+Xm5opFixaJV155RUilUlG1alUREBAgIiMjRUZGhhBCiNjYWNGrVy/h4eEhLC0thYeHhxg0aJC4fPmyyrXSJh4hhNiyZYto2rSpkEqlwtHRUQwZMkT8+++/Stuo+3w8ffpUTJw4UVSrVk1UqVJFhIaGips3b2pd4p2dnS1mzZolateuLSwsLISbm5t48803xdWrVxXbPH78WEyePFl4eHgICwsLUb9+ffH5558r3b5BiILyeXVl8S9+hnNycsS0adNEkyZNhK2trahSpYpo0qSJWLlypdI+ur7PQhSUz0+ZMkW4u7sLa2tr0bp1a5GQkCCCg4NFcHCwYjtN5fNVqlRROaa6a67L74wQBd9ZEolEpKSkKLUXdkuJF3/nN23aJNq1ayecnZ1FpUqVhJOTk+jTp484efKkyrlkMplwd3cXH330UaEx6YNEiAo604205uXlBT8/P+zevbusQ9Ho119/Re/evXHo0CFF2S6VPn9/fzg7O6vMnyGiik8mk8HX1xf9+/fHvHnzDHquHTt2YPDgwbh69arKkJm+cY4QGYU1a9agTp06ZT6EaCry8vLw/Plzpba4uDicPXvWaBY1JSL9Mjc3x9y5cxEdHW2wik25RYsWYfz48QZPggDOEaJybvPmzTh37hz27NmD5cuXG/x+ElTg1q1bCAkJwVtvvQUPDw9cunQJq1evhpubm8qN2ojIdAwYMAADBgww+HkSEhIMfg45JkJUrg0aNAg2NjYYOXKk4t4nZHhVq1ZFQEAAvvnmG9y7dw9VqlRBjx49sHDhwmKvh0dEVB5xjhARERGZLM4RIiIiIpPFRIiIiIhMFucIFSE/Px+3b9+Gra0tJ+oSEREZCSEEHj9+DA8PD5iZae73YSJUhNu3bxv8DrNERERkGDdv3kSNGjU0Ps9EqAjyxflu3rwJOzu7Mo6GiIiItJGZmQlPT0+lRXbVYSJUBPlwmJ2dHRMhIiIiI1PUtBZOliYiIiKTxUSIiIiITBYTISIiIjJZTISIiIjIZDERIiIiIpPFRIiIiIhMFhMhIiIiMllMhIiIiMhkMREiIiIik8U7S1cQsnyB48kPcPfxM7jYWqFFbUeYm3GRWCIiosIwEaoAYhJTEbnrAlIznina3O2tEBHqi25+7mUYGRERUfnGoTEjF5OYijEbTiklQQCQlvEMYzacQkxiahlFRkREVP4xETJisnyByF0XINQ8J2+L3HUBsnx1WxARERETISN2PPmBSk/QiwSA1IxnOJ78oPSCIiIiMiJGlwhFR0fDy8sLVlZWCAwMxPHjxwvdPioqCg0bNoS1tTU8PT0xefJkPHumOXkwJncfa/c6tN2OiIjI1BhVIrRlyxaEh4cjIiICp06dQpMmTdC1a1fcvXtX7fabNm3CjBkzEBERgYsXL+Lbb7/Fli1b8OGHH5Zy5IbhYmul1+2IiIhMjVElQkuXLsWoUaMQFhYGX19frF69GpUrV8batWvVbn/06FG0bt0agwcPhpeXF7p06YJBgwYV2YtkLFrUdoS7vRU0FclLUFA91qK2I2T5AglX0/HrmVtIuJrOeUNEREQwokQoNzcXJ0+eREhIiKLNzMwMISEhSEhIULtPUFAQTp48qUh8rl27hr1796J79+6lErOhmZtJEBHqCwAqyZD8cUSoL/ZdSEObRQcwaM2fmLT5DAat+RNtFh1gRRkREZk8o0mE7t+/D5lMBldXV6V2V1dXpKWlqd1n8ODBmDt3Ltq0aQMLCwvUrVsX7du3L3RoLCcnB5mZmUo/5Vk3P3eseqsZ3OyVh7/c7K2w6q1mAMDyeiIiIg0q9A0V4+LiMH/+fKxcuRKBgYG4cuUKJk2ahHnz5mH27Nlq91mwYAEiIyNLOdKS6ebnjs6+bip3lgaANosOaCyvl6CgvL6zrxvvQk1ERCbJaBIhJycnmJub486dO0rtd+7cgZubm9p9Zs+ejaFDh+Kdd94BADRq1AhZWVkYPXo0Zs2aBTMz1Q6xmTNnIjw8XPE4MzMTnp6eenwlhmFuJkGrutWU2hKupmtdXv/yvkRERKbAaIbGLC0tERAQgNjYWEVbfn4+YmNj0apVK7X7ZGdnqyQ75ubmAAAh1E8WlkqlsLOzU/oxViyvJyIiKpzR9AgBQHh4OIYPH47mzZujRYsWiIqKQlZWFsLCwgAAw4YNQ/Xq1bFgwQIAQGhoKJYuXYqmTZsqhsZmz56N0NBQRUJUkbG8noiIqHBGlQgNGDAA9+7dw8cff4y0tDT4+/sjJiZGMYE6JSVFqQfoo48+gkQiwUcffYRbt27B2dkZoaGh+PTTT8vqJZQqeXl9WsYztfOEJCiYVC2fT8QV7ImIyNRIhKYxIgJQMEfI3t4eGRkZRjlMJl+UFYBSMiRPb1a91Qzd/Ny5gj0REVUo2n5/G80cISqeosrr5UkQS+yJiMgUGdXQGBWPpvJ6czNJkSvYs8SeiIgqMiZCJkJdeT2g2wr2LLEnIqKKhkNjJo4l9kREZMrYI2TidCmxZ1UZERFVNEyETJy2JfYPs3LRZtEBVpUREVGFwqExE6fNCvY9m7hj3CZWlRERUcXDRIgKLbGPHtwUO8+maqwqAwqqymT5vB0VEREZHw6NEQDNJfasKiMiooqMiRApqCuxZ1UZERFVZBwao0Jx4VYiIqrI2CNEhdJl4VaW1xMRkbFhIkSFkleVjdlwChKoX7g1ItQX+y6kcdFWIiIyOhwaoyIVtXArAC7aSkRERok9QqQVTVVlANBm0QEu2kpEREaJiRBpTV1VWcLVdJbXExGR0eLQGJUIy+uJiMiYMRGiEmF5PRERGTMOjVGJ6FJeD4Al9kREVK4wEaIS0ba83txMgpjEVJbYExFRucKhMSqxosrru/m5IyYxlSX2RERU7rBHiPRCU3m9uZkEsnyByF0XWGJPRETlDhMh0ht15fUAuII9ERGVWxwaI4NjiT0REZVXTITI4FhiT0RE5RWHxsjguII9ERGVV0yEyOC4gj0REZVXHBqjUsEV7ImIqDxijxCVGq5gT0RE5Q0TISpVXMGeiIjKEw6NUZljeT0REZUVJkJU5lheT0REZYVDY1TmuII9ERGVFSZCVOa4gj0REZUVDo1RucAV7ImIqCywR4jKDa5gT0REpY2JEJUrXMGeiIhKE4fGyCiwxJ6IiAzB6BKh6OhoeHl5wcrKCoGBgTh+/Hih2z969Ajjxo2Du7s7pFIpGjRogL1795ZStKQvupTYy/IFEq6m49czt5BwNR2yfHUDakREREY2NLZlyxaEh4dj9erVCAwMRFRUFLp27YqkpCS4uLiobJ+bm4vOnTvDxcUFP//8M6pXr44bN27AwcGh9IOnEtG2xP5hVi7aLDrAqjIiItKKRAhhNH8uBwYG4tVXX8WXX34JAMjPz4enpycmTJiAGTNmqGy/evVqfP7557h06RIsLCyKdc7MzEzY29sjIyMDdnZ2JYqfSkZeNQaoL7Ef3a42vj6UrJIoyZ+XV58REVHFp+33t9EMjeXm5uLkyZMICQlRtJmZmSEkJAQJCQlq99m5cydatWqFcePGwdXVFX5+fpg/fz5kMpnG8+Tk5CAzM1Pph8qHwkrsowc3xc6zqRqryoCCqjIOkxER0YuMZmjs/v37kMlkcHV1VWp3dXXFpUuX1O5z7do1HDhwAEOGDMHevXtx5coVjB07Fnl5eYiIiFC7z4IFCxAZGan3+Ek/NJXYs6qMiIiKw2gSoeLIz8+Hi4sLvv76a5ibmyMgIAC3bt3C559/rjERmjlzJsLDwxWPMzMz4enpWVohkxbUldizqoyIiIrDaBIhJycnmJub486dO0rtd+7cgZubm9p93N3dYWFhAXNzc0Wbj48P0tLSkJubC0tLS5V9pFIppFKpfoMng+PCrUREVBxGM0fI0tISAQEBiI2NVbTl5+cjNjYWrVq1UrtP69atceXKFeTn5yvaLl++DHd3d7VJEBkveVWZpntKS1BQPdaitiPL64mISMFoeoQAIDw8HMOHD0fz5s3RokULREVFISsrC2FhYQCAYcOGoXr16liwYAEAYMyYMfjyyy8xadIkTJgwAf/88w/mz5+PiRMnluXLIAPQduHWfRfSuGgrEREpGFUiNGDAANy7dw8ff/wx0tLS4O/vj5iYGMUE6pSUFJiZ/dfJ5enpid9//x2TJ09G48aNUb16dUyaNAnTp08vq5dABiSvKns50XH7/0QHAMZsOKVSWSZftJXl9UREpseo7iNUFngfIeMjyxcqVWUAVG60+CL5DRmPTO/IRVuJiCoAbb+/japHiEgb6qrKEq6ms7yeiIhUGM1kaaKSYHk9ERGpw0SITALL64mISB0OjZFJ0HbRVvl8InXzjDh3iIio4mEiRCZB2/J6czMJYhJTWWJPRGQiijU09ujRI3zzzTeYOXMmHjx4AAA4deoUbt26pdfgiPSpsEVb5aXz8hXuX55YLS+xj0lMLc2QiYjIwHTuETp37hxCQkJgb2+P69evY9SoUXB0dMS2bduQkpKC77//3hBxEumFpkVbzc0kkOULRO66oHEFewkKVrDv7OvGYTIiogpC5x6h8PBwjBgxAv/88w+srP77y7p79+44dOiQXoMjMgR5eX0v/+poVbeaIqnRZQV7IiKqGHROhE6cOIF3331Xpb169epIS0vTS1BEZYEl9kREpkfnREgqlSIzM1Ol/fLly3B2dtZLUERlgSX2RESmR+dEqGfPnpg7dy7y8vIAABKJBCkpKZg+fTr69u2r9wCJSgtXsCciMj06rzWWkZGBN998E3/99RceP34MDw8PpKWloVWrVti7dy+qVKliqFjLBNcaMy3yqjFAfYn9qreaAQDL64mIyjltv7+LvejqkSNHcO7cOTx58gTNmjVDSEhIsYMtz5gImZ7C7iMEqF/B/sVEickQEVHZM3giZCqYCJkmrmBPRGTcDLb6/Ny5cwt9/uOPP9b1kETlDlewJyIyDTonQtu3b1d6nJeXh+TkZFSqVAl169ZlIkQVFsvriYgqHp0TodOnT6u0ZWZmYsSIEejTp49egiIqj1heT0RU8RRrrbGX2dnZITIyErNnz9bH4YjKJZbXExFVPHpbfT4jIwMZGRn6OhxRuaPtCvb7LqSxvJ6IyEjonAitWLFC6bEQAqmpqfjhhx/w2muv6S0wovJIvoL9y4mOWxHl9fLV61leT0RUvuhcPl+7dm2lx2ZmZnB2dkbHjh0xc+ZM2Nra6jXAssbyeVKH5fVEROWbwcrnk5OTSxQYUUXA8noioopBL5OliYjl9URExkirHqE33nhD6wNu27at2MEQGTOW1xMRGR+tEiF7e3tDx0Fk9OTl9WkZz1QmSwP/zRGSzydSN8+Ic4eIiEqXVonQunXrDB0HkdHTtrze3ExS6MKurCojIio9nCNEpEfy8no3e+XhLzd7K0XpfExiKsZsOKUysVpeYh+TmFqaIRMRmbRi3VDx559/xk8//YSUlBTk5uYqPXfq1Cm9BEZkrLr5uaOzr5vaYS9ZvkDkrgtqh84ECnqOInddQGdfNw6TERGVAp17hFasWIGwsDC4urri9OnTaNGiBapVq4Zr167xhopE/09eXt/Lvzpa1a2mSGqOJz/QusSeiIgMT+dEaOXKlfj666/xxRdfwNLSEh988AH27duHiRMncokNoiKwxJ6IqHzRORFKSUlBUFAQAMDa2hqPHz8GAAwdOhQ//vijfqMjqmB0KbHnwq1ERIan8xwhNzc3PHjwALVq1ULNmjXx559/okmTJkhOToaOq3UQmRxtS+wfZuWqLNfBqjIiIv3TuUeoY8eO2LlzJwAgLCwMkydPRufOnTFgwAD06dNH7wESVSTyEnvgv5J6Ofnjnk3cMW4Tq8qIiEqDzouu5ufnIz8/H5UqFXQmbd68GUePHkX9+vXx7rvvwtLS0iCBlhUuukqGoOk+QrN7+GDenotcuJWIqIS0/f7WOREyNUyEyFDU3Vn6ePIDDFrzZ5H7/jiqJRduJSIqhMFWn69Xrx7eeustDB48GA0aNChRkESmTN0K9qwqIyIqXTrPERo3bhz27NkDHx8fvPrqq1i+fDnS0tIMERuRyeHCrUREpUvnRGjy5Mk4ceIELl68iO7duyM6Ohqenp7o0qULvv/+e0PESGQy5FVlmmb/SFAwl6hFbUeW1xMR6UGx1xpr0KABIiMjcfnyZRw+fBj37t1DWFiYPmNTKzo6Gl5eXrCyskJgYCCOHz+u1X6bN2+GRCJB7969DRsgUQloU1UWEeqLfRfS0GbRAQxa8ycmbT6DQWv+RJtFB1hRRkSkoxItunr8+HG8//776NOnDy5fvox+/frpKy61tmzZgvDwcERERODUqVNo0qQJunbtirt37xa63/Xr1zF16lS0bdvWoPER6UNRC7cC4KKtRER6onPV2OXLl7Fx40b8+OOPSE5ORseOHTFkyBC88cYbsLGxMVScAIDAwEC8+uqr+PLLLwEUlPJ7enpiwoQJmDFjhtp9ZDIZ2rVrh7fffhuHDx/Go0ePsGPHDq3PyaoxKivqqsoAqNxo8UUsryciKmCwqjFvb2+8+uqrGDduHAYOHAhXV9cSBaqt3NxcnDx5EjNnzlS0mZmZISQkBAkJCRr3mzt3LlxcXDBy5EgcPny4yPPk5OQgJydH8TgzM7NkgRMVk7qqsoSr6Vov2sryeiKioumcCCUlJaF+/fqGiKVQ9+/fh0wmU0m8XF1dcenSJbX7HDlyBN9++y3OnDmj9XkWLFiAyMjIkoRKZDAsryci0i+d5wiVRRJUHI8fP8bQoUOxZs0aODk5ab3fzJkzkZGRofi5efOmAaMk0g3L64mI9EvnHqGy4uTkBHNzc9y5c0ep/c6dO3Bzc1PZ/urVq7h+/TpCQ0MVbfn5+QCASpUqISkpCXXr1lXZTyqVQiqV6jl6Iv3QdtFW+XwidfOMOHeIiOg/RpMIWVpaIiAgALGxsYoS+Pz8fMTGxmL8+PEq23t7e+P8+fNKbR999BEeP36M5cuXw9PTszTCJtIreXn9mA2nIAGUkqEXy+vNzSQa1zPjCvZERP8xmkQIAMLDwzF8+HA0b94cLVq0QFRUFLKyshT3Lxo2bBiqV6+OBQsWwMrKCn5+fkr7Ozg4AIBKO5ExkZfXv5zkuL2Q5MQkpmLMhlMqvUbyEvtVbzVjMkREhGIkQnPnzsXUqVNRuXJlpfanT5/i888/x8cff6y34F42YMAA3Lt3Dx9//DHS0tLg7++PmJgYxQTqlJQUmJmV6NZIREahm587Ovu6qR32kuULRO66oHboTKCg5yhy1wV09nXjMBkRmTyd7yNkbm6O1NRUuLi4KLWnp6fDxcUFMplMrwGWNd5HiIxNwtV0rmBPRCZP2+9vnbtPhBCQSFT/ijx79iwcHR11PRwR6RlL7ImItKf10FjVqlUhkUggkUjQoEEDpWRIJpPhyZMneO+99wwSJBFpjyX2RETa0zoRioqKghACb7/9NiIjI2Fvb694ztLSEl5eXmjVqpVBgiQi7elSYs/yeiIydTrPEfrjjz8QFBQECwsLQ8VUrnCOEBkjedUYoL7EXr54K8vriaii0vb7W+dECCi4f8+VK1dw9+5dxU0K5dq1a6d7tOUYEyEyVoXdRwiA2vL6FxMlJkNEZMwMtujqn3/+icGDB+PGjRt4OYeSSCQVrmqMyFhpKrEHClawZ3k9EVExEqH33nsPzZs3x549e+Du7q62goyIygeuYE9EVDidE6F//vkHP//8M+rVq2eIeIjIwFheT0T0H53vIxQYGIgrV64YIhYiKgUsryci+o/OPUITJkzAlClTkJaWhkaNGqlUjzVu3FhvwRGR/nEFeyKi/+hcNaZuLS+JRKK443RFmyzNqjGqiLQpr5cv3soSeyIyRgYrn79x40ahz9eqVUuXw5V7TISooioqydG0gj1L7InIGBj0PkKmhIkQVWSahr1k+QJtFh3QWF0mHz47Mr0jh8mIqFzS632Edu7ciddeew0WFhbYuXNnodv27NlTt0iJqMyoK68HgOPJD1hiT0QmQatEqHfv3khLS4OLiwt69+6tcbuKOEeIyBSxxJ6ITIVWidCLy2i8vKQGEVU8LLEnIlOhc/k8EVV8XMGeiExFsRKhrKws/PHHH0hJSUFubq7ScxMnTtRLYERUdszNJIgI9cWYDacggfoS+4hQX+y7kMbyeiIyajpXjZ0+fRrdu3dHdnY2srKy4OjoiPv376Ny5cpwcXHBtWvXDBVrmWDVGJkyrmBPRMbKYOXz7du3R4MGDbB69WrY29vj7NmzsLCwwFtvvYVJkybhjTfeKHHw5QkTITJ16oa+ALC8nojKNb2Wz7/ozJkz+Oqrr2BmZgZzc3Pk5OSgTp06+OyzzzB8+PAKlwgRmTquYE9EFZnOi65aWFgoltlwcXFBSkoKAMDe3h43b97Ub3REVC6xvJ6IKgqde4SaNm2KEydOoH79+ggODsbHH3+M+/fv44cffoCfn58hYiSickaX8npWlRFReaZzIjR//nw8fvwYAPDpp59i2LBhGDNmDOrXr4+1a9fqPUAiKn+0La9/mJWrMpeIVWVEVJ7oNFlaCIGbN2/CxcUFVlamcSM1TpYmUq+oFexHt6uNrw8ls6qMiMqEtt/fOs0REkKgXr16nAtEROjm545VbzWDm73yH0Vu9laIHtwUO8+mqu0tkrdF7roAWT7XfCaisqXT0JiZmRnq16+P9PR01K9f31AxEZGR6Obnjs6+bipzgLhoKxEZC52rxhYuXIhp06YhMTHREPEQkZGRl9f38q+OVnWrwdxMwqoyIjIaOk+WHjZsGLKzs9GkSRNYWlrC2tpa6fkHDx7oLTgiMk5ctJWIjIXOidCyZcsgkbD0lYg002XRVkD93atZYk9EpUHnRGjEiBEGCIOIKhJtF201N5MUup4Zq8qIyNB0niNkbm6Ou3fvqrSnp6fD3NxcL0ERkfErrKpMXjovL8F/eWJ1WsYzjNlwCjGJqaUZMhGZIJ17hDTddignJweWlpYlDoiIKg5NVWXmZhLI8gUid13QWGIvQUGJfWdfNw6TEZHBaJ0IrVixAgAgkUjwzTffwMbGRvGcTCbDoUOH4O3trf8IicioqVu0FQBL7ImoXNA6EVq2bBmAgh6h1atXKw2DWVpawsvLC6tXr9Z/hERUIbHEnojKA60ToeTkZABAhw4dsG3bNlStWtVgQRFRxccSeyIqD3SeI3Tw4EGlxzKZDOfPn0etWrWYHBGR1nQpsWd5PREZis6J0Pvvv49GjRph5MiRkMlkaNeuHRISElC5cmXs3r0b7du3N0CYRFTRaFtiv+9CGsvrichgdC6f37p1K5o0aQIA2LVrF65fv45Lly5h8uTJmDVrlt4DfFl0dDS8vLxgZWWFwMBAHD9+XOO2a9asQdu2bVG1alVUrVoVISEhhW5PRKWrqBJ7ACyvJyKDkghN9fAaWFlZ4cqVK6hRowZGjx6NypUrIyoqCsnJyWjSpAkyMzMNFSu2bNmCYcOGYfXq1QgMDERUVBS2bt2KpKQkuLi4qGw/ZMgQtG7dGkFBQbCyssKiRYuwfft2/P3336hevbpW58zMzIS9vT0yMjJgZ2en75dERFB/Z2kAaLPogMbKMvnQ2ZHpHTlMRkQqtP3+1rlHyNXVFRcuXIBMJkNMTAw6d+4MAMjOzjb4DRWXLl2KUaNGISwsDL6+vli9ejUqV66MtWvXqt1+48aNGDt2LPz9/eHt7Y1vvvkG+fn5iI2NNWicRKQbdQu36lJeT0RUXDrPEQoLC0P//v3h7u4OiUSCkJAQAMCxY8cMeh+h3NxcnDx5EjNnzlS0mZmZISQkBAkJCVodIzs7G3l5eXB0dNS4TU5ODnJychSPDdnDRUSasbyeiEqDzonQnDlz4Ofnh5s3b6Jfv36QSqUACpbemDFjht4DlLt//z5kMhlcXV2V2l1dXXHp0iWtjjF9+nR4eHgokjd1FixYgMjIyBLFSkQlx/J6IioNOidCAPDmm2+qtA0fPrzEwRjSwoULsXnzZsTFxcHKSvM/nDNnzkR4eLjicWZmJjw9PUsjRCJ6Acvriag0FCsRio2NRWxsLO7evYv8/Hyl5zTN1ykpJycnmJub486dO0rtd+7cgZubW6H7Ll68GAsXLsT+/fvRuHHjQreVSqWKXi4iKjssryei0qDzZOnIyEh06dIFsbGxuH//Ph4+fKj0YyiWlpYICAhQmugsn/jcqlUrjft99tlnmDdvHmJiYtC8eXODxUdE+sfyeiIyNJ17hFavXo3169dj6NChhoinUOHh4Rg+fDiaN2+OFi1aICoqCllZWQgLCwMADBs2DNWrV8eCBQsAAIsWLcLHH3+MTZs2wcvLC2lpaQAAGxsbpUVjiaj80rSCPVBQXs/V64moJHROhHJzcxEUFGSIWIo0YMAA3Lt3Dx9//DHS0tLg7++PmJgYxQTqlJQUmJn918m1atUq5ObmqsxpioiIwJw5c0ozdCIqAXUr2CdcTefq9URUYjrfUHH69OmwsbHB7NmzDRVTucIbKhKVT7+euYVJm88Uud3ygf7o5a/dDVSJqOLQ9vtb5x6hZ8+e4euvv1ZMPLawsFB6funSpbpHS0SkI5bXE5E+6JwInTt3Dv7+/gCAxMREpeckEo7DE1Hp0KW8HlC/jAfnDhGRzonQwYMHDREHEZFOtC2vNzeTICYxlSX2RKSWzuXzL/r333/x77//6isWIiKdFFVe383PHTGJqSyxJyKNdO4Rys/PxyeffIIlS5bgyZMnAABbW1tMmTIFs2bNUqraIiIyNE3l9eZmEsjyBSJ3XWCJPRFppHMiNGvWLHz77bdYuHAhWrduDQA4cuQI5syZg2fPnuHTTz/Ve5BERIVRV14PQKcV7FliT2SadE6EvvvuO3zzzTfo2bOnoq1x48aoXr06xo4dy0SIiMoNrmBPREXReRzrwYMH8Pb2Vmn39vbGgwcP9BIUlR9eXl6IiorSatvs7Gz07dsXdnZ2kEgkePTokU77azJnzhxFpSKRLlhiT0RF0TkRatKkCb788kuV9i+//BJNmjTRS1Cku3v37mHMmDGoWbMmpFIp3Nzc0LVrV8THx2u1//r16+Hg4KDSfuLECYwePVqrY3z33Xc4fPgwjh49itTUVNjb2+u0P5G+yUvsNc3+kaCgeky+gn3C1XT8euYWEq6mQ5av071michI6Tw09tlnn6FHjx7Yv3+/YrHThIQE3Lx5E3v37tV7gKSdvn37Ijc3F9999x3q1KmDO3fuIDY2Funp6SU6rrOzs9bbXr16FT4+PvDz8yvW/kT6xhXsiagoOvcIBQcHIykpCX369MGjR4/w6NEjvPHGG0hKSkLbtm0NESMV4dGjRzh8+DAWLVqEDh06oFatWmjRogVmzpypmMu1dOlSNGrUCFWqVIGnpyfGjh2rqPqLi4tDWFgYMjIyIJFIIJFIFGuxvTi0JYTAnDlzFL1OHh4emDhxIgCgffv2WLJkCQ4dOgSJRIL27dur7C+P9Z133oGzszPs7OzQsWNHnD17Vun1LFy4EK6urrC1tcXIkSPx7Bnnb1DxcQV7IiqMzj1CAFC9enVOii5HbGxsYGNjgx07dqBly5aQSqUq25iZmWHFihWoXbs2rl27hrFjx+KDDz7AypUrERQUhKioKHz88cdISkpSHPNlv/zyC5YtW4bNmzfjlVdeQVpamiKJ2bZtG2bMmIHExERs27YNlpaWamPt168frK2t8dtvv8He3h5fffUVOnXqhMuXL8PR0RE//fQT5syZg+joaLRp0wY//PADVqxYgTp16ujxipGp4Qr2RKSJzonQunXrYGNjg379+im1b926FdnZ2Rg+fLjegiPtVKpUCevXr8eoUaOwevVqNGvWDMHBwRg4cCAaN24MAHj//fcV23t5eeGTTz7Be++9h5UrV8LS0hL29vaQSCRwc3PTeJ6UlBS4ubkhJCQEFhYWqFmzJlq0aAEAcHR0ROXKlWFpaanxGEeOHMHx48dx9+5dRbK2ePFi7NixAz///DNGjx6NqKgojBw5EiNHjgQAfPLJJ9i/fz97hajEuII9Eamj89DYggUL4OTkpNLu4uKC+fPn6yUo0l3fvn1x+/Zt7Ny5E926dUNcXByaNWuG9evXAwD279+PTp06oXr16rC1tcXQoUORnp6O7Oxsrc/Rr18/PH36FHXq1MGoUaOwfft2PH/+XOv9z549iydPnqBatWqKXiwbGxskJyfj6tWrAICLFy8iMDBQaT/5XDQifWN5PRHpnAilpKSgdu3aKu21atVCSkqKXoKi4rGyskLnzp0xe/ZsHD16FCNGjEBERASuX7+O119/HY0bN8Yvv/yCkydPIjo6GgCQm5ur9fE9PT2RlJSElStXwtraGmPHjkW7du2Ql5en1f5PnjyBu7s7zpw5o/STlJSEadOmFes1E5UEy+uJSOdEyMXFBefOnVNpP3v2LKpVY9dxeeLr64usrCycPHkS+fn5WLJkCVq2bIkGDRrg9u3bSttaWlpCJpMVeUxra2uEhoZixYoViIuLQ0JCAs6fP69VPM2aNUNaWhoqVaqEevXqKf3Iexl9fHxw7Ngxpf3+/PNPLV8xkW5YXk9EOs8RGjRoECZOnAhbW1u0a9cOAPDHH39g0qRJGDhwoN4DpKKlp6ejX79+ePvtt9G4cWPY2trir7/+wmeffYZevXqhXr16yMvLwxdffIHQ0FDEx8dj9erVSsfw8vLCkydPEBsbiyZNmqBy5cqoXLmy0jbr16+HTCZDYGAgKleujA0bNsDa2hq1atXSKs6QkBC0atUKvXv3xmeffaZIyPbs2YM+ffqgefPmmDRpEkaMGIHmzZujdevW2LhxI/7++29OliaDYHk9EencIzRv3jwEBgaiU6dOsLa2hrW1Nbp06YKOHTtyjlAZsbGxQWBgIJYtW4Z27drBz88Ps2fPxqhRoxQ3uly6dCkWLVoEPz8/bNy4EQsWLFA6RlBQEN577z0MGDAAzs7O+Oyzz1TO4+DggDVr1qB169Zo3Lgx9u/fj127dmndEyiRSLB37160a9cOYWFhaNCgAQYOHIgbN27A1dUVADBgwADMnj0bH3zwAQICAnDjxg2MGTOm5BeJSAOW1xOZNokQolj9u//88w/OnDkDa2trNGrUSOteAWOTmZkJe3t7ZGRkwM7OrqzDISIDkeULjeX1mirLJChImI5M78jyeqJyRtvv72LdRwgA6tevj/r16xd3dyKicoXl9USmSeehMSIiU8HyeqKKr9g9QkREFZ2u5fXqhtc4ZEZUvjERIiLSQF5en5bxTO0yHPI5Qi1qOyImMZWVZURGiENjREQayMvrAajca+jl8npWlhEZJ616hNTdQFET+dpWREQVgby8/uXeHrf/7+3p7OvGhVuJjJhWiZC/vz8kEgmEEJBICv9F1ubuxERExkTT6vXmZhJWlhEZOa0SoeTkZMX/nz59GlOnTsW0adMUi2EmJCRgyZIlam/CR0RUEagrrwdYWUZk7LRKhF68WWK/fv2wYsUKdO/eXdHWuHFjeHp6Yvbs2ejdu7fegyQiKq+4cCuRcdO5auz8+fNqV5+vXbs2Lly4oJegiIiMhS6VZSyvJyp/dF5io1mzZvDz88M333wDS0tLAEBubi7eeecdJCYm4tSpUwYJtKxwiQ0iKkpMYirGbCj4t0/dwq3yNctYXk9UerT9/tY5ETp+/DhCQ0MhhFBUiJ07dw4SiQS7du1CixYtShZ5OcNEiIi0Udh9hICChVtf/sf2xUSJyRCRfhksEQKArKwsbNy4EZcuXQIA+Pj4YPDgwahSpUrxIy6nmAgRkba4cCtR+WHQRVerVKmC0aNHFzs4IqKKiAu3EhkfrRKhnTt3an3Anj17FjsYIqKKhuX1ROWbVomQtiXxEomEN1QkInoBy+uJyjetEqH8/HxDx0FEVCHpUl4PcAV7otLG1eeJiAxIvnDrmA2nIIH68vqIUF+Ym0m4gj1RGSjW6vN//PEHQkNDUa9ePdSrVw89e/bE4cOH9R0bEVGFIF+41c1eefjLzd5KUTovvxcRV7AnKl06J0IbNmxASEgIKleujIkTJ2LixImwtrZGp06dsGnTJkPEqCQ6OhpeXl6wsrJCYGAgjh8/Xuj2W7duhbe3N6ysrNCoUSPs3bvX4DESEb2sm587jkzviB9HtcTygf74cVRLHJneEd383CHLF4jcdUHjCvZAwc0YZfk63+2EiIqg832EfHx8MHr0aEyePFmpfenSpVizZg0uXryo1wBftGXLFgwbNgyrV69GYGAgoqKisHXrViQlJcHFxUVl+6NHj6Jdu3ZYsGABXn/9dWzatAmLFi3CqVOn4Ofnp9U5eR8hIjK0hKvpGLTmzyK3+3FUS5bYE2nJYDdUlEql+Pvvv1GvXj2l9itXrsDPzw/PnhmuBDQwMBCvvvoqvvzySwAFk7g9PT0xYcIEzJgxQ2X7AQMGICsrC7t371a0tWzZEv7+/li9erVW5zRoIpSVpd/jEZFR2n3uNqZtPVfkdp/3a4zXG3uUQkREpchAN2M22A0VPT09ERsbq5II7d+/H56enrpHqqXc3FycPHkSM2fOVLSZmZkhJCQECQkJavdJSEhAeHi4UlvXrl2xY8cOjefJyclBTk6O4nFmZmbJAi+MjY3hjk1ERuP1//8p0jIDB0JUFnRf4EKvdE6EpkyZgokTJ+LMmTMICgoCAMTHx2P9+vVYvny53gOUu3//PmQyGVxdXZXaXV1dFUt9vCwtLU3t9mlpaRrPs2DBAkRGRpY8YCIiIir3dE6ExowZAzc3NyxZsgQ//fQTgIJ5Q1u2bEGvXr30HmBpmzlzplIvUmZmpuF6up48Mcxxicjo7Ps7DZM2nwGgvsR++UB/AMD83y4iLeO/Xms3eyk+fM0HnV9xK5U4iSqaYt1HqE+fPujTp4++YymUk5MTzM3NcefOHaX2O3fuwM1N/T8Abm5uOm0PFMyBkkqlJQ9YGxVwkVoiKp7OLepiaeXKGu8jJIN8BXsJYPlfGf71p8DobZewqnJl3muIqBiKfUPF3Nxc3L17V+Wu0zVr1ixxUOpYWloiICAAsbGxiiU/8vPzERsbi/Hjx6vdp1WrVoiNjcX777+vaNu3bx9atWplkBiJiEqim587Ovu6aVzBXlN5vQQF5fWdfd14F2oiHemcCP3zzz94++23cfToUaV2IYTB1xoLDw/H8OHD0bx5c7Ro0QJRUVHIyspCWFgYAGDYsGGoXr06FixYAACYNGkSgoODsWTJEvTo0QObN2/GX3/9ha+//tpgMRIRlQRXsCcqXTonQiNGjEClSpWwe/duuLu7QyIpvb8+BgwYgHv37uHjjz9GWloa/P39ERMTo5gQnZKSAjOz/+4RGRQUhE2bNuGjjz7Chx9+iPr162PHjh1a30OIiKg84Ar2RIaj832EqlSpgpMnT8Lb29tQMZUrvKEiEZU13nCRSHcGu4+Qr68v7t+/X6LgiIhIe7qsYM/V64l0o1WP0Is3Ffzrr7/w0UcfYf78+WjUqBEsLCyUtq1ovSbsESKi8kC+KCugvrx+1VvNAICr1xP9P70usWFmZqY0F0g+MfpFpTFZuiwwESKi8iImMVVjogPIy+uVvZgoMRkiU6LXobGDBw/qLTAiIioeltcT6Z9WiVBwcLCh4yAiIi2wvJ5Iv8yK3kRZTEwMjhw5ongcHR0Nf39/DB48GA8fPtRrcEREVDSW1xMVn86J0LRp0xSTp8+fP4/w8HB0794dycnJKiu9ExGR4bnYWhW9kQ7bEZkSncvnk5OT4etbMDHvl19+QWhoKObPn49Tp06he/fueg+QiIgKp0t5PQCW2BO9QOdEyNLSEtnZ2QCA/fv3Y9iwYQAAR0dHpTJ7IiIqHeZmEkSE+mLMhlOQQH15fUSoL8zNJIVWnrGqjEyRzkNjbdq0QXh4OObNm4fjx4+jR48eAIDLly+jRo0aeg+QiIiK1s3PHaveagY3e+XhLzd7K0XpvPxeRC9PrE7LeIYxG04hJjG1NEMmKhd07hH68ssvMXbsWPz8889YtWoVqlevDgD47bff0K1bN70HSERE2tFUXm9uJoEsXyBy1wWW2BO9ROe1xkwNb6hIRBUB1ysjU6Pt97fOQ2MAcPXqVXz00UcYNGgQ7t69C6CgR+jvv/8uXrRERGRQLLEnUk/nROiPP/5Ao0aNcOzYMWzbtg1PnjwBAJw9exYRERF6D5CIiEqOJfZE6umcCM2YMQOffPIJ9u3bB0tLS0V7x44d8eefRXe7EhFR6ZOX2Gua/SNBQfWYfAX7hKvp+PXMLSRcTYcsnzMoqOLSebL0+fPnsWnTJpV2FxcX3L9/Xy9BERGRfmlbYr/vQhrL68mk6Nwj5ODggNRU1RLL06dPKyrIiIio/CmqxB4Ay+vJ5OjcIzRw4EBMnz4dW7duhUQiQX5+PuLj4zF16lTFzRWJiKh84gr2RMp0ToTmz5+PcePGwdPTEzKZDL6+vpDJZBg8eDA++ugjQ8RIRER6xBXsif6j09CYEAJpaWlYsWIFrl27ht27d2PDhg24dOkSfvjhB5ibmxsqTiqBOXPmwN/fX+vtr1+/DolEgjNnzhgspsKMGDECvXv3LpNzE5kqlteTqdKpR0gIgXr16uHvv/9G/fr14enpaai4SEuhoaHIy8tDTEyMynOHDx9Gu3btcPbsWUyYMMGgcYwYMQKPHj3Cjh07DHoeIjIMXcrruWgrVSQ6JUJmZmaoX78+0tPTUb9+fUPFRDoYOXIk+vbti3///Vdlrbd169ahefPmaNy4cRlFR0TGQtsV7B9m5aLNogOsKqMKQ+eqsYULF2LatGlITEw0RDyko9dffx3Ozs5Yv369UvuTJ0+wdetWjBw5UmVoLD8/H3PnzkWNGjUglUrh7++vtkdJTiaTYeTIkahduzasra3RsGFDLF++XPH8nDlz8N133+HXX3+FRCKBRCJBXFwcAODmzZvo378/HBwc4OjoiF69euH69etKxw4PD4eDgwOqVauGDz74AFz1haj0ycvrAajca0j+uGcTd4zbxKoyqlh0ToSGDRuG48ePo0mTJrC2toajo6PSD5WuSpUqYdiwYVi/fr1SArF161bIZDIMGjRIZZ/ly5djyZIlWLx4Mc6dO4euXbuiZ8+e+Oeff9SeIz8/HzVq1MDWrVtx4cIFfPzxx/jwww/x008/AQCmTp2K/v37o1u3bkhNTUVqaiqCgoKQl5eHrl27wtbWFocPH0Z8fDxsbGzQrVs35ObmAgCWLFmC9evXY+3atThy5AgePHiA7du3G+BKEVFRCiuvjx7cFDvPpmqsKgMKqsp480UyNjpXjUVFRRkgDCqJt99+G59//jn++OMPtG/fHkDBsFjfvn1hb2+vsv3ixYsxffp0DBw4EACwaNEiHDx4EFFRUYiOjlbZ3sLCApGRkYrHtWvXRkJCAn766Sf0798fNjY2sLa2Rk5ODtzc3BTbbdiwAfn5+fjmm28gkUgUcTk4OCAuLg5dunRBVFQUZs6ciTfeeAMAsHr1avz+++96uzZEpBtN5fXHkx+wqowqJJ0ToeHDhxsiDioBb29vBAUFYe3atWjfvj2uXLmCw4cPY+7cuSrbZmZm4vbt22jdurVSe+vWrXH27FmN54iOjsbatWuRkpKCp0+fIjc3t8hKtLNnz+LKlSuwtbVVan/27BmuXr2KjIwMpKamIjAwUPFcpUqV0Lx5cw6PEZUhdeX1rCqjikrnRAgoGCq5cuUK7t69i/z8fKXn2rVrp5fASDcjR47EhAkTEB0djXXr1qFu3boIDg7Wy7E3b96MqVOnYsmSJWjVqhVsbW3x+eef49ixY4Xu9+TJEwQEBGDjxo0qzzk7O+slNiIqHVy0lSoqnROhP//8E4MHD8aNGzdU/mqXSCSQyWR6C460179/f0yaNAmbNm3C999/jzFjxiiGo15kZ2cHDw8PxMfHKyVK8fHxaNGihdpjx8fHIygoCGPHjlW0Xb16VWkbS0tLlfe+WbNm2LJlC1xcXGBnZ6f22O7u7jh27JgigX7+/DlOnjyJZs2aaffCiahUaFtVJr9LNUvsyVjoPFn6vffeQ/PmzZGYmIgHDx7g4cOHip8HDx4YIkbSgo2NDQYMGICZM2ciNTUVI0aM0LjttGnTsGjRImzZsgVJSUmYMWMGzpw5g0mTJqndvn79+vjrr7/w+++/4/Lly5g9ezZOnDihtI2XlxfOnTuHpKQk3L9/H3l5eRgyZAicnJzQq1cvHD58GMnJyYiLi8PEiRPx77//AgAmTZqEhQsXYseOHbh06RLGjh2LR48e6euyEJGeaFNVFhHqC3MzCWISU9Fm0QEMWvMnJm0+g0Fr/kSbRQdYVUblks6J0D///IP58+fDx8cHDg4OsLe3V/qhsjNy5Eg8fPgQXbt2hYeHh8btJk6ciPDwcEyZMgWNGjVCTEwMdu7cqfHeUO+++y7eeOMNDBgwAIGBgUhPT1fqHQKAUaNGoWHDhmjevDmcnZ0RHx+PypUr49ChQ6hZsybeeOMN+Pj4YOTIkXj27Jmih2jKlCkYOnQohg8frhh269Onj/4uChHpTVGLtnbzc0dMYioXbiWjIhE6zkrt2LEjPvjgA3Tr1s1QMZUrmZmZsLe3R0ZGhsbhHSIiU6Jp2EuWL1Rutvgi+fDZkekdOUxGBqft97dWc4TOnTun+P8JEyZgypQpSEtLQ6NGjWBhYaG0Le9iTERUsamrKgPAEnsySlolQv7+/pBIJEqTo99++23F/8uf42RpIiLTxRJ7MkZaJULJycmGjoOIiIwcS+zJGGmVCNWqVUvx/4cOHUJQUBAqVVLe9fnz5zh69KjStkREZDp0KbFneT2VFzpPljY3N0dqaipcXFyU2tPT0+Hi4lLhhsY4WZqISHvyqjEASsmQPMVZ9VbBPcIid13gCvZkUNp+f+tcPi+fC/Sy9PR0VKlSRdfDERFRBVJUiT0AltdTuaL1naXli2JKJBKMGDECUqlU8ZxMJsO5c+cQFBSk/wj/34MHDzBhwgTs2rULZmZm6Nu3L5YvXw4bGxuN20dEROB///sfUlJS4OzsjN69e2PevHm83xERkQFpWrgVANosOqBxBXsJCnqKOvu6cZiMSo3WiZA8eRBCwNbWFtbW1ornLC0t0bJlS4waNUr/Ef6/IUOGIDU1Ffv27UNeXh7CwsIwevRobNq0Se32t2/fxu3bt7F48WL4+vrixo0beO+993D79m38/PPPBouTiIjUl9gnXE1neT2VO1onQuvWrQNQsJTC1KlTS3UY7OLFi4iJicGJEyfQvHlzAMAXX3yB7t27Y/HixWrvouzn54dffvlF8bhu3br49NNP8dZbb+H58+cqk72JiMiwWF5P5ZHOc4QiIiJKfS5QQkICHBwcFEkQAISEhMDMzKzIFdBfJJ8wxSSIiKj0sbyeyiOjyAjS0tJUqtQqVaoER0dHpKWlaXWM+/fvY968eRg9enSh2+Xk5CAnJ0fxODMzU/eAiYhIBcvrqTzSuUdIn2bMmAGJRFLoz6VLl0p8nszMTPTo0QO+vr6YM2dOodsuWLBAaRFZT0/PEp+fiIi0X8F+34U0rl5PpUbn+wjp071795Cenl7oNnXq1MGGDRswZcoUPHz4UNH+/PlzWFlZYevWrYWuVv748WN07doVlStXxu7du2FlVXiXq7oeIU9PT95HiIhIT2ISUzXeRwgoKK9/+YvpxfsQ8V5DpA29LrpqKM7OznB2di5yu1atWuHRo0c4efIkAgICAAAHDhxAfn4+AgMDNe6XmZmJrl27QiqVYufOnUUmQQAglUqVbg1ARET6xfJ6Kk+0ToRWrFih1XYTJ04sdjCa+Pj4oFu3bhg1ahRWr16NvLw8jB8/HgMHDlRUjN26dQudOnXC999/jxYtWiAzMxNdunRBdnY2NmzYgMzMTMV8H2dnZ5ibm+s9TiIi0g7L66m80DoRWrZsmdLjmzdvwt3dXakCSyKRGCQRAoCNGzdi/Pjx6NSpk+KGii8mZ3l5eUhKSkJ2djYA4NSpU4qKsnr16ikdKzk5GV5eXgaJk4iIiofl9VQWtE6EXl6B3tbWFn/88Qfq1Kmj96DUcXR01HjzRKDg/kYvTndq3749ynD6ExER6Yjl9VQWjKJ8noiIKj5dyusBsMSe9IKJEBERlQvy8voxG05BAvWr10eE+sLcTFJo5RmrykgXZXofISIiohcVtXp9Nz93xCSmcgV70hute4RevsOyRCLBkydPVNp5rx0iIioJTeX15mYSyPIFInddYIk96Y3WiZCDgwMkkv8+VEIING3aVOmxRCKBTCbTb4RERGRy1JXXA8Dx5AcssSe90joROnjwoCHjICIiKhJL7EnftE6EgoODDRkHERFRkVhiT/pWosnSPXr0QGoqJ6UREVHpkJfYa5r9I0FB9Zh8BfuEq+n49cwtJFxNhyyf95YjVSUqnz906BCePn2qr1iIiIgKpW2J/b4LaSyvJ62wfJ6IiIxKUSX2AFheT1orUY9QrVq1YGFhoa9YiIiItMIV7ElfStQjlJiYCE9PTwDAv//+i9GjR+slKCIioqLIS+x7+VdHq7rVYG4m0am8ngjQ49BYeno6vv32W30djoiISGcsryddcY4QERFVGCyvJ11x0VUiIqowuII96YqJEBERVRhcwZ50pXUi9MYbbxT6/KNHj0oaCxERUYnJy+tfTnLcXkhy5CvYv9xrJC+xl690TxWf1omQvb19kc8PGzasxAERERGVFFewJ21pnQitW7fOkHEQERHpFVewJ22waoyIiEwKS+zpRUyEiIjIpLDEnl7EqjEiIjIpupTYs7y+4mMiREREJoUr2NOLODRGREQmhyvYkxx7hIiIyCRxBXsCmAgREZEJU1din3A1neX1JoRDY0RERC9geb1pYSJERET0ApbXmxYOjREREb2A5fWmhYkQERHRC1heb1o4NEZERPQSltebDvYIERERqcHyetPARIiIiEgDltdXfBwaIyIi0gHL6ysW9ggRERHpQNfyelaWlW9MhIiIiHSgS3l9TGIqK8vKOQ6NERER6UBeXg/8V04v93J5PSvLyj8mQkRERDoqqry+s68bIndd0FhZBhRUlsny1W1BpcloEqEHDx5gyJAhsLOzg4ODA0aOHIknT55ota8QAq+99hokEgl27Nhh2ECJiMgkdPNzx5HpHfHjqJZYPtAfP45qiSPTO6KbnzuOJz/QurKMypbRzBEaMmQIUlNTsW/fPuTl5SEsLAyjR4/Gpk2bitw3KioKEgknphERkX6pK68HWFlmTIwiEbp48SJiYmJw4sQJNG/eHADwxRdfoHv37li8eDE8PDw07nvmzBksWbIEf/31F9zdOTGNiIgMjwu3Gg+jGBpLSEiAg4ODIgkCgJCQEJiZmeHYsWMa98vOzsbgwYMRHR0NNze30giViIhIUVmmaSxCgoLqMfnCrQlX0/HrmVtIuJrOeUOlzCh6hNLS0uDi4qLUVqlSJTg6OiItLU3jfpMnT0ZQUBB69eql9blycnKQk5OjeJyZmal7wEREZNK4cKvxKNMeoRkzZkAikRT6c+nSpWIde+fOnThw4ACioqJ02m/BggWwt7dX/Hh6ehbr/EREZNq4cKtxkAghyqwP7t69e0hPTy90mzp16mDDhg2YMmUKHj58qGh//vw5rKyssHXrVvTp00dlv/fffx8rVqyAmdl/uZ5MJoOZmRnatm2LuLg4tedT1yPk6emJjIwM2NnZ6fgKiYjI1Km7szRQsHCrpsoy+U0Zj0zvyLtQF1NmZibs7e2L/P4u06ExZ2dnODs7F7ldq1at8OjRI5w8eRIBAQEAgAMHDiA/Px+BgYFq95kxYwbeeecdpbZGjRph2bJlCA0N1XguqVQKqVSqw6sgIiLSjAu3lm9GMUfIx8cH3bp1w6hRo7B69Wrk5eVh/PjxGDhwoKJi7NatW+jUqRO+//57tGjRAm5ubmonSNesWRO1a9cu7ZdARESkwPL68sMoqsYAYOPGjfD29kanTp3QvXt3tGnTBl9//bXi+by8PCQlJSE7O7sMoyQiIioay+vLD6PoEQIAR0fHQm+e6OXlhaKmO5XhdCgiIiIFXRZu5er1hmU0iRAREVFFwfL68sNohsaIiIgqEpbXlw/sESIiIioj3fzc0dnXTWN5vabV6yUoWL2+s68bh8lKiIkQERFRGWJ5fdni0BgREVE5w/L60sNEiIiIqJxheX3p4dAYERFROaNLeT2gfhkPzh3SDhMhIiKickbb8npzMwliElNZYl8CHBojIiIqh4oqr+/m546YxFSW2JcQe4SIiIjKKU3l9eZmEsjyBSJ3XWCJfQkxESIiIirH1JXXA8Dx5AcssdcDDo0REREZIZbY6wcTISIiIiPEEnv94NAYERGREeIK9vrBRIiIiMgIcQV7/eDQGBERkZHiCvYlxx4hIiIiI8YV7EuGiRAREZGR4wr2xcehMSIiogqI5fXaYSJERERUAbG8XjscGiMiIqqAWF6vHSZCREREFRDL67XDoTEiIqIKiuX1RWOPEBERUQXG8vrCMREiIiKq4FherxmHxoiIiEwQy+sLMBEiIiIyQSyvL8ChMSIiIhOkS3k9gApbYs9EiIiIyARpW15vbiZBTGJqhS2x59AYERGRiSqqvL6bnztiElMrdIk9e4SIiIhMmKbyenMzCWT5ApG7LlToEnsmQkRERCZOXXk9ABxPflDhS+w5NEZERERqmUKJPRMhIiIiUssUSuyZCBEREZFa8hJ7TbN/JCioHpOX2BsjJkJERESklrzEHoBKMvRyib2xYiJEREREGmlTYm/MWDVGREREhSqsxN7YGU2P0IMHDzBkyBDY2dnBwcEBI0eOxJMnT4rcLyEhAR07dkSVKlVgZ2eHdu3a4enTp6UQMRERUcUhL7Hv5V8drepWqxBJEGBEidCQIUPw999/Y9++fdi9ezcOHTqE0aNHF7pPQkICunXrhi5duuD48eM4ceIExo8fDzMzo3nZREREZEASIYS6G0aWKxcvXoSvry9OnDiB5s2bAwBiYmLQvXt3/Pvvv/Dw8FC7X8uWLdG5c2fMmzev2OfOzMyEvb09MjIyYGdnV+zjEBERUenR9vvbKLpGEhIS4ODgoEiCACAkJARmZmY4duyY2n3u3r2LY8eOwcXFBUFBQXB1dUVwcDCOHDlS6LlycnKQmZmp9ENEREQVk1EkQmlpaXBxcVFqq1SpEhwdHZGWlqZ2n2vXrgEA5syZg1GjRiEmJgbNmjVDp06d8M8//2g814IFC2Bvb6/48fT01N8LISIionKlTBOhGTNmQCKRFPpz6dKlYh07Pz8fAPDuu+8iLCwMTZs2xbJly9CwYUOsXbtW434zZ85ERkaG4ufmzZvFOj8RERGVf2VaPj9lyhSMGDGi0G3q1KkDNzc33L17V6n9+fPnePDgAdzc3NTu5+5ecF8DX19fpXYfHx+kpKRoPJ9UKoVUKtUieiIiIjJ2ZZoIOTs7w9nZucjtWrVqhUePHuHkyZMICAgAABw4cAD5+fkIDAxUu4+Xlxc8PDyQlJSk1H758mW89tprJQ+eiIiIjJ5RzBHy8fFBt27dMGrUKBw/fhzx8fEYP348Bg4cqKgYu3XrFry9vXH8+HEAgEQiwbRp07BixQr8/PPPuHLlCmbPno1Lly5h5MiRZflyiIiIqJwwmjtLb9y4EePHj0enTp1gZmaGvn37YsWKFYrn8/LykJSUhOzsbEXb+++/j2fPnmHy5Ml48OABmjRpgn379qFu3bpl8RKIiIionDGK+wiVpYyMDDg4OODmzZu8jxAREZGRyMzMhKenJx49egR7e3uN2xlNj1BZefz4MQCwjJ6IiMgIPX78uNBEiD1CRcjPz8ft27dha2sLiaR8rasiz3bZW1VyvJb6weuoP7yW+sNrqT/GdC2FEHj8+DE8PDwKXVqLPUJFMDMzQ40aNco6jELZ2dmV+w+kseC11A9eR/3htdQfXkv9MZZrWVhPkJxRVI0RERERGQITISIiIjJZTISMmFQqRUREBO+ErQe8lvrB66g/vJb6w2upPxXxWnKyNBEREZks9ggRERGRyWIiRERERCaLiRARERGZLCZCREREZLKYCJVz0dHR8PLygpWVFQIDA3H8+PFCt9+6dSu8vb1hZWWFRo0aYe/evaUUafmny7Vcs2YN2rZti6pVq6Jq1aoICQkp8tqbCl0/k3KbN2+GRCJB7969DRugEdH1Wj569Ajjxo2Du7s7pFIpGjRowN/x/6frtYyKikLDhg1hbW0NT09PTJ48Gc+ePSulaMunQ4cOITQ0FB4eHpBIJNixY0eR+8TFxaFZs2aQSqWoV68e1q9fb/A49U5QubV582ZhaWkp1q5dK/7++28xatQo4eDgIO7cuaN2+/j4eGFubi4+++wzceHCBfHRRx8JCwsLcf78+VKOvPzR9VoOHjxYREdHi9OnT4uLFy+KESNGCHt7e/Hvv/+WcuTli67XUS45OVlUr15dtG3bVvTq1at0gi3ndL2WOTk5onnz5qJ79+7iyJEjIjk5WcTFxYkzZ86UcuTlj67XcuPGjUIqlYqNGzeK5ORk8fvvvwt3d3cxefLkUo68fNm7d6+YNWuW2LZtmwAgtm/fXuj2165dE5UrVxbh4eHiwoUL4osvvhDm5uYiJiamdALWEyZC5ViLFi3EuHHjFI9lMpnw8PAQCxYsULt9//79RY8ePZTaAgMDxbvvvmvQOI2BrtfyZc+fPxe2trbiu+++M1SIRqE41/H58+ciKChIfPPNN2L48OFMhP6frtdy1apVok6dOiI3N7e0QjQaul7LcePGiY4dOyq1hYeHi9atWxs0TmOiTSL0wQcfiFdeeUWpbcCAAaJr164GjEz/ODRWTuXm5uLkyZMICQlRtJmZmSEkJAQJCQlq90lISFDaHgC6du2qcXtTUZxr+bLs7Gzk5eXB0dHRUGGWe8W9jnPnzoWLiwtGjhxZGmEaheJcy507d6JVq1YYN24cXF1d4efnh/nz50Mmk5VW2OVSca5lUFAQTp48qRg+u3btGvbu3Yvu3buXSswVRUX5zuGiq+XU/fv3IZPJ4OrqqtTu6uqKS5cuqd0nLS1N7fZpaWkGi9MYFOdavmz69Onw8PBQ+aU3JcW5jkeOHMG3336LM2fOlEKExqM41/LatWs4cOAAhgwZgr179+LKlSsYO3Ys8vLyEBERURphl0vFuZaDBw/G/fv30aZNGwgh8Pz5c7z33nv48MMPSyPkCkPTd05mZiaePn0Ka2vrMopMN+wRIirCwoULsXnzZmzfvh1WVlZlHY7RePz4MYYOHYo1a9bAycmprMMxevn5+XBxccHXX3+NgIAADBgwALNmzcLq1avLOjSjExcXh/nz52PlypU4deoUtm3bhj179mDevHllHRqVAfYIlVNOTk4wNzfHnTt3lNrv3LkDNzc3tfu4ubnptL2pKM61lFu8eDEWLlyI/fv3o3HjxoYMs9zT9TpevXoV169fR2hoqKItPz8fAFCpUiUkJSWhbt26hg26nCrOZ9Ld3R0WFhYwNzdXtPn4+CAtLQ25ubmwtLQ0aMzlVXGu5ezZszF06FC88847AIBGjRohKysLo0ePxqxZs2Bmxj4CbWj6zrGzszOa3iCAPULllqWlJQICAhAbG6toy8/PR2xsLFq1aqV2n1atWiltDwD79u3TuL2pKM61BIDPPvsM8+bNQ0xMDJo3b14aoZZrul5Hb29vnD9/HmfOnFH89OzZEx06dMCZM2fg6elZmuGXK8X5TLZu3RpXrlxRJJMAcPnyZbi7u5tsEgQU71pmZ2erJDvyBFNw+U2tVZjvnLKerU2abd68WUilUrF+/Xpx4cIFMXr0aOHg4CDS0tKEEEIMHTpUzJgxQ7F9fHy8qFSpkli8eLG4ePGiiIiIYPn8/9P1Wi5cuFBYWlqKn3/+WaSmpip+Hj9+XFYvoVzQ9Tq+jFVj/9H1WqakpAhbW1sxfvx4kZSUJHbv3i1cXFzEJ598UlYvodzQ9VpGREQIW1tb8eOPP4pr166J//3vf6Ju3bqif//+ZfUSyoXHjx+L06dPi9OnTwsAYunSpeL06dPixo0bQgghZsyYIYYOHarYXl4+P23aNHHx4kURHR3N8nnSvy+++ELUrFlTWFpaihYtWog///xT8VxwcLAYPny40vY//fSTaNCggbC0tBSvvPKK2LNnTylHXH7pci1r1aolAKj8RERElH7g5Yyun8kXMRFSpuu1PHr0qAgMDBRSqVTUqVNHfPrpp+L58+elHHX5pMu1zMvLE3PmzBF169YVVlZWwtPTU4wdO1Y8fPiw9AMvRw4ePKj23z35tRs+fLgIDg5W2cff319YWlqKOnXqiHXr1pV63CUlEYL9gERERGSaOEeIiIiITBYTISIiIjJZTISIiIjIZDERIiIiIpPFRIiIiIhMFhMhIiIiMllMhIiIiMhkMREiohIZMWIEevfuXdZhFMrLywtRUVF6P252djb69u0LOzs7SCQSPHr0SO/nKMz69evh4OBQquckKsyhQ4cQGhoKDw8PSCQS7Nixo9TOvXDhQkgkErz//vs67cdEiMgErV69Gra2tnj+/Lmi7cmTJ7CwsED79u2Vto2Li4NEIsHVq1fVHmv58uVYv369AaMtuRMnTmD06NGKx/r6B/q7777D4cOHcfToUaSmpsLe3r7Ex9REXTI3YMAAXL582WDnJNJVVlYWmjRpgujo6FI974kTJ/DVV18Va3FsJkJEJqhDhw548uQJ/vrrL0Xb4cOH4ebmhmPHjuHZs2eK9oMHD6JmzZoaV4q3t7cvt70Subm5AABnZ2dUrlxZ78e/evUqfHx84OfnBzc3N0gkEo0xGIK1tTVcXFwMdnwiXb322mv45JNP0KdPH7XP5+TkYOrUqahevTqqVKmCwMBAxMXFleicT548wZAhQ7BmzRpUrVpV5/2ZCBGZoIYNG8Ld3V3pH6C4uDj06tULtWvXxp9//qnU3qFDB43HenlorH379pgwYQLef/99VK1aFa6urlizZg2ysrIQFhYGW1tb1KtXD7/99pvSOSQSCfbs2YPGjRvDysoKLVu2RGJiomKbOXPmwN/fX+ncUVFR8PLyUonl008/hYeHBxo2bAhAuTdFvn2fPn0gkUjg5eWF69evw8zMTCkxlB+/Vq1aSiu+v/g6lyxZgkOHDkEikSh60ry8vDBv3jwMGzYMdnZ2ip6o6dOno0GDBqhcuTLq1KmD2bNnIy8vT+mYu3btwquvvgorKys4OTkpvkzat2+PGzduYPLkyZBIJIqES93Q2KpVq1C3bl1YWlqiYcOG+OGHH5Sel0gk+Oabb9CnTx9UrlwZ9evXx86dO1VeH5EhjB8/HgkJCdi8eTPOnTuHfv36oVu3bvjnn3+Kfcxx48ahR48eCAkJKdb+TISITFSHDh1w8OBBxeODBw+iffv2CA4OVrQ/ffoUx44dKzQRUue7776Dk5MTjh8/jgkTJmDMmDHo168fgoKCcOrUKXTp0gVDhw5Fdna20n7Tpk3DkiVLcOLECTg7OyM0NFQlWShKbGwskpKSsG/fPuzevVvl+RMnTgAA1q1bh9TUVJw4cQJeXl4ICQnBunXrlLZdt24dRowYATMz1X8qt23bhlGjRqFVq1ZITU3Ftm3bFM8tXrwYTZo0wenTpzF79mwAgK2tLdavX48LFy5g+fLlWLNmDZYtW6bYZ8+ePejTpw+6d++O06dPIzY2Fi1atFCcq0aNGpg7dy5SU1ORmpqq9rVv374dkyZNwpQpU5CYmIh3330XYWFhSu8zAERGRqJ///44d+4cunfvjiFDhuDBgwfaXF6iYktJScG6deuwdetWtG3bFnXr1sXUqVPRpk0bld89bW3evBmnTp3CggULih9YWa/6SkRlY82aNaJKlSoiLy9PZGZmikqVKom7d++KTZs2iXbt2gkhhIiNjRUAxI0bNzQe5+UV5YODg0WbNm0Uj58/fy6qVKkihg4dqmhLTU0VAERCQoIQ4r9Vrzdv3qzYJj09XVhbW4stW7YIIYSIiIgQTZo0UTr3smXLRK1atZRicXV1FTk5OUrb1apVSyxbtkzxGIDYvn270jZbtmwRVatWFc+ePRNCCHHy5EkhkUhEcnKyxtc+adIkldW4a9WqJXr37q1xH7nPP/9cBAQEKB63atVKDBkyROP2L78GIYRYt26dsLe3VzwOCgoSo0aNUtqmX79+onv37orHAMRHH32kePzkyRMBQPz2229Fxkyki5d/z3bv3i0AiCpVqij9VKpUSfTv318IIcTFixcVq95r+pk+fboQQoiUlBTh4uIizp49qzhHcHCwmDRpkk5xVip+CkVExqx9+/bIysrCiRMn8PDhQzRo0ADOzs4IDg5GWFgYnj17hri4ONSpUwc1a9bU6dgvTlg0NzdHtWrV0KhRI0Wbq6srAODu3btK+7Vq1Urx/46OjmjYsCEuXryo07kbNWoES0tLnfYBgN69e2PcuHHYvn07Bg4ciPXr16NDhw5KQ2/aat68uUrbli1bsGLFCly9ehVPnjzB8+fPYWdnp3j+zJkzGDVqlM7netHFixeVJoUDQOvWrbF8+XKlthffnypVqsDOzk7lvSDStydPnsDc3BwnT56Eubm50nM2NjYAgDp16hT5O1+tWjUAwMmTJ3H37l00a9ZM8ZxMJsOhQ4fw5ZdfIicnR+U86jARIjJR9erVQ40aNXDw4EE8fPgQwcHBAAAPDw94enri6NGjOHjwIDp27KjzsS0sLJQeSyQSpTb5HBd1c280MTMzQ8Efmf9RN2xWpUoVXUJVsLS0xLBhw7Bu3Tq88cYb2LRpk0oCoa2XY0hISMCQIUMQGRmJrl27wt7eHps3b8aSJUsU21hbWxfrXMWh7v3R5b0gKo6mTZtCJpPh7t27aNu2rdptLC0t4e3trdXxOnXqhPPnzyu1hYWFwdvbG9OnT9cqCQKYCBGZtA4dOiAuLg4PHz7EtGnTFO3t2rXDb7/9huPHj2PMmDGlFs+ff/6p6H16+PAhLl++DB8fHwAFlV9paWkQQigSqTNnzhTrPBYWFpDJZCrt77zzDvz8/LBy5Uo8f/4cb7zxRvFeyEuOHj2KWrVqYdasWYq2GzduKG3TuHFjxMbGIiwsTO0xLC0t1cb8Ih8fH8THx2P48OGKtvj4ePj6+pYgeiLtPXnyBFeuXFE8Tk5OxpkzZ+Do6IgGDRpgyJAhGDZsGJYsWYKmTZvi3r17iI2NRePGjdGjRw+dzmVraws/Pz+ltipVqqBatWoq7YVhIkRkwjp06IBx48YhLy9P0SMEAMHBwRg/fjxyc3N1nihdEnPnzkW1atXg6uqKWbNmwcnJSVGR1r59e9y7dw+fffYZ3nzzTcTExOC3335TGl7SlpeXF2JjY9G6dWtIpVJFya2Pjw9atmyJ6dOn4+2339ZbL039+vWRkpKCzZs349VXX8WePXuwfft2pW0iIiLQqVMn1K1bFwMHDsTz58+xd+9eTJ8+XRHzoUOHMHDgQEilUjg5OamcZ9q0aejfvz+aNm2KkJAQ7Nq1C9u2bcP+/fv18jqIivLXX38p/ZsRHh4OABg+fDjWr1+PdevW4ZNPPsGUKVNw69YtODk5oWXLlnj99dfLKmRWjRGZsg4dOuDp06eoV6+eYt4OUJAIPX78WFFmX1oWLlyISZMmISAgAGlpadi1a5divo+Pjw9WrlyJ6OhoNGnSBMePH8fUqVOLdZ4lS5Zg37598PT0RNOmTZWeGzlyJHJzc/H222+X+PXI9ezZE5MnT8b48ePh7++Po0ePKqrJ5Nq3b4+tW7di586d8Pf3R8eOHXH8+HHF83PnzsX169dRt25dODs7qz1P7969sXz5cixevBivvPIKvvrqK6xbt07lJplEhtK+fXsIIVR+5DddtbCwQGRkJJKTk5Gbm4vbt29j27ZtSnMISyIuLk7nu8hLxMuD7kREpUx+r6KHDx+W+c0Z582bh61bt+LcuXNlGgcRlQ72CBERoWBuQ2JiIr788ktMmDChrMMholLCRIiICAV3vA0ICED79u31OixGROUbh8aIiIjIZLFHiIiIiEwWEyEiIiIyWUyEiIiIyGQxESIiIiKTxUSIiIiITBYTISIiIjJZTISIiIjIZDERIiIiIpPFRIiIiIhM1v8BMn5veuFWJpoAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -404,7 +416,7 @@ ], "metadata": { "kernelspec": { - "display_name": "process", + "display_name": ".venv", "language": "python", "name": "python3" }, @@ -418,7 +430,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.9" + "version": "3.10.15" } }, "nbformat": 4, diff --git a/process/blanket_library.py b/process/blanket_library.py index 46a26afbce..b0d21f23c9 100644 --- a/process/blanket_library.py +++ b/process/blanket_library.py @@ -15,16 +15,13 @@ build_variables, constants, divertor_variables, - error_handling, fwbs_variables, heat_transport_variables, physics_variables, primary_pumping_variables, ) -from process.fortran import ( - error_handling as eh, -) from process.utilities.f2py_string_patch import f2py_compatible_to_string +from process.warning_handler import WarningManager # Acronyms for this module: # BB Breeding Blanket @@ -647,7 +644,9 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): if (blanket_library.bllengi < (fwbs_variables.b_bz_liq * 3)) or ( blanket_library.bllengo < (fwbs_variables.b_bz_liq * 3) ): - eh.report_error(278) + WarningManager.create_warning( + "Your blanket modules are too small for the Liquid Metal pipes" + ) # Unless there is no IB blanket... else: @@ -661,7 +660,9 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) / fwbs_variables.nopipes # Poloidal if blanket_library.bllengo < (fwbs_variables.b_bz_liq * 3): - eh.report_error(278) + WarningManager.create_warning( + "Your blanket modules are too small for the Liquid Metal pipes" + ) # Calculate total flow lengths, used for pressure drop calculation # Blanket primary coolant flow @@ -1345,7 +1346,9 @@ def liquid_breeder_properties(self, output: bool = False): (t_ranges[:, 0] > mid_temp_liq).any() or (t_ranges[:, 1] < mid_temp_liq).any() ): - error_handling.report_error(280) + WarningManager.create_warning( + "Outside temperature limit for one or more liquid metal breeder properties" + ) if output: po.ocmmnt( diff --git a/process/build.py b/process/build.py index 1d31a4531f..388e1e2255 100644 --- a/process/build.py +++ b/process/build.py @@ -11,7 +11,6 @@ constants, current_drive_variables, divertor_variables, - error_handling, fwbs_variables, numerics, pfcoil_variables, @@ -19,6 +18,7 @@ tfcoil_variables, ) from process.variables import AnnotatedVariable +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) @@ -101,9 +101,9 @@ def portsz(self): current_drive_variables.rtanmax = f * np.cos(eps) - 0.5e0 * c else: # coil separation is too narrow for beam... - error_handling.fdiags[0] = g - error_handling.fdiags[1] = c - error_handling.report_error(63) + WarningManager.create_warning( + "Max beam tangency radius set =0 temporarily; change beamwd", g=g, c=c + ) current_drive_variables.rtanmax = 0.0e0 @@ -1758,9 +1758,11 @@ def calculate_radial_build(self, output: bool) -> None: # Notify user that build_variables.r_cp_top has been set to 1.01*build_variables.r_tf_inboard_out (lvl 2 error) if build_variables.r_cp_top < 1.01e0 * build_variables.r_tf_inboard_out: - error_handling.fdiags[0] = build_variables.r_cp_top - error_handling.fdiags[1] = build_variables.r_tf_inboard_out - error_handling.report_error(268) + WarningManager.create_warning( + "TF CP top radius (r_cp_top) replaced by 1.01*r_tf_inboard_out -> potential top rbuild issue", + r_cp_top=build_variables.r_cp_top, + r_tf_inboard_out=build_variables.r_tf_inboard_out, + ) # build_variables.r_cp_top correction build_variables.r_cp_top = build_variables.r_tf_inboard_out * 1.01e0 @@ -1774,9 +1776,11 @@ def calculate_radial_build(self, output: bool) -> None: elif build_variables.i_r_cp_top == 1: # Notify user that build_variables.r_cp_top has been set to 1.01*build_variables.r_tf_inboard_out (lvl 2 error) if build_variables.r_cp_top < 1.01e0 * build_variables.r_tf_inboard_out: - error_handling.fdiags[0] = build_variables.r_cp_top - error_handling.fdiags[1] = build_variables.r_tf_inboard_out - error_handling.report_error(268) + WarningManager.create_warning( + "TF CP top radius (r_cp_top) replaced by 1.01*r_tf_inboard_out -> potential top rbuild issue", + r_cp_top=build_variables.r_cp_top, + r_tf_inboard_out=build_variables.r_tf_inboard_out, + ) # build_variables.r_cp_top correction build_variables.r_cp_top = build_variables.r_tf_inboard_out * 1.01e0 @@ -1810,8 +1814,10 @@ def calculate_radial_build(self, output: bool) -> None: ) + tfcoil_variables.drtop ): - error_handling.fdiags[0] = build_variables.r_cp_top - error_handling.report_error(256) + WarningManager.create_warning( + "Top CP radius larger that its value determined with plasma shape", + r_cp_top=build_variables.r_cp_top, + ) if build_variables.i_tf_inside_cs == 1: # Radial position of vacuum vessel [m] build_variables.r_vv_inboard_out = ( @@ -2065,24 +2071,34 @@ def calculate_radial_build(self, output: bool) -> None: ) po.ocmmnt(self.outfile, " its range of applicability.)") po.oblnkl(self.outfile) - error_handling.report_error(62) + WarningManager.create_warning( + "Ripple result may be inaccurate, as the fit has been extrapolated" + ) if self.ripflag == 1: - error_handling.fdiags[0] = ( - tfcoil_variables.wwp1 - * tfcoil_variables.n_tf_coils - / physics_variables.rmajor + WarningManager.create_warning( + "(TF coil ripple calculation) Dimensionless coil width X out of fitted range", + diagnostic=( + tfcoil_variables.wwp1 + * tfcoil_variables.n_tf_coils + / physics_variables.rmajor + ), ) - error_handling.report_error(141) + elif self.ripflag == 2: # Convert to integer as idiags is integer array - error_handling.idiags[0] = int(tfcoil_variables.n_tf_coils) - error_handling.report_error(142) + WarningManager.create_warning( + "(TF coil ripple calculation) No of TF coils not between 16 and 20 inclusive", + n_tf_coils=tfcoil_variables.n_tf_coils, + ) else: - error_handling.fdiags[0] = ( - physics_variables.rmajor + physics_variables.rminor - ) / build_variables.r_tf_outboard_mid - error_handling.report_error(143) + WarningManager.create_warning( + "(TF coil ripple calculation) (R+a)/rtot out of fitted range", + diagnostic=( + (physics_variables.rmajor + physics_variables.rminor) + / build_variables.r_tf_outboard_mid + ), + ) po.ovarin( self.outfile, diff --git a/process/caller.py b/process/caller.py index 7e30a20618..596b275e13 100644 --- a/process/caller.py +++ b/process/caller.py @@ -7,6 +7,7 @@ import numpy as np from tabulate import tabulate +import process.constraints as constraints from process import fortran as ft from process.final import finalise from process.io.mfile import MFile @@ -75,7 +76,10 @@ def call_models(self, xc: np.ndarray, m: int) -> tuple[float, np.ndarray]: self._call_models_once(xc) # Evaluate objective function and constraints objf = objective_function(ft.numerics.minmax) - conf, _, _, _, _ = ft.constraints.constraint_eqns(m, -1) + conf, _, _, _, _ = constraints.constraint_eqns(m, -1) + + # PyVMCON and other bits of PROCESS require a numpy array + conf = np.array(conf) if objf_prev is None and conf_prev is None: # First run: run again to check idempotence diff --git a/process/constraints.py b/process/constraints.py new file mode 100644 index 0000000000..42d5f7741e --- /dev/null +++ b/process/constraints.py @@ -0,0 +1,2371 @@ +from collections.abc import Callable, Hashable +from dataclasses import dataclass +from typing import ClassVar, Literal + +import numpy as np + +import process.fortran as fortran +from process.exceptions import ProcessError, ProcessValueError +from process.warning_handler import WarningManager + +ConstraintSymbolType = Literal["=", ">=", "<="] + + +@dataclass +class ConstraintResult: + """The constraint quantities given the current state of the code + (aka given an evaluation at the point x). + """ + + cc: float + """The normalised residual of the constraint.""" + con: float + """The value of the constraint (in the physical units).""" + err: float + """The residual error of the constraint (in the physical units).""" + + +@dataclass +class ConstraintRegistration: + """Contains information about a constraint. + + E.g. how to call the constraint, its units, and its symbol (=, >=, <=) + """ + + name: Hashable + result: Callable[[], ConstraintResult] + units: str + symbol: ConstraintSymbolType + + +class ConstraintManager: + _constraint_registry: ClassVar[dict[Hashable, ConstraintRegistration]] = {} + """An internal registry of the PROCESS constraint equations""" + + def __init__(self): + raise NotImplementedError(f"{self.__class__.__name__} cannot be instantiated.") + + @classmethod + def register_constraint( + cls, name: Hashable, units: str, symbol: ConstraintSymbolType + ) -> Callable[[], Callable[[], ConstraintResult]]: + def wrapper(wrapped_func: Callable[[], ConstraintResult]): + if name in cls._constraint_registry: + raise ValueError(f"Constraint {name} already exists.") + cls._constraint_registry[name] = ConstraintRegistration( + name, wrapped_func, units, symbol + ) + + return wrapped_func + + return wrapper + + @classmethod + def get_constraint(cls, name: Hashable): + return cls._constraint_registry.get(name) + + +@ConstraintManager.register_constraint(1, "", "=") +def constraint_equation_1(): + """Relationship between beta, temperature (keV) and density + + author: J Morris + + beta: total plasma beta + beta_{ft}: fast alpha beta component + beta_{NBI}: neutral beam beta component + n_e: electron density [/m3] + n_i: total ion density [/m3] + T_e: density weighted average electron temperature [keV] + T_i: density weighted average ion temperature [keV] + B_{tot}: total toroidal + poloidal field [T] + """ + cc = ( + 1.0 + - ( + fortran.physics_variables.beta_fast_alpha + + fortran.physics_variables.beta_beam + + 2.0e3 + * fortran.constants.rmu0 + * fortran.constants.electron_charge + * ( + fortran.physics_variables.dene * fortran.physics_variables.ten + + fortran.physics_variables.nd_ions_total + * fortran.physics_variables.tin + ) + / fortran.physics_variables.btot**2 + ) + / fortran.physics_variables.beta + ) + return ConstraintResult( + cc=cc, + con=(fortran.physics_variables.beta * (1.0 - cc)), + err=(fortran.physics_variables.beta * cc), + ) + + +@ConstraintManager.register_constraint(2, "MW/m3", "=") +def constraint_equation_2(): + """author: J. Morris + + i_rad_loss: switch for radiation loss term usage in power balance (see User Guide): + - 0 total power lost is scaling power plus radiation (needed for ipedestal=2,3) + - 1 total power lost is scaling power plus core radiation only + - 2 total power lost is scaling power only, with no additional + allowance for radiation. This is not recommended for power plant models. + + i_plasma_ignited: switch for ignition assumption: + - 0 do not assume plasma ignition; + - 1 assume ignited (but include auxiliary power in costs) + + pden_electron_transport_loss_mw: electron transport power per volume (MW/m3) + pden_ion_transport_loss_mw: ion transport power per volume (MW/m3) + pden_plasma_rad_mw: total radiation power per volume (MW/m3) + pden_plasma_core_rad_mw: total core radiation power per volume (MW/m3) + f_alpha_plasma: fraction of alpha power deposited in plasma + alpha_power_density_total: alpha power per volume (MW/m3) + charged_power_density: non-alpha charged particle fusion power per volume (MW/m3) + pden_plasma_ohmic_mw: ohmic heating power per volume (MW/m3) + p_hcd_injected_total_mw: total auxiliary injected power (MW) + vol_plasma: plasma volume (m3) + """ + # pscaling: total transport power per volume (MW/m3) + + pscaling = ( + fortran.physics_variables.pden_electron_transport_loss_mw + + fortran.physics_variables.pden_ion_transport_loss_mw + ) + # Total power lost is scaling power plus radiation: + if fortran.physics_variables.i_rad_loss == 0: + pnumerator = pscaling + fortran.physics_variables.pden_plasma_rad_mw + elif fortran.physics_variables.i_rad_loss == 1: + pnumerator = pscaling + fortran.physics_variables.pden_plasma_core_rad_mw + else: + pnumerator = pscaling + + # if plasma not ignited include injected power + if fortran.physics_variables.i_plasma_ignited == 0: + pdenom = ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_density_total + + fortran.physics_variables.charged_power_density + + fortran.physics_variables.pden_plasma_ohmic_mw + + fortran.current_drive_variables.p_hcd_injected_total_mw + / fortran.physics_variables.vol_plasma + ) + else: + # if plasma ignited + pdenom = ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_density_total + + fortran.physics_variables.charged_power_density + + fortran.physics_variables.pden_plasma_ohmic_mw + ) + + cc = 1.0 - pnumerator / pdenom + + return ConstraintResult(cc, pdenom * (1.0 - cc), pdenom * cc) + + +@ConstraintManager.register_constraint(3, "MW/m3", "=") +def constraint_equation_3(): + """Global power balance equation for ions + i_plasma_ignited: switch for ignition assumption + - 0 do not assume plasma ignition; + - 1 assume ignited (but include auxiliary power in costs) + + pden_ion_transport_loss_mw: ion transport power per volume (MW/m3) + piepv: ion/electron equilibration power per volume (MW/m3) + f_alpha_plasma: fraction of alpha power deposited in plasma + alpha_power_ions_density: alpha power per volume to ions (MW/m3) + p_hcd_injected_ions_mw: auxiliary injected power to ions (MW) + vol_plasma: plasma volume (m3) + """ + # No assume plasma ignition: + if fortran.physics_variables.i_plasma_ignited == 0: + cc = 1.0 - ( + fortran.physics_variables.pden_ion_transport_loss_mw + + fortran.physics_variables.piepv + ) / ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_ions_density + + fortran.current_drive_variables.p_hcd_injected_ions_mw + / fortran.physics_variables.vol_plasma + ) + return ConstraintResult( + cc, + ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_ions_density + + fortran.current_drive_variables.p_hcd_injected_ions_mw + / fortran.physics_variables.vol_plasma + ) + * (1.0 - cc), + ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_ions_density + + fortran.current_drive_variables.p_hcd_injected_ions_mw + / fortran.physics_variables.vol_plasma + ) + * cc, + ) + + # Plasma ignited: + cc = 1.0 - ( + fortran.physics_variables.pden_ion_transport_loss_mw + + fortran.physics_variables.piepv + ) / ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_ions_density + ) + return ConstraintResult( + cc, + ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_ions_density + ) + * (1.0 - cc), + ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_ions_density + ) + * cc, + ) + + +@ConstraintManager.register_constraint(4, "MW/m3", "=") +def constraint_equation_4(): + """Global power balance equation for electrons + author: P B Lloyd, CCFE, Culham Science Centre + + i_rad_loss: switch for radiation loss term usage in power balance + - 0 total power lost is scaling power plus radiation (needed for ipedestal=2,3) + - 1 total power lost is scaling power plus core radiation only + - 2 total power lost is scaling power only, with no additional + allowance for radiation. This is not recommended for power plant models. + + i_plasma_ignited: switch for ignition assumption: + - 0 do not assume plasma ignition; + - 1 assume ignited (but include auxiliary power in costs) + + pden_electron_transport_loss_mw: electron transport power per volume (MW/m3) + pden_plasma_rad_mw: total radiation power per volume (MW/m3) + pden_plasma_core_rad_mw: total core radiation power per volume (MW/m3) + f_alpha_plasma: fraction of alpha power deposited in plasma + alpha_power_electron_density: alpha power per volume to electrons (MW/m3) + piepv: ion/electron equilibration power per volume (MW/m3) + p_hcd_injected_electrons_mw: auxiliary injected power to electrons (MW) + vol_plasma: plasma volume (m3) + """ + # pscaling: total transport power per volume (MW/m3) + + pscaling = fortran.physics_variables.pden_electron_transport_loss_mw + # Total power lost is scaling power plus radiation: + if fortran.physics_variables.i_rad_loss == 0: + pnumerator = pscaling + fortran.physics_variables.pden_plasma_rad_mw + elif fortran.physics_variables.i_rad_loss == 1: + pnumerator = pscaling + fortran.physics_variables.pden_plasma_core_rad_mw + else: + pnumerator = pscaling + + # if plasma not ignited include injected power + if fortran.physics_variables.i_plasma_ignited == 0: + pdenom = ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_electron_density + + fortran.physics_variables.piepv + + fortran.current_drive_variables.p_hcd_injected_electrons_mw + / fortran.physics_variables.vol_plasma + ) + else: + # if plasma ignited + pdenom = ( + fortran.physics_variables.f_alpha_plasma + * fortran.physics_variables.alpha_power_electron_density + + fortran.physics_variables.piepv + ) + + cc = 1.0 - pnumerator / pdenom + return ConstraintResult(cc, pdenom * (1.0 - cc), pdenom * cc) + + +@ConstraintManager.register_constraint(5, "/m3", "<=") +def constraint_equation_5(): + """Equation for density upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fdene: f-value for density limit + dene: electron density (/m3) + dnelimt: density limit (/m3) + dnla: line averaged electron density (m-3) + + i_density_limit: + - 1 old ASDEX; + - 2 Borrass model for ITER (I); + - 3 Borrass model for ITER (II); + - 4 JET edge radiation; + - 5 JET simplified; + - 6 Hugill-Murakami Mq limit; + - 7 Greenwald limit + """ + # Apply Greenwald limit to line-averaged density + if fortran.physics_variables.i_density_limit == 7: + return ConstraintResult( + fortran.physics_variables.dnla / fortran.physics_variables.dnelimt + - 1.0 * fortran.constraint_variables.fdene, + fortran.constraint_variables.fdene * fortran.physics_variables.dnelimt, + fortran.constraint_variables.fdene * fortran.physics_variables.dnelimt + - fortran.physics_variables.dnla, + ) + + cc = ( + fortran.physics_variables.dene / fortran.physics_variables.dnelimt + - 1.0 * fortran.constraint_variables.fdene + ) + return ConstraintResult( + cc, + fortran.physics_variables.dnelimt * (1.0 - cc), + fortran.physics_variables.dene * cc, + ) + + +@ConstraintManager.register_constraint(6, "", "<=") +def constraint_equation_6(): + """Equation for epsilon beta-poloidal upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fbeta_poloidal_eps: f-value for epsilon beta-poloidal + beta_poloidal_eps_max: maximum (eps*beta_poloidal) + eps: inverse aspect ratio + beta_poloidal: poloidal beta + """ + cc = ( + (fortran.physics_variables.eps * fortran.physics_variables.beta_poloidal) + / fortran.physics_variables.beta_poloidal_eps_max + - 1.0 * fortran.constraint_variables.fbeta_poloidal_eps + ) + return ConstraintResult( + cc, + fortran.physics_variables.beta_poloidal_eps_max * (1.0 - cc), + (fortran.physics_variables.eps * fortran.physics_variables.beta_poloidal) * cc, + ) + + +@ConstraintManager.register_constraint(7, "/m3", "=") +def constraint_equation_7(): + """Equation for hot beam ion density + + i_plasma_ignited: switch for ignition assumption: + - 0 do not assume plasma ignition + - 1 assume ignited (but include auxiliary power in costs) + O + bviously, i_plasma_ignited must be zero if current drive is required. + If i_plasma_ignited=1, any auxiliary power is assumed to be used only + during plasma start-up, and is excluded from all steady-state + power balance calculations. + beam_density_out: hot beam ion density from calculation (/m3) + nd_beam_ions: hot beam ion density, variable (/m3) + """ + if fortran.physics_variables.i_plasma_ignited == 1: + raise ProcessValueError( + "Do not use constraint equation 7 if i_plasma_ignited=1" + ) + + cc = ( + 1.0 + - fortran.physics_variables.beam_density_out + / fortran.physics_variables.nd_beam_ions + ) + return ConstraintResult( + cc, + fortran.physics_variables.nd_beam_ions * (1.0 - cc), + fortran.physics_variables.nd_beam_ions * cc, + ) + + +@ConstraintManager.register_constraint(8, "MW/m2", "<=") +def constraint_equation_8(): + """Equation for neutron wall load upper limit + + fwalld: f-value for maximum wall load + walalw: allowable wall-load (MW/m2) + pflux_fw_neutron_mw: average neutron wall load (MW/m2) + """ + return ConstraintResult( + ( + fortran.physics_variables.pflux_fw_neutron_mw + / fortran.constraint_variables.walalw + - 1.0 * fortran.constraint_variables.fwalld + ), + fortran.constraint_variables.fwalld * fortran.constraint_variables.walalw, + fortran.constraint_variables.fwalld * fortran.constraint_variables.walalw + - fortran.physics_variables.pflux_fw_neutron_mw, + ) + + +@ConstraintManager.register_constraint(9, "MW", "<=") +def constraint_equation_9(): + """Equation for fusion power upper limit + + ffuspow: f-value for maximum fusion power + powfmax: maximum fusion power (MW) + fusion_power: fusion power (MW) + """ + cc = ( + fortran.physics_variables.fusion_power / fortran.constraint_variables.powfmax + - 1.0 * fortran.constraint_variables.ffuspow + ) + return ConstraintResult( + cc, + fortran.constraint_variables.powfmax * (1.0 - cc), + fortran.physics_variables.fusion_power * cc, + ) + + +@ConstraintManager.register_constraint(11, "m", "=") +def constraint_equation_11(): + """Equation for radial build + author: P B Lloyd, CCFE, Culham Science Centre + + rbld: sum of thicknesses to the major radius (m) + rmajor: plasma major radius (m) + """ + cc = 1.0 - fortran.build_variables.rbld / fortran.physics_variables.rmajor + return ConstraintResult( + cc, + fortran.physics_variables.rmajor * (1.0 - cc), + fortran.physics_variables.rmajor * cc, + ) + + +@ConstraintManager.register_constraint(12, "V.sec", ">=") +def constraint_equation_12(): + """Equation for volt-second capability lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + vs_plasma_total_required: total V-s needed (Wb) + vs_plasma_total_required (lower limit) is positive; vs_cs_pf_total_pulse (available) is negative + fvs: f-value for flux-swing (V-s) requirement (STEADY STATE) + vs_cs_pf_total_pulse: total flux swing for pulse (Wb) + """ + # vs_cs_pf_total_pulse is negative, requires sign change + cc = ( + 1.0 + - fortran.constraint_variables.fvs + * (-fortran.pfcoil_variables.vs_cs_pf_total_pulse) + / fortran.physics_variables.vs_plasma_total_required + ) + + return ConstraintResult( + cc, + fortran.pfcoil_variables.vs_plasma_total_required * (1.0 - cc), + fortran.pfcoil_variables.vs_plasma_total_required * cc, + ) + + +@ConstraintManager.register_constraint(13, "sec", ">=") +def constraint_equation_13(): + """Equation for burn time lower limit + + author: P B Lloyd, CCFE, Culham Science Centre + + ft_burn: f-value for minimum burn time + t_burn: burn time (s) (calculated if i_pulsed_plant=1) + t_burn_min: minimum burn time (s) + """ + return ConstraintResult( + 1.0 + - fortran.constraint_variables.ft_burn + * fortran.times_variables.t_burn + / fortran.constraint_variables.t_burn_min, + fortran.constraint_variables.t_burn_min / fortran.constraint_variables.ft_burn, + fortran.constraint_variables.t_burn_min / fortran.constraint_variables.ft_burn + - fortran.times_variables.t_burn, + ) + + +@ConstraintManager.register_constraint(15, "MW", ">=") +def constraint_equation_15(): + """Equation for L-H power threshold limit + author: P B Lloyd, CCFE, Culham Science Centre + + fl_h_threshold: f-value for L-H power threshold + p_l_h_threshold_mw: L-H mode power threshold (MW) + p_plasma_separatrix_mw: power to conducted to the divertor region (MW) + """ + return ConstraintResult( + 1.0 + - fortran.constraint_variables.fl_h_threshold + * fortran.physics_variables.p_plasma_separatrix_mw + / fortran.physics_variables.p_l_h_threshold_mw, + fortran.physics_variables.p_l_h_threshold_mw, + fortran.physics_variables.p_l_h_threshold_mw + - fortran.physics_variables.p_plasma_separatrix_mw + / fortran.constraint_variables.fl_h_threshold, + ) + + +@ConstraintManager.register_constraint(16, "MW", ">=") +def constraint_equation_16(): + """Equation for net electric power lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + fpnetel: f-value for net electric power + pnetelmw: net electric power (MW) + pnetelin: required net electric power (MW) + """ + return ConstraintResult( + 1.0 + - fortran.constraint_variables.fpnetel + * fortran.heat_transport_variables.pnetelmw + / fortran.constraint_variables.pnetelin, + fortran.constraint_variables.pnetelin, + fortran.heat_transport_variables.pnetelmw + - fortran.constraint_variables.pnetelin / fortran.constraint_variables.fpnetel, + ) + + +@ConstraintManager.register_constraint(14, "", "=") +def constraint_equation_14(): + """Equation to fix number of NBI decay lengths to plasma centre + author: P B Lloyd, CCFE, Culham Science Centre + + n_beam_decay_lengths_core: neutral beam e-decay lengths to plasma centre + tbeamin: permitted neutral beam e-decay lengths to plasma centre + """ + cc = ( + 1.0 + - fortran.current_drive_variables.n_beam_decay_lengths_core + / fortran.current_drive_variables.tbeamin + ) + return ConstraintResult( + cc, + fortran.current_drive_variables.tbeamin * (1.0 - cc), + fortran.current_drive_variables.tbeamin * cc, + ) + + +@ConstraintManager.register_constraint(17, "MW/m3", "<=") +def constraint_equation_17(): + """Equation for radiation power upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + f_alpha_plasma: fraction of alpha power deposited in plasma + p_hcd_injected_total_mw: total auxiliary injected power (MW) + vol_plasma: plasma volume (m3) + alpha_power_density_total: alpha power per volume (MW/m3) + charged_power_density: non-alpha charged particle fusion power per volume (MW/m3) + pden_plasma_ohmic_mw: ohmic heating power per volume (MW/m3) + fradpwr: f-value for core radiation power limit + pden_plasma_rad_mw: total radiation power per volume (MW/m3) + """ + # Maximum possible power/vol_plasma that can be radiated (local) + pradmaxpv = ( + fortran.current_drive_variables.p_hcd_injected_total_mw + / fortran.physics_variables.vol_plasma + + fortran.physics_variables.alpha_power_density_total + * fortran.physics_variables.f_alpha_plasma + + fortran.physics_variables.charged_power_density + + fortran.physics_variables.pden_plasma_ohmic_mw + ) + + cc = ( + fortran.physics_variables.pden_plasma_rad_mw / pradmaxpv + - 1.0 * fortran.constraint_variables.fradpwr + ) + return ConstraintResult( + cc, pradmaxpv * (1.0 - cc), fortran.physics_variables.pden_plasma_rad_mw * cc + ) + + +@ConstraintManager.register_constraint(18, "MW/m2", "<=") +def constraint_equation_18(): + """Equation for divertor heat load upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fpflux_div_heat_load_mw: f-value for divertor heat load + pflux_div_heat_load_max_mw: heat load limit (MW/m2) + pflux_div_heat_load_mw: divertor heat load (MW/m2) + """ + cc = ( + fortran.divertor_variables.pflux_div_heat_load_mw + / fortran.divertor_variables.pflux_div_heat_load_max_mw + - 1.0 * fortran.constraint_variables.fpflux_div_heat_load_mw + ) + return ConstraintResult( + cc, + fortran.divertor_variables.pflux_div_heat_load_max_mw * (1.0 - cc), + fortran.divertor_variables.pflux_div_heat_load_mw * cc, + ) + + +@ConstraintManager.register_constraint(19, "MVA", "<=") +def constraint_equation_19(): + """Equation for MVA (power) upper limit: resistive TF coil set + author: P B Lloyd, CCFE, Culham Science Centre + + tfcpmw: peak resistive TF coil inboard leg power (total) (MW) + tflegmw: TF coil outboard leg resistive power (total) (MW) + fmva: f-value for maximum MVA + mvalim: MVA limit for resistive TF coil set (total) (MW) + """ + totmva = fortran.tfcoil_variables.tfcpmw + fortran.tfcoil_variables.tflegmw + + cc = ( + totmva / fortran.constraint_variables.mvalim + - 1.0 * fortran.constraint_variables.fmva + ) + return ConstraintManager( + cc, fortran.constraint_variables.mvalim * (1.0 - cc), totmva * cc + ) + + +@ConstraintManager.register_constraint(20, "m", "<=") +def constraint_equation_20(): + """Equation for neutral beam tangency radius upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fportsz: f-value for neutral beam tangency radius limit + rtanmax: maximum tangency radius for centreline of beam (m) + rtanbeam: neutral beam centreline tangency radius (m) + """ + cc = ( + fortran.current_drive_variables.rtanbeam + / fortran.current_drive_variables.rtanmax + - 1.0 * fortran.constraint_variables.fportsz + ) + return ConstraintResult( + cc, + fortran.current_drive_variables.rtanmax * (1.0 - cc), + fortran.current_drive_variables.rtanbeam * cc, + ) + + +@ConstraintManager.register_constraint(21, "", ">=") +def constraint_equation_21(): + """Equation for minor radius lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + frminor: f-value for minor radius limit + rminor: plasma minor radius (m) + aplasmin: minimum minor radius (m) + """ + cc = ( + 1.0 + - fortran.constraint_variables.frminor + * fortran.physics_variables.rminor + / fortran.build_variables.aplasmin + ) + return ConstraintResult( + cc, + fortran.build_variables.aplasmin * (1.0 - cc), + fortran.build_variables.aplasmin * cc, + ) + + +@ConstraintManager.register_constraint(23, "m", "<=") +def constraint_equation_23(): + """Equation for conducting shell radius / rminor upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + rminor: plasma minor radius (m) + dr_fw_plasma_gap_outboard: gap between plasma and first wall, outboard side (m) + dr_fw_outboard: outboard first wall thickness, initial estimate (m) + dr_blkt_outboard: outboard blanket thickness (m) + fr_conducting_wall: f-value for conducting wall radius / rminor limit + f_r_conducting_wall: maximum ratio of conducting wall distance to plasma minor radius for vertical stability + """ + # conducting shell radius (m) + rcw = ( + fortran.physics_variables.rminor + + fortran.build_variables.dr_fw_plasma_gap_outboard + + fortran.build_variables.dr_fw_outboard + + fortran.build_variables.dr_blkt_outboard + ) + + cc = ( + rcw + / ( + fortran.physics_variables.f_r_conducting_wall + * fortran.physics_variables.rminor + ) + - 1.0 * fortran.constraint_variables.fr_conducting_wall + ) + return ConstraintManager( + cc, + fortran.physics_variables.f_r_conducting_wall + * fortran.physics_variables.rminor + * (1.0 - cc), + rcw * cc, + ) + + +@ConstraintManager.register_constraint(24, "", "<=") +def constraint_equation_24(): + """Equation for beta upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + i_beta_component: switch for beta limit scaling (constraint equation 24): + - 0 apply limit to total beta; + - 1 apply limit to thermal beta; + - 2 apply limit to thermal + neutral beam beta + - 3 apply limit to toroidal beta + istell: switch for stellarator option (set via device.dat): + - 0 use tokamak model; + - 1 use stellarator model + fbeta_max: f-value for beta limit + beta_max: allowable beta + beta: total plasma beta (calculated if ipedestal =3) + beta_fast_alpha: fast alpha beta component + beta_beam: neutral beam beta component + bt: toroidal field + btot: total field + """ + # Include all beta components: relevant for both tokamaks and stellarators + if ( + fortran.physics_variables.i_beta_component == 0 + or fortran.stellarator_variables.istell != 0 + ): + cc = ( + fortran.physics_variables.beta / fortran.physics_variables.beta_max + - 1.0 * fortran.constraint_variables.fbeta_max + ) + con = fortran.physics_variables.beta_max + err = ( + fortran.physics_variables.beta_max + - fortran.physics_variables.beta / fortran.constraint_variables.fbeta_max + ) + # Here, the beta limit applies to only the thermal component, not the fast alpha or neutral beam parts + elif fortran.physics_variables.i_beta_component == 1: + cc = ( + ( + fortran.physics_variables.beta + - fortran.physics_variables.beta_fast_alpha + - fortran.physics_variables.beta_beam + ) + / fortran.physics_variables.beta_max + - 1.0 * fortran.constraint_variables.fbeta_max + ) + con = fortran.physics_variables.beta_max + err = ( + fortran.physics_variables.beta_max + - ( + fortran.physics_variables.beta + - fortran.physics_variables.beta_fast_alpha + - fortran.physics_variables.beta_beam + ) + / fortran.constraint_variables.fbeta_max + ) + # Beta limit applies to thermal + neutral beam: components of the total beta, i.e. excludes alphas + elif fortran.physics_variables.i_beta_component == 2: + cc = ( + (fortran.physics_variables.beta - fortran.physics_variables.beta_fast_alpha) + / fortran.physics_variables.beta_max + - 1.0 * fortran.constraint_variables.fbeta_max + ) + con = fortran.physics_variables.beta_max * (1.0 - cc) + err = ( + fortran.physics_variables.beta - fortran.physics_variables.beta_fast_alpha + ) * cc + # Beta limit applies to toroidal beta + elif fortran.physics_variables.i_beta_component == 3: + cc = ( + ( + fortran.physics_variables.beta + * (fortran.physics_variables.btot / fortran.physics_variables.bt) ** 2 + ) + / fortran.physics_variables.beta_max + - 1.0 * fortran.constraint_variables.fbeta_max + ) + con = fortran.physics_variables.beta_max + err = ( + fortran.physics_variables.beta_max + - ( + fortran.physics_variables.beta + * (fortran.physics_variables.btot / fortran.physics_variables.bt) ** 2 + ) + / fortran.constraint_variables.fbeta_max + ) + + return ConstraintResult(cc, con, err) + + +@ConstraintManager.register_constraint(25, "T", "<=") +def constraint_equation_25(): + """Equation for peak toroidal field upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fpeakb: f-value for maximum toroidal field + bmxlim: maximum peak toroidal field (T) + b_tf_inboard_peak: mean peak field at TF coil (T) + """ + cc = ( + fortran.tfcoil_variables.b_tf_inboard_peak / fortran.constraint_variables.bmxlim + - 1.0 * fortran.constraint_variables.fpeakb + ) + return ConstraintResult( + cc, + fortran.constraint_variables.bmxlim * (1.0 - cc), + fortran.tfcoil_variables.b_tf_inboard_peak * cc, + ) + + +@ConstraintManager.register_constraint(26, "A/m2", "<=") +def constraint_equation_26(): + """Equation for Central Solenoid current density upper limit at EOF + author: P B Lloyd, CCFE, Culham Science Centre + + fjohcreal: f-value for central solenoid current at end-of-flattop + j_cs_critical_flat_top_end: allowable central solenoid current density at end of flat-top (A/m2) + j_cs_flat_top_end: central solenoid overall current density at end of flat-top (A/m2) + """ + return ConstraintResult( + fortran.pfcoil_variables.j_cs_flat_top_end + / fortran.pfcoil_variables.j_cs_critical_flat_top_end + - 1.0 * fortran.constraint_variables.fjohc, + fortran.pfcoil_variables.j_cs_critical_flat_top_end, + fortran.pfcoil_variables.j_cs_critical_flat_top_end + - fortran.pfcoil_variables.j_cs_flat_top_end + / fortran.constraint_variables.fjohc, + ) + + +@ConstraintManager.register_constraint(27, "A/m2", "<=") +def constraint_equation_27(): + """Equation for Central Solenoid current density upper limit at BOP + author: P B Lloyd, CCFE, Culham Science Centre + + fjohc0: f-value for central solenoid current at beginning of pulse + j_cs_critical_pulse_start: allowable central solenoid current density at beginning of pulse (A/m2) + j_cs_pulse_start: central solenoid overall current density at beginning of pulse (A/m2) + """ + return ConstraintResult( + fortran.pfcoil_variables.j_cs_pulse_start + / fortran.pfcoil_variables.j_cs_critical_pulse_start + - 1.0 * fortran.constraint_variables.fjohc0, + fortran.pfcoil_variables.j_cs_critical_pulse_start, + fortran.pfcoil_variables.j_cs_critical_pulse_start + - fortran.pfcoil_variables.j_cs_pulse_start + / fortran.constraint_variables.fjohc0, + ) + + +@ConstraintManager.register_constraint(28, "", ">=") +def constraint_equation_28(): + """Equation for fusion gain (big Q) lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + fqval: pf-value for Q + bigq: Fusion gain; P_fusion / (P_injection + P_ohmic) + bigqmin: minimum fusion gain Q + i_plasma_ignited : input integer : switch for ignition assumption: + - 0 do not assume plasma ignition; + - 1 assume ignited (but include auxiliary power in costs) + Obviously, ignite must be zero if current drive is required. + If i_plasma_ignited=1, any auxiliary power is assumed to be used only + during plasma start-up, and is excluded from all steady-state + power balance calculations. + """ + if fortran.physics_variables.i_plasma_ignited != 0: + raise ProcessValueError("Do not use constraint 28 if i_plasma_ignited=1") + + cc = ( + 1.0 + - fortran.constraint_variables.fqval + * fortran.current_drive_variables.bigq + / fortran.constraint_variables.bigqmin + ) + return ConstraintResult( + cc, + fortran.constraint_variables.bigqmin * (1.0 - cc), + fortran.constraint_variables.bigqmin * cc, + ) + + +@ConstraintManager.register_constraint(29, "m", "=") +def constraint_equation_29(): + """Equation for inboard major radius: This is a consistency equation + author: P B Lloyd, CCFE, Culham Science Centre + + rmajor: plasma major radius (m) (iteration variable 3) + rminor: plasma minor radius (m) + rinboard: plasma inboard radius (m) + """ + cc = ( + 1.0 + - (fortran.physics_variables.rmajor - fortran.physics_variables.rminor) + / fortran.build_variables.rinboard + ) + return ConstraintResult( + cc, + fortran.build_variables.rinboard * (1.0 - cc), + fortran.build_variables.rinboard * cc, + ) + + +@ConstraintManager.register_constraint(30, "MW", "<=") +def constraint_equation_30(): + """Equation for injection power upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + p_hcd_injected_total_mw: total auxiliary injected power (MW) + fpinj: f-value for injection power + p_hcd_injected_max: Maximum allowable value for injected power (MW) + """ + return ConstraintResult( + fortran.current_drive_variables.p_hcd_injected_total_mw + / fortran.current_drive_variables.p_hcd_injected_max + - 1.0 * fortran.constraint_variables.fpinj, + fortran.current_drive_variables.p_hcd_injected_max, + fortran.current_drive_variables.p_hcd_injected_max + - fortran.current_drive_variables.p_hcd_injected_total_mw + / fortran.constraint_variables.fpinj, + ) + + +@ConstraintManager.register_constraint(31, "Pa", "<=") +def constraint_equation_31(): + """Equation for TF coil case stress upper limit (SCTF) + author: P B Lloyd, CCFE, Culham Science Centre + + fstrcase: f-value for TF coil case stress + sig_tf_case_max: Allowable maximum shear stress in TF coil case (Tresca criterion) (Pa) + sig_tf_case: Constrained stress in TF coil case (Pa) + """ + return ConstraintResult( + fortran.tfcoil_variables.sig_tf_case / fortran.tfcoil_variables.sig_tf_case_max + - 1.0 * fortran.constraint_variables.fstrcase, + fortran.tfcoil_variables.sig_tf_case_max, + fortran.tfcoil_variables.sig_tf_case_max + - fortran.tfcoil_variables.sig_tf_case / fortran.constraint_variables.fstrcase, + ) + + +@ConstraintManager.register_constraint(32, "Pa", "<=") +def constraint_equation_32(): + """Equation for TF coil conduit stress upper limit (SCTF) + author: P B Lloyd, CCFE, Culham Science Centre + + fstrcond: f-value for TF coil conduit stress + sig_tf_wp_max: Allowable maximum shear stress in TF coil conduit (Tresca criterion) (Pa) + sig_tf_wp: Constrained stress in TF conductor conduit (Pa) + """ + return ConstraintResult( + fortran.tfcoil_variables.sig_tf_wp / fortran.tfcoil_variables.sig_tf_wp_max + - 1.0 * fortran.constraint_variables.fstrcond, + fortran.tfcoil_variables.sig_tf_wp_max, + fortran.tfcoil_variables.sig_tf_wp_max + - fortran.tfcoil_variables.sig_tf_wp / fortran.constraint_variables.fstrcond, + ) + + +@ConstraintManager.register_constraint(33, "A/m2", "<=") +def constraint_equation_33(): + """Equation for TF coil operating/critical J upper limit (SCTF) + author: P B Lloyd, CCFE, Culham Science Centre + args : output structure : residual error; constraint value; + + fiooic: f-value for TF coil operating current / critical + jwdgcrt: critical current density for winding pack (A/m2) + j_tf_wp: winding pack current density (A/m2) + """ + if fortran.constraint_variables.fiooic > 0.7: + WarningManager.create_warning( + "fiooic shouldn't be above 0.7 for engineering reliability" + ) + + cc = ( + fortran.tfcoil_variables.j_tf_wp / fortran.tfcoil_variables.jwdgcrt + - 1.0 * fortran.constraint_variables.fiooic + ) + return ConstraintResult( + cc, + fortran.tfcoil_variables.jwdgcrt * (1.0 - cc), + fortran.tfcoil_variables.j_tf_wp * cc, + ) + + +@ConstraintManager.register_constraint(34, "V", "<=") +def constraint_equation_34(): + """Equation for TF coil dump voltage upper limit (SCTF) + author: P B Lloyd, CCFE, Culham Science Centre + + fvdump: f-value for dump voltage + vdalw: max voltage across TF coil during quench (kV) + vtfskv: voltage across a TF coil during quench (kV) + """ + return ConstraintResult( + fortran.tfcoil_variables.vtfskv / fortran.tfcoil_variables.vdalw + - 1.0 * fortran.constraint_variables.fvdump, + fortran.tfcoil_variables.vdalw, + fortran.tfcoil_variables.vdalw - fortran.tfcoil_variables.vtfskv, + ) + + +@ConstraintManager.register_constraint(35, "A/m2", "<=") +def constraint_equation_35(): + """Equation for TF coil J_wp/J_prot upper limit (SCTF) + author: P B Lloyd, CCFE, Culham Science Centre + + fjprot: f-value for TF coil winding pack current density + jwdgpro: allowable TF coil winding pack current density, for dump temperature + rise protection (A/m2) + j_tf_wp: winding pack current density (A/m2) + """ + return ConstraintResult( + fortran.tfcoil_variables.j_tf_wp / fortran.tfcoil_variables.jwdgpro + - 1.0 * fortran.constraint_variables.fjprot, + fortran.tfcoil_variables.jwdgpro, + fortran.tfcoil_variables.j_tf_wp - fortran.tfcoil_variables.jwdgpro, + ) + + +@ConstraintManager.register_constraint(36, "K", ">=") +def constraint_equation_36(): + """Equation for TF coil s/c temperature margin lower limit (SCTF) + author: P B Lloyd, CCFE, Culham Science Centre + + ftmargtf: f-value for TF coil temperature margin + tmargtf: TF coil temperature margin (K) + tmargmin_tf: minimum allowable temperature margin : TF coils (K) + """ + return ConstraintResult( + 1.0 + - fortran.constraint_variables.ftmargtf + * fortran.tfcoil_variables.tmargtf + / fortran.tfcoil_variables.tmargmin_tf, + fortran.tfcoil_variables.tmargmin_tf, + fortran.tfcoil_variables.tmargmin_tf - fortran.tfcoil_variables.tmargtf, + ) + + +@ConstraintManager.register_constraint(37, "1E20 A/Wm2", "<=") +def constraint_equation_37(): + """Equation for current drive gamma upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fgamcd: f-value for current drive gamma + gammax: maximum current drive gamma + eta_cd_norm_hcd_primary: normalised current drive efficiency (1.0e20 A/W-m2) + """ + cc = ( + fortran.current_drive_variables.eta_cd_norm_hcd_primary + / fortran.constraint_variables.gammax + - 1.0 * fortran.constraint_variables.fgamcd + ) + return ConstraintResult( + cc, + fortran.constraint_variables.gammax * (1.0 - cc), + fortran.current_drive_variables.eta_cd_norm_hcd_primary * cc, + ) + + +@ConstraintManager.register_constraint(39, "K", "<=") +def constraint_equation_39(): + """Equation for first wall temperature upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + ftpeak: f-value for first wall peak temperature + temp_fw_max: maximum temperature of first wall material (K) (i_thermal_electric_conversion>1) + temp_fw_peak: peak first wall temperature (K) + """ + if fortran.fwbs_variables.temp_fw_peak < 1.0: + raise ProcessValueError( + "temp_fw_peak = 0 implies i_pulsed_plant=0; do not use constraint 39 if i_pulsed_plant=0" + ) + cc = ( + fortran.fwbs_variables.temp_fw_peak / fortran.fwbs_variables.temp_fw_max + - 1.0 * fortran.constraint_variables.ftpeak + ) + return ConstraintResult( + cc, + fortran.fwbs_variables.temp_fw_max * (1.0 - cc), + fortran.fwbs_variables.temp_fw_peak * cc, + ) + + +@ConstraintManager.register_constraint(40, "MW", ">=") +def constraint_equation_40(): + """Equation for auxiliary power lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + fauxmn: f-value for minimum auxiliary power + p_hcd_injected_total_mw: total auxiliary injected power (MW) + auxmin: minimum auxiliary power (MW) + """ + cc = ( + 1.0 + - fortran.constraint_variables.fauxmn + * fortran.current_drive_variables.p_hcd_injected_total_mw + / fortran.constraint_variables.auxmin + ) + return ConstraintResult( + cc, + fortran.constraint_variables.auxmin * (1.0 - cc), + fortran.constraint_variables.auxmin * cc, + ) + + +@ConstraintManager.register_constraint(41, "sec", ">=") +def constraint_equation_41(): + """Equation for plasma current ramp-up time lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + ft_current_ramp_up: f-value for plasma current ramp-up time + t_current_ramp_up: plasma current ramp-up time for current initiation (s) + t_current_ramp_up_min: minimum plasma current ramp-up time (s) + """ + cc = ( + 1.0 + - fortran.constraint_variables.ft_current_ramp_up + * fortran.times_variables.t_current_ramp_up + / fortran.constraint_variables.t_current_ramp_up_min + ) + return ConstraintResult( + cc, + fortran.constraint_variables.t_current_ramp_up_min * (1.0 - cc), + fortran.constraint_variables.t_current_ramp_up_min * cc, + ) + + +@ConstraintManager.register_constraint(42, "sec", ">=") +def constraint_equation_42(): + """Equation for cycle time lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + ftcycl: f-value for cycle time + t_cycle: full cycle time (s) + tcycmn: minimum cycle time (s) + """ + if fortran.constraint_variables.tcycmn < 1.0: + raise ProcessValueError( + "tcycmn = 0 implies that i_pulsed_plant=0; do not use constraint 42 if i_pulsed_plant=0" + ) + + cc = ( + 1.0 + - fortran.constraint_variables.ftcycl + * fortran.times_variables.t_cycle + / fortran.constraint_variables.tcycmn + ) + return ConstraintResult( + cc, + fortran.constraint_variables.tcycmn * (1.0 - cc), + fortran.constraint_variables.tcycmn * cc, + ) + + +@ConstraintManager.register_constraint(43, "deg C", "=") +def constraint_equation_43(): + """Equation for average centrepost temperature: This is a consistency equation (TART) + author: P B Lloyd, CCFE, Culham Science Centre + + temp_cp_average: average temp of TF coil inboard leg conductor (C)e + tcpav2: centrepost average temperature (C) (for consistency) + itart: switch for spherical tokamak (ST) models: + - 0 use conventional aspect ratio models; + - 1 use spherical tokamak models + """ + if fortran.physics_variables.itart == 0: + raise ProcessValueError("Do not use constraint 43 if itart=0") + + if fortran.tfcoil_variables.i_tf_sup == 0: + temp_cp_average = fortran.tfcoil_variables.temp_cp_average - 273.15 + tcpav2 = fortran.tfcoil_variables.tcpav2 - 273.15 + else: + temp_cp_average = fortran.tfcoil_variables.temp_cp_average + tcpav2 = fortran.tfcoil_variables.tcpav2 + + cc = 1.0 - temp_cp_average / tcpav2 + + return ConstraintResult(cc, tcpav2 * (1.0 - cc), tcpav2 * cc) + + +@ConstraintManager.register_constraint(44, "deg C", "<=") +def constraint_equation_44(): + """Equation for centrepost temperature upper limit (TART) + author: P B Lloyd, CCFE, Culham Science Centre + + fptemp: f-value for peak centrepost temperature + ptempalw: maximum peak centrepost temperature (K) + tcpmax: peak centrepost temperature (K) + itart: switch for spherical tokamak (ST) models: + - 0: use conventional aspect ratio models; + - 1: use spherical tokamak models + """ + if fortran.physics_variables.itart == 0: + raise ProcessValueError("Do not use constraint 44 if itart=0") + + if fortran.tfcoil_variables.i_tf_sup == 0: # ! Copper case + ptempalw = fortran.tfcoil_variables.ptempalw - 273.15 + tcpmax = fortran.tfcoil_variables.tcpmax - 273.15 + else: + ptempalw = fortran.tfcoil_variables.ptempalw + tcpmax = fortran.tfcoil_variables.tcpmax + + cc = tcpmax / ptempalw - 1.0 * fortran.constraint_variables.fptemp + return ConstraintResult(cc, ptempalw * (1.0 - cc), tcpmax * cc) + + +@ConstraintManager.register_constraint(45, "", ">=") +def constraint_manager_45(): + """Equation for edge safety factor lower limit (TART) + author: P B Lloyd, CCFE, Culham Science Centre + + fq: f-value for edge safety factor + q95 : safety factor 'near' plasma edge + (unless i_plasma_current = 2 (ST current scaling), in which case q = mean edge safety factor qbar) + q95_min: lower limit for edge safety factor + itart : input integer : switch for spherical tokamak (ST) models: + - 0 use conventional aspect ratio models; + - 1 use spherical tokamak models""" + if fortran.physics_variables.itart == 0: + raise ProcessValueError("Do not use constraint 45 if itart=0") + + cc = ( + 1.0 + - fortran.constraint_variables.fq + * fortran.physics_variables.q95 + / fortran.physics_variables.q95_min + ) + return ConstraintResult( + cc, + fortran.physics_variables.q95_min * (1.0 - cc), + fortran.physics_variables.q95_min * cc, + ) + + +@ConstraintManager.register_constraint(46, "", "<=") +def constraint_equation_46(): + """Equation for Ip/Irod upper limit (TART) + author: P B Lloyd, CCFE, Culham Science Centre + + eps: inverse aspect ratio + fipir: f-value for Ip/Irod upper limit + c_tf_total: total (summed) current in TF coils (A) + plasma_current: plasma current (A) + itart: switch for spherical tokamak (ST) models: + - 0: use conventional aspect ratio models; + - 1: use spherical tokamak models + """ + if fortran.physics_variables.itart == 0: + raise ProcessValueError("Do not use constraint 46 if itart=0") + + # maximum ratio of plasma current to centrepost current + cratmx = 1.0 + 4.91 * (fortran.physics_variables.eps - 0.62) + cc = ( + fortran.physics_variables.plasma_current / fortran.tfcoil_variables.c_tf_total + ) / cratmx - 1.0 * fortran.constraint_variables.fipir + + return ConstraintResult( + cc, + cratmx * (1.0 - cc), + fortran.physics_variables.plasma_current + / fortran.tfcoil_variables.c_tf_total + * cc, + ) + + +@ConstraintManager.register_constraint(48, "", "<=") +def constraint_equation_48(): + """Equation for poloidal beta upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fbeta_poloidal: rf-value for poloidal beta + beta_poloidal_max: maximum poloidal beta + beta_poloidal: poloidal beta + """ + cc = ( + fortran.physics_variables.beta_poloidal + / fortran.constraint_variables.beta_poloidal_max + - 1.0 * fortran.constraint_variables.fbeta_poloidal + ) + return ConstraintResult( + cc, + fortran.constraint_variables.beta_poloidal_max * (1.0 - cc), + fortran.physics_variables.beta_poloidal * cc, + ) + + +@ConstraintManager.register_constraint(50, "Hz", "<=") +def constraint_equation_50(): + """IFE option: Equation for repetition rate upper limit + author: P B Lloyd, CCFE, Culham Science Centre + author: S I Muldrew, CCFE, Culham Science Centre + """ + cc = ( + fortran.ife_variables.reprat / fortran.ife_variables.rrmax + - 1.0 * fortran.ife_variables.frrmax + ) + return ConstraintResult( + cc, fortran.ife_variables.rrmax * (1.0 - cc), fortran.ife_variables.reprat * cc + ) + + +@ConstraintManager.register_constraint(51, "V.s", "=") +def constraint_equation_51(): + """Equation to enforce startup flux = available startup flux + author: P B Lloyd, CCFE, Culham Science Centre + + vs_plasma_res_ramp: resistive losses in startup V-s (Wb) + vs_plasma_ind_ramp: internal and external plasma inductance V-s (Wb)) + vs_cs_pf_total_ramp: total flux swing for startup (Wb) + """ + cc = 1.0 - fortran.pfcoil_variables.fvs_cs_pf_total_ramp * abs( + ( + fortran.physics_variables.vs_plasma_res_ramp + + fortran.physics_variables.vs_plasma_ind_ramp + ) + / fortran.pfcoil_variables.vs_cs_pf_total_ramp + ) + return ConstraintResult( + cc, + fortran.pfcoil_variables.vs_cs_pf_total_ramp * (1.0 - cc), + fortran.pfcoil_variables.vs_cs_pf_total_ramp * cc, + ) + + +@ConstraintManager.register_constraint(52, "", ">=") +def constraint_equation_52(): + """Equation for tritium breeding ratio lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + ftbr: f-value for minimum tritium breeding ratio + tbr: tritium breeding ratio (i_blanket_type=2,3 (KIT HCPB/HCLL)) + tbrmin: minimum tritium breeding ratio (If i_blanket_type=1, tbrmin=minimum 5-year time-averaged tritium breeding ratio) + """ + cc = ( + 1.0 + - fortran.constraint_variables.ftbr + * fortran.fwbs_variables.tbr + / fortran.constraint_variables.tbrmin + ) + return ConstraintResult( + cc, + fortran.constraint_variables.tbrmin * (1.0 - cc), + fortran.constraint_variables.tbrmin * cc, + ) + + +@ConstraintManager.register_constraint(53, "neutron/m2", "<=") +def constraint_equation_53(): + """Equation for fast neutron fluence on TF coil upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fflutf: f-value for maximum TF coil nuclear heating + nflutfmax: max fast neutron fluence on TF coil (n/m2) + nflutf: peak fast neutron fluence on TF coil superconductor (n/m2) + """ + cc = ( + fortran.fwbs_variables.nflutf / fortran.constraint_variables.nflutfmax + - 1.0 * fortran.constraint_variables.fflutf + ) + return ConstraintResult( + cc, + fortran.constraint_variables.nflutfmax * (1.0 - cc), + fortran.fwbs_variables.nflutf * cc, + ) + + +@ConstraintManager.register_constraint(54, "MW/m3", "<=") +def constraint_equation_54(): + """Equation for peak TF coil nuclear heating upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fptfnuc: f-value for maximum TF coil nuclear heating + ptfnucmax: maximum nuclear heating in TF coil (MW/m3) + ptfnucpm3: nuclear heating in the TF coil (MW/m3) (blktmodel>0) + """ + cc = ( + fortran.fwbs_variables.ptfnucpm3 / fortran.constraint_variables.ptfnucmax + - 1.0 * fortran.constraint_variables.fptfnuc + ) + return ConstraintResult( + cc, + fortran.constraint_variables.ptfnucmax * (1.0 - cc), + fortran.fwbs_variables.ptfnucpm3 * cc, + ) + + +@ConstraintManager.register_constraint(56, "MW/m", "<=") +def constraint_equation_56(): + """Equation for power through separatrix / major radius upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fpsepr: f-value for maximum Psep/R limit + pseprmax: maximum ratio of power crossing the separatrix to plasma major radius (Psep/R) (MW/m) + p_plasma_separatrix_mw: power to be conducted to the divertor region (MW) + rmajor: plasma major radius (m) + """ + cc = ( + ( + fortran.physics_variables.p_plasma_separatrix_mw + / fortran.physics_variables.rmajor + ) + / fortran.constraint_variables.pseprmax + - 1.0 * fortran.constraint_variables.fpsepr + ) + return ConstraintResult( + cc, + fortran.constraint_variables.pseprmax * (1.0 - cc), + ( + fortran.physics_variables.p_plasma_separatrix_mw + / fortran.physics_variables.rmajor + ) + * cc, + ) + + +@ConstraintManager.register_constraint(59, "", "<=") +def constraint_equation_59(): + """Equation for neutral beam shine-through fraction upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fnbshinef: f-value for maximum neutral beam shine-through fraction + f_p_beam_shine_through_max: maximum neutral beam shine-through fraction + f_p_beam_shine_through: neutral beam shine-through fraction + """ + cc = ( + fortran.current_drive_variables.f_p_beam_shine_through + / fortran.constraint_variables.f_p_beam_shine_through_max + - 1.0 * fortran.constraint_variables.fnbshinef + ) + return ConstraintResult( + cc, + fortran.constraint_variables.f_p_beam_shine_through_max * (1.0 - cc), + fortran.current_drive_variables.f_p_beam_shine_through * cc, + ) + + +@ConstraintManager.register_constraint(60, "K", ">=") +def constraint_equation_60(): + """Equation for Central Solenoid s/c temperature margin lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + ftmargoh: f-value for central solenoid temperature margin + temp_cs_margin: Central solenoid temperature margin (K) + tmargmin_cs: Minimum allowable temperature margin : CS (K) + """ + return ConstraintResult( + 1.0 + - fortran.constraint_variables.ftmargoh + * fortran.pfcoil_variables.temp_cs_margin + / fortran.tfcoil_variables.tmargmin_cs, + fortran.tfcoil_variables.tmargmin_cs, + fortran.tfcoil_variables.tmargmin_cs - fortran.pfcoil_variables.temp_cs_margin, + ) + + +@ConstraintManager.register_constraint(61, "", ">=") +def constraint_equation_61(): + """Equation for availability lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + favail: F-value for minimum availability + cfactr: Total plant availability fraction + avail_min: Minimum availability + """ + cc = ( + 1.0 + - fortran.cost_variables.favail + * fortran.cost_variables.cfactr + / fortran.cost_variables.avail_min + ) + return ConstraintResult( + cc, + fortran.cost_variables.avail_min * (1.0 - cc), + fortran.cost_variables.cfactr * cc, + ) + + +@ConstraintManager.register_constraint(62, "", ">=") +def constraint_equation_62(): + """Lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement times + author: P B Lloyd, CCFE, Culham Science Centre + + falpha_energy_confinement: f-value for lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement + t_alpha_confinement: alpha particle confinement time (s) + t_energy_confinement: global thermal energy confinement time (sec) + f_alpha_energy_confinement_min: Lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement times + """ + cc = ( + 1.0 + - fortran.constraint_variables.falpha_energy_confinement + * ( + fortran.physics_variables.t_alpha_confinement + / fortran.physics_variables.t_energy_confinement + ) + / fortran.constraint_variables.f_alpha_energy_confinement_min + ) + return ConstraintResult( + cc, + fortran.constraint_variables.f_alpha_energy_confinement_min, + ( + fortran.physics_variables.t_alpha_confinement + / fortran.physics_variables.t_energy_confinement + ) + * cc, + ) + + +@ConstraintManager.register_constraint(63, "", "<=") +def constraint_equation_63(): + """Upper limit on niterpump (vacuum_model = simple) + author: P B Lloyd, CCFE, Culham Science Centre + + fniterpump: f-value for constraint that number of pumps < tfno + tfno: number of TF coils (default = 50 for stellarators) + niterpump: number of high vacuum pumps (real number), each with the throughput + """ + cc = ( + fortran.vacuum_variables.niterpump / fortran.tfcoil_variables.n_tf_coils + - 1.0 * fortran.constraint_variables.fniterpump + ) + return ConstraintResult( + cc, + fortran.tfcoil_variables.n_tf_coils, + fortran.tfcoil_variables.n_tf_coils * cc, + ) + + +@ConstraintManager.register_constraint(64, "", "<=") +def constraint_equation_64(): + """Upper limit on Zeff + author: P B Lloyd, CCFE, Culham Science Centre + + fzeffmax: f-value for maximum zeff + zeffmax: maximum value for Zeff + zeff: plasma effective charge + """ + cc = ( + fortran.physics_variables.zeff / fortran.constraint_variables.fzeffmax + - 1.0 * fortran.constraint_variables.ffzeffmax + ) + return ConstraintResult( + cc, + fortran.constraint_variables.fzeffmax, + fortran.constraint_variables.fzeffmax * cc, + ) + + +@ConstraintManager.register_constraint(65, "Pa", "<=") +def constraint_equation_65(): + """Upper limit on stress of the vacuum vessel that occurs when the TF coil quenches. + author: Timothy Nunn, UKAEA + + fmaxvvstress: f-value for constraint on maximum VV stress + max_vv_stress: Maximum permitted stress of the VV (Pa) + vv_stress_quench: Stress of the VV (Pa) + """ + cc = ( + fortran.sctfcoil_module.vv_stress_quench + / fortran.tfcoil_variables.max_vv_stress + - 1.0 * fortran.constraint_variables.fmaxvvstress + ) + return ConstraintResult( + cc, + fortran.tfcoil_variables.max_vv_stress, + fortran.tfcoil_variables.max_vv_stress * cc, + ) + + +@ConstraintManager.register_constraint(66, "MW", "<=") +def constrain_equation_66(): + """Upper limit on rate of change of energy in poloidal field + author: P B Lloyd, CCFE, Culham Science Centre + + fpoloidalpower: f-value for constraint on rate of change of energy in poloidal field + maxpoloidalpower: Maximum permitted absolute rate of change of stored energy in poloidal field (MW) + peakpoloidalpower: Peak absolute rate of change of stored energy in poloidal field (MW) (11/01/16) + """ + cc = ( + fortran.pf_power_variables.peakpoloidalpower + / fortran.pf_power_variables.maxpoloidalpower + - 1.0 * fortran.constraint_variables.fpoloidalpower + ) + return ConstraintResult( + cc, + fortran.pf_power_variables.maxpoloidalpower, + fortran.pf_power_variables.maxpoloidalpower * cc, + ) + + +@ConstraintManager.register_constraint(67, "MW/m2", "<=") +def constraint_equation_67(): + """Simple upper limit on radiation wall load + author: P B Lloyd, CCFE, Culham Science Centre + + fradwall: f-value for upper limit on radiation wall load + pflux_fw_rad_max: Maximum permitted radiation wall load (MW/m^2) + pflux_fw_rad_max_mw: Peak radiation wall load (MW/m^2) + """ + cc = ( + fortran.constraint_variables.pflux_fw_rad_max_mw + / fortran.constraint_variables.pflux_fw_rad_max + - 1.0 * fortran.constraint_variables.fradwall + ) + return ConstraintResult( + cc, + fortran.constraint_variables.pflux_fw_rad_max, + fortran.constraint_variables.pflux_fw_rad_max * cc, + ) + + +@ConstraintManager.register_constraint(68, "MWT/m", "<=") +def constraint_equation_68(): + """Upper limit on Psep scaling (PsepB/qAR) + author: P B Lloyd, CCFE, Culham Science Centre + + fpsepbqar: f-value for upper limit on psepbqar, maximum Psep*Bt/qAR limit + psepbqarmax: maximum permitted value of ratio of Psep*Bt/qAR (MWT/m) + p_plasma_separatrix_mw: Power to conducted to the divertor region (MW) + bt: toroidal field on axis (T) (iteration variable 2) + q95: safety factor q at 95% flux surface + aspect: aspect ratio (iteration variable 1) + rmajor: plasma major radius (m) (iteration variable 3) + i_q95_fixed: Switch that allows for fixing q95 only in this constraint. + q95_fixed: fixed safety factor q at 95% flux surface + """ + if fortran.constraint_variables.i_q95_fixed == 1: + cc = ( + ( + ( + fortran.physics_variables.p_plasma_separatrix_mw + * fortran.physics_variables.bt + ) + / ( + fortran.constraint_variables.q95_fixed + * fortran.physics_variables.aspect + * fortran.physics_variables.rmajor + ) + ) + / fortran.constraint_variables.psepbqarmax + - 1.0 * fortran.constraint_variables.fpsepbqar + ) + err = ( + fortran.physics_variables.p_plasma_separatrix_mw + * fortran.physics_variables.bt + ) / ( + fortran.constraint_variables.q95_fixed + * fortran.physics_variables.aspect + * fortran.physics_variables.rmajor + ) - fortran.constraint_variables.psepbqarmax + else: + cc = ( + ( + ( + fortran.physics_variables.p_plasma_separatrix_mw + * fortran.physics_variables.bt + ) + / ( + fortran.physics_variables.q95 + * fortran.physics_variables.aspect + * fortran.physics_variables.rmajor + ) + ) + / fortran.constraint_variables.psepbqarmax + - 1.0 * fortran.constraint_variables.fpsepbqar + ) + err = ( + fortran.physics_variables.p_plasma_separatrix_mw + * fortran.physics_variables.bt + ) / ( + fortran.physics_variables.q95 + * fortran.physics_variables.aspect + * fortran.physics_variables.rmajor + ) - fortran.constraint_variables.psepbqarmax + + return ConstraintResult(cc, fortran.constraint_variables.psepbqarmax, err) + + +@ConstraintManager.register_constraint(72, "Pa", "<=") +def constraint_equation_72(): + """Upper limit on central Solenoid Tresca yield stress + author: P B Lloyd, CCFE, Culham Science Centre + + In the case if the bucked and wedged option ( i_tf_bucking >= 2 ) the constrained + stress is the largest the largest stress of the + - CS stress at maximum current (conservative as the TF inward pressure is not taken + into account) + - CS stress at flux swing (no current in CS) from the TF inward pressure + This allow to cover the 2 worst stress scenario in the bucked and wedged design + Otherwise (free standing TF), the stress limits are only set by the CS stress at max current + Reverse the sign so it works as an inequality constraint (tmp_cc > 0) + This will have no effect if it is used as an equality constraint because it will be squared. + + foh_stress: f-value for Tresca yield criterion in Central Solenoid + alstroh: allowable hoop stress in Central Solenoid structural material (Pa) + s_shear_cs_peak: Maximum shear stress coils/central solenoid (Pa) + sig_tf_cs_bucked: Maximum shear stress in CS case at flux swing (no current in CS) + can be significant for the bucked and weged design + i_tf_bucking: switch for TF structure design + """ + # bucked and wedged desing + if ( + fortran.tfcoil_variables.i_tf_bucking >= 2 + and fortran.build_variables.i_tf_inside_cs == 0 + ): + cc = ( + max( + fortran.pfcoil_variables.s_shear_cs_peak, + fortran.tfcoil_variables.sig_tf_cs_bucked, + ) + / fortran.pfcoil_variables.alstroh + - 1.0 * fortran.constraint_variables.foh_stress + ) + err = fortran.pfcoil_variables.alstroh - max( + fortran.pfcoil_variables.s_shear_cs_peak, + fortran.tfcoil_variables.sig_tf_cs_bucked, + ) + # Free standing CS + else: + cc = ( + fortran.pfcoil_variables.s_shear_cs_peak / fortran.pfcoil_variables.alstroh + - 1.0 * fortran.constraint_variables.foh_stress + ) + err = ( + fortran.pfcoil_variables.alstroh - fortran.pfcoil_variables.s_shear_cs_peak + ) + + return ConstraintResult(cc, fortran.pfcoil_variables.alstroh, err) + + +@ConstraintManager.register_constraint(73, "MW", ">=") +def constraint_equation_73(): + """Lower limit to ensure separatrix power is greater than the L-H power + auxiliary power + Related to constraint 15 + author: P B Lloyd, CCFE, Culham Science Centre + + fplhsep: F-value for Psep >= Plh + Paux : for consistency of two values of separatrix power + p_l_h_threshold_mw: L-H mode power threshold (MW) + p_plasma_separatrix_mw: power to be conducted to the divertor region (MW) + p_hcd_injected_total_mw : inout real : total auxiliary injected power (MW) + """ + cc = ( + 1.0 + - fortran.physics_variables.fplhsep + * fortran.physics_variables.p_plasma_separatrix_mw + / ( + fortran.physics_variables.p_l_h_threshold_mw + + fortran.current_drive_variables.p_hcd_injected_total_mw + ) + ) + return ConstraintResult( + cc, + fortran.physics_variables.p_plasma_separatrix_mw, + fortran.physics_variables.p_plasma_separatrix_mw * cc, + ) + + +@ConstraintManager.register_constraint(74, "K", "<=") +def constraint_equation_74(): + """Upper limit to ensure TF coil quench temperature < tmax_croco + ONLY used for croco HTS coil + author: P B Lloyd, CCFE, Culham Science Centre + + fcqt: f-value: TF coil quench temparature remains below tmax_croco + croco_quench_temperature: CroCo strand: Actual temp reached during a quench (K) + tmax_croco: CroCo strand: maximum permitted temp during a quench (K) + """ + cc = ( + fortran.tfcoil_variables.croco_quench_temperature + / fortran.tfcoil_variables.tmax_croco + - 1.0 * fortran.constraint_variables.fcqt + ) + return ConstraintResult( + cc, + fortran.tfcoil_variables.croco_quench_temperature, + fortran.tfcoil_variables.croco_quench_temperature * cc, + ) + + +@ConstraintManager.register_constraint(75, "A/m2", "<=") +def constraint_equation_75(): + """Upper limit to ensure that TF coil current / copper area < Maximum value + ONLY used for croco HTS coil + author: P B Lloyd, CCFE, Culham Science Centre + + copperA_m2: TF coil current / copper area (A/m2) + copperA_m2_max: Maximum TF coil current / copper area (A/m2) + f_coppera_m2: f-value for TF coil current / copper area < copperA_m2_max + """ + cc = ( + fortran.rebco_variables.coppera_m2 / fortran.rebco_variables.coppera_m2_max + - 1.0 * fortran.rebco_variables.f_coppera_m2 + ) + return ConstraintResult( + cc, fortran.rebco_variables.coppera_m2, fortran.rebco_variables.coppera_m2 * cc + ) + + +@ConstraintManager.register_constraint(76, "m-3", "<=") +def constraint_equation_76(): + """Upper limit for Eich critical separatrix density model: Added for issue 558 + author: P B Lloyd, CCFE, Culham Science Centre + + Eich critical separatrix density model + Added for issue 558 with ref to http://iopscience.iop.org/article/10.1088/1741-4326/aaa340/pdf + + alpha_crit: critical ballooning parameter value + nesep_crit: critical electron density at separatrix [m-3] + kappa: plasma separatrix elongation (calculated if i_plasma_geometry = 1-5, 7 or 9) + triang: plasma separatrix triangularity (calculated if i_plasma_geometry = 1, 3-5 or 7) + aspect: aspect ratio (iteration variable 1) + p_plasma_separatrix_mw: power to conducted to the divertor region (MW) + dlimit(7)array : density limit (/m3) as calculated using various models + fnesep: f-value for Eich critical separatrix density + """ + # TODO: why on earth are these variables being set here!? Should they be local? + fortran.physics_variables.alpha_crit = (fortran.physics_variables.kappa**1.2) * ( + 1.0 + 1.5 * fortran.physics_variables.triang + ) + fortran.physics_variables.nesep_crit = ( + 5.9 + * fortran.physics_variables.alpha_crit + * (fortran.physics_variables.aspect ** (-2.0 / 7.0)) + * (((1.0 + (fortran.physics_variables.kappa**2.0)) / 2.0) ** (-6.0 / 7.0)) + * ((fortran.physics_variables.p_plasma_separatrix_mw * 1.0e6) ** (-11.0 / 70.0)) + * fortran.physics_variables.dlimit[6] + ) + + cc = ( + fortran.physics_variables.nesep / fortran.physics_variables.nesep_crit + - 1.0 * fortran.constraint_variables.fnesep + ) + return ConstraintResult( + cc, fortran.physics_variables.nesep, fortran.physics_variables.nesep * cc + ) + + +@ConstraintManager.register_constraint(77, "A/turn", "<=") +def constraint_equation_77(): + """Equation for maximum TF current per turn upper limit + author: P B Lloyd, CCFE, Culham Science Centre + + fcpttf: f-value for TF coil current per turn + cpttf_max : allowable TF coil current per turn [A/turn] + cpttf : TF coil current per turn [A/turn] + """ + cc = ( + fortran.tfcoil_variables.cpttf / fortran.tfcoil_variables.cpttf_max + - 1.0 * fortran.constraint_variables.fcpttf + ) + return ConstraintResult( + cc, fortran.tfcoil_variables.cpttf_max, fortran.tfcoil_variables.cpttf_max * cc + ) + + +@ConstraintManager.register_constraint(78, "", ">=") +def constraint_equation_78(): + """Equation for Reinke criterion, divertor impurity fraction lower limit + author: P B Lloyd, CCFE, Culham Science Centre + + freinke : input : f-value for Reinke criterion (itv 147) + fzmin : input : minimum impurity fraction from Reinke model + fzactual : input : actual impurity fraction + """ + cc = ( + 1.0 + - fortran.constraint_variables.freinke + * fortran.reinke_variables.fzactual + / fortran.reinke_variables.fzmin + ) + return ConstraintResult( + cc, + fortran.reinke_variables.fzmin * (1.0 - cc), + fortran.reinke_variables.fzmin * cc, + ) + + +@ConstraintManager.register_constraint(79, "A/turn", "<=") +def constraint_equation_79(): + """Equation for maximum CS field + author: P B Lloyd, CCFE, Culham Science Centre + + fb_cs_limit_max: F-value for CS mmax field (cons. 79, itvar 149) + b_cs_limit_max: Central solenoid max field limit [T] + b_cs_peak_pulse_start: maximum field in central solenoid at beginning of pulse (T) + b_cs_peak_flat_top_end: maximum field in central solenoid at end of flat-top (EoF) (T) + (Note: original code has "b_cs_peak_flat_top_end/b_cs_peak_pulse_start | peak CS field [T]".) + """ + cc = ( + max( + fortran.pfcoil_variables.b_cs_peak_flat_top_end, + fortran.pfcoil_variables.b_cs_peak_pulse_start, + ) + / fortran.pfcoil_variables.b_cs_limit_max + - 1.0 * fortran.pfcoil_variables.fb_cs_limit_max + ) + return ConstraintResult( + cc, + fortran.pfcoil_variables.b_cs_limit_max, + max( + fortran.pfcoil_variables.b_cs_peak_flat_top_end, + fortran.pfcoil_variables.b_cs_peak_pulse_start, + ) + * cc, + ) + + +@ConstraintManager.register_constraint(80, "MW", ">=") +def constraint_equation_80(): + """Equation for p_plasma_separatrix_mw lower limit + author: J Morris, Culham Science Centre + args : output structure : residual error; constraint value; residual error in physical units; + output string; units string + Lower limit p_plasma_separatrix_mw + #=# physics + #=#=# fp_plasma_separatrix_min_mw, p_plasma_separatrix_mw + Logic change during pre-factoring: err, symbol, units will be assigned only if present. + fp_plasma_separatrix_min_mw : input : F-value for lower limit on p_plasma_separatrix_mw (cons. 80, itvar 153) + p_plasma_separatrix_min_mw : input : Minimum power crossing separatrix p_plasma_separatrix_mw [MW] + p_plasma_separatrix_mw : input : Power crossing separatrix [MW] + """ + cc = ( + 1.0 + - fortran.physics_variables.fp_plasma_separatrix_min_mw + * fortran.physics_variables.p_plasma_separatrix_mw + / fortran.constraint_variables.p_plasma_separatrix_min_mw + ) + return ConstraintResult( + cc, + fortran.constraint_variables.p_plasma_separatrix_min_mw, + fortran.constraint_variables.p_plasma_separatrix_min_mw * cc, + ) + + +@ConstraintManager.register_constraint(81, "m-3", ">=") +def constraint_equation_81(): + """Lower limit to ensure central density is larger that the pedestal one + author: S Kahn, Culham Science Centre + args : output structure : residual error; constraint value; + residual error in physical units; output string; units string + Lower limit ne0 > neped + !#=# physics + !#=#=# ne0, neped + Logic change during pre-factoring: err, symbol, units will be + assigned only if present. + fne0 : input : F-value for constraint on ne0 > neped + ne0 : input : Central electron density [m-3] + neped : input : Electron density at pedestal [m-3] + """ + cc = ( + 1.0 + - fortran.physics_variables.fne0 + * fortran.physics_variables.ne0 + / fortran.physics_variables.neped + ) + return ConstraintResult( + cc, fortran.physics_variables.fne0, fortran.physics_variables.fne0 * cc + ) + + +@ConstraintManager.register_constraint(82, "m", ">=") +def constraint_equation_82(): + """Equation for toroidal consistency of stellarator build + author: J Lion, IPP Greifswald + + ftoroidalgap: f-value for constraint toroidalgap > dx_tf_inboard_out_toroidal + toroidalgap: minimal gap between two stellarator coils + dx_tf_inboard_out_toroidal: total toroidal width of a tf coil + """ + return ConstraintResult( + 1.0 + - fortran.tfcoil_variables.ftoroidalgap + * fortran.tfcoil_variables.toroidalgap + / fortran.tfcoil_variables.dx_tf_inboard_out_toroidal, + fortran.tfcoil_variables.toroidalgap, + fortran.tfcoil_variables.toroidalgap + - fortran.tfcoil_variables.dx_tf_inboard_out_toroidal + / fortran.tfcoil_variables.ftoroidalgap, + ) + + +@ConstraintManager.register_constraint(83, "m", ">=") +def constraint_equation_83(): + """Equation for radial consistency of stellarator build + author: J Lion, IPP Greifswald + + f_avspace: f-value for constraint available_radial_space > required_radial_space + available_radial_space: avaible space in radial direction as given by each s.-configuration + required_radial_space: required space in radial direction + """ + cc = ( + 1.0 + - fortran.build_variables.f_avspace + * fortran.build_variables.available_radial_space + / fortran.build_variables.required_radial_space + ) + return ConstraintResult( + cc, + fortran.build_variables.available_radial_space * (1.0 - cc), + fortran.build_variables.required_radial_space * cc, + ) + + +@ConstraintManager.register_constraint(84, "", ">=") +def constraint_equation_84(): + """Equation for the lower limit of beta + author: J Lion, IPP Greifswald + + fbeta_min: f-value for constraint beta-beta_fast_alpha > beta_min + beta_min: Lower limit for beta + beta: plasma beta + """ + cc = ( + 1.0 + - fortran.constraint_variables.fbeta_min + * fortran.physics_variables.beta + / fortran.physics_variables.beta_min + ) + return ConstraintResult( + cc, + fortran.physics_variables.beta_min * (1.0 - cc), + fortran.physics_variables.beta * cc, + ) + + +@ConstraintManager.register_constraint(85, "years", "=") +def constraint_equation_85(): + """Equality constraint for the centerpost (CP) lifetime + Author : S Kahn + + Depending on the chosen option i_cp_lifetime: + - 0 : The CP full power year lifelime is set by the user (cplife_input) + - 1 : The CP lifelime is equal to the divertor one + - 2 : The CP lifetime is equal to the breeding blankets one + - 3 : The CP lifetime is equal to the plant one + + cplife: calculated CP full power year lifetime (years) + life_blkt_fpy: calculated first wall/blanket power year lifetime (years) + divlife: calculated divertor power year lifetime (years) + i_cp_lifetime: switch chosing which plant element the CP + the CP lifetime must equate + """ + # The CP lifetime is equal to the the divertor one + if fortran.cost_variables.i_cp_lifetime == 0: + cc = 1.0 - fortran.cost_variables.cplife / fortran.cost_variables.cplife_input + + elif fortran.cost_variables.i_cp_lifetime == 1: + cc = 1.0 - fortran.cost_variables.cplife / fortran.cost_variables.divlife + + # The CP lifetime is equal to the tritium breeding blankets / FW one + elif fortran.cost_variables.i_cp_lifetime == 2: + cc = 1.0 - fortran.cost_variables.cplife / fortran.fwbs_variables.life_blkt_fpy + + elif fortran.cost_variables.i_cp_lifetime == 3: + cc = 1.0 - fortran.cost_variables.cplife / fortran.cost_variables.tlife + + return ConstraintResult( + cc, + fortran.cost_variables.divlife * (1.0 - cc), + fortran.cost_variables.divlife * cc, + ) + + +@ConstraintManager.register_constraint(86, "m", "<=") +def constraint_equation_86(): + """Upper limit on the turn edge length in the TF winding pack + Author : S Kahn + + t_turn_tf: TF coil turn edge length including turn insulation [m] + f_t_turn_tf: f-value for TF turn edge length constraint + t_turn_tf_max: TF turn edge length including turn insulation upper limit [m] + """ + cc = ( + fortran.tfcoil_variables.t_turn_tf / fortran.tfcoil_variables.t_turn_tf_max + - 1.0 * fortran.tfcoil_variables.f_t_turn_tf + ) + return ConstraintResult( + cc, + fortran.tfcoil_variables.t_turn_tf_max * (1.0 - cc), + fortran.tfcoil_variables.t_turn_tf_max * cc, + ) + + +@ConstraintManager.register_constraint(87, "MW", "<=") +def constraint_equation_87(): + """Equation for TF coil cryogenic power upper limit + author: S. Kahn, CCFE, Culham Science Centre + + crypmw: cryogenic plant power (MW) + f_crypmw: f-value for maximum cryogenic plant power + crypmw_max: Maximum cryogenic plant power (MW) + """ + cc = ( + fortran.heat_transport_variables.crypmw + / fortran.heat_transport_variables.crypmw_max + - 1.0 * fortran.heat_transport_variables.f_crypmw + ) + return ConstraintResult( + cc, + fortran.heat_transport_variables.crypmw_max * (1.0 - cc), + fortran.heat_transport_variables.crypmw * cc, + ) + + +@ConstraintManager.register_constraint(88, "", "<=") +def constraint_equation_88(): + """Equation for TF coil vertical strain upper limit (absolute value) + author: CPS Swanson, PPPL, USA + + fstr_wp: f-value for TF coil strain + str_wp_max: Allowable maximum TF coil vertical strain + str_wp: Constrained TF coil vertical strain + """ + return ConstraintResult( + abs(fortran.tfcoil_variables.str_wp) / fortran.tfcoil_variables.str_wp_max + - 1.0 * fortran.constraint_variables.fstr_wp, + fortran.tfcoil_variables.str_wp_max, + fortran.tfcoil_variables.str_wp_max + - abs(fortran.tfcoil_variables.str_wp) / fortran.constraint_variables.fstr_wp, + ) + + +@ConstraintManager.register_constraint(89, "A/m2", "<=") +def constraint_equation_89(): + """Upper limit to ensure that the Central Solenoid [OH] coil current / copper area < Maximum value + author: G Turkington, CCFE, Culham Science Centre + + copperaoh_m2: CS coil current at EOF / copper area [A/m2] + copperaoh_m2_max: maximum coil current / copper area [A/m2] + f_copperaoh_m2: f-value for CS coil current / copper area + """ + cc = ( + fortran.rebco_variables.copperaoh_m2 / fortran.rebco_variables.copperaoh_m2_max + - 1.0 * fortran.rebco_variables.f_copperaoh_m2 + ) + return ConstraintResult( + cc, + fortran.rebco_variables.copperaoh_m2, + fortran.rebco_variables.copperaoh_m2 * cc, + ) + + +@ConstraintManager.register_constraint(90, "", ">=") +def constraint_equation_90(): + """Lower limit for CS coil stress load cycles + author: A. Pearce, G Turkington CCFE, Culham Science Centre + + fncycle: f-value for constraint n_cycle > n_cycle_min + n_cycle: Allowable number of cycles for CS + n_cycle_min: Minimum required cycles for CS + """ + if ( + fortran.cost_variables.ibkt_life == 1 + and fortran.cs_fatigue_variables.bkt_life_csf == 1 + ): + fortran.cs_fatigue_variables.n_cycle_min = fortran.cost_variables.bktcycles + + cc = ( + 1.0 + - fortran.constraint_variables.fncycle + * fortran.cs_fatigue_variables.n_cycle + / fortran.cs_fatigue_variables.n_cycle_min + ) + return ConstraintResult( + cc, + fortran.cs_fatigue_variables.n_cycle_min * (1.0 - cc), + fortran.cs_fatigue_variables.n_cycle * cc, + ) + + +@ConstraintManager.register_constraint(91, "MW", ">=") +def constraint_equation_91(): + """Lower limit to ensure ECRH te is greater than required te for ignition + at lower values for n and B. Or if the design point is ECRH heatable (if i_plasma_ignited==0) + stellarators only (but in principle usable also for tokamaks). + author: J Lion, IPP Greifswald + + fecrh_ignition: f-value for constraint powerht_local > powerscaling + max_gyrotron_frequency: Max. av. gyrotron frequency + te0_ecrh_achievable: Max. achievable electron temperature at ignition point + """ + # Achievable ECRH te needs to be larger than needed te for igntion + if fortran.physics_variables.i_plasma_ignited == 0: + cc = ( + 1.0 + - fortran.constraint_variables.fecrh_ignition + * ( + fortran.stellarator_variables.powerht_constraint + + fortran.current_drive_variables.p_hcd_primary_extra_heat_mw + ) + / fortran.stellarator_variables.powerscaling_constraint + ) + else: + cc = ( + 1.0 + - fortran.constraint_variables.fecrh_ignition + * fortran.stellarator_variables.powerht_constraint + / fortran.stellarator_variables.powerscaling_constraint + ) + + return ConstraintResult( + cc, + fortran.stellarator_variables.powerscaling_constraint * (1.0 - cc), + fortran.stellarator_variables.powerht_constraint * cc, + ) + + +@ConstraintManager.register_constraint(92, "", "=") +def constraint_equation_92(): + """Equation for checking is D/T ratio is consistent, and sums to 1. + author: G Turkington, UKAEA + + f_deuterium: fraction of deuterium ions + f_tritium: fraction of tritium ions + f_helium3: fraction of helium-3 ions + """ + f_deuterium = 1.0 - ( + fortran.physics_variables.f_tritium + fortran.physics_variables.f_helium3 + ) + cc = 1.0 - ( + f_deuterium + + fortran.physics_variables.f_tritium + + fortran.physics_variables.f_helium3 + ) + + return ConstraintResult(cc, 1.0, cc) + + +def constraint_eqns(m: int, ieqn: int): + """Evaluates the constraints given the current state of PROCESS. + + :param m: The number of constraints to evaluate + :param ieqn: Evaluates the 'ieqn'th constraint equation (index starts at 1) + or all equations if <= 0 + """ + + if ieqn > 0: + i1 = ieqn - 1 + i2 = ieqn + else: + i1 = 0 + i2 = m + + cc, con, err, symbol, units = [], [], [], [], [] + + for i in range(i1, i2): + constraint_id = fortran.numerics.icc[i].item() + try: + constraint = ConstraintManager.get_constraint(constraint_id) + + if constraint is None: + tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units = getattr( + fortran.constraints, f"constraint_eqn_{constraint_id:03d}" + )() + else: + result = constraint.result() + tmp_cc, tmp_con, tmp_err = result.cc, result.con, result.err + tmp_symbol, tmp_units = constraint.symbol, constraint.units + + except AttributeError as e: + error_msg = f"Constraint equation {i + 1} cannot be found" + raise ProcessError(error_msg) from e + + if np.isnan(tmp_cc) or np.isinf(tmp_cc) or abs(tmp_cc) > 9.99e99: + error_msg = ( + f"Constraint equation {constraint_id} returned an invalid residual" + ) + + raise ProcessValueError(error_msg, cc=tmp_cc) + + # Reverse the sign so it works as an inequality constraint (cc(i) > 0) + # This will have no effect if it is used as an equality constraint because it will be squared. + cc.append(-tmp_cc) + con.append(tmp_con) + err.append(tmp_err) + symbol.append(tmp_symbol) + units.append(tmp_units) + + return cc, con, err, symbol, units + + +def init_constraint_variables(): + fortran.constraint_variables.auxmin = 0.1 + fortran.constraint_variables.beta_poloidal_max = 0.19 + fortran.constraint_variables.bigqmin = 10.0 + fortran.constraint_variables.bmxlim = 12.0 + fortran.constraint_variables.fauxmn = 1.0 + fortran.constraint_variables.fbeta_poloidal_eps = 1.0 + fortran.constraint_variables.fbeta_poloidal = 1.0 + fortran.constraint_variables.fbeta_max = 1.0 + fortran.constraint_variables.fbeta_min = 1.0 + fortran.constraint_variables.fcpttf = 1.0 + fortran.constraint_variables.fr_conducting_wall = 1.0 + fortran.constraint_variables.fdene = 1.0 + fortran.constraint_variables.fdtmp = 1.0 + fortran.constraint_variables.fflutf = 1.0 + fortran.constraint_variables.ffuspow = 1.0 + fortran.constraint_variables.fgamcd = 1.0 + fortran.constraint_variables.fpflux_div_heat_load_mw = 1.0 + fortran.constraint_variables.fiooic = 0.5 + fortran.constraint_variables.fipir = 1.0 + fortran.constraint_variables.q95_fixed = 3.0 + fortran.constraint_variables.fjohc = 1.0 + fortran.constraint_variables.fjohc0 = 1.0 + fortran.constraint_variables.fjprot = 1.0 + fortran.constraint_variables.fl_h_threshold = 1.0 + fortran.constraint_variables.fmva = 1.0 + fortran.constraint_variables.fnbshinef = 1.0 + fortran.constraint_variables.fncycle = 1.0 + fortran.constraint_variables.fnesep = 1.0 + fortran.constraint_variables.foh_stress = 1.0 + fortran.constraint_variables.fpeakb = 1.0 + fortran.constraint_variables.fpinj = 1.0 + fortran.constraint_variables.fpnetel = 1.0 + fortran.constraint_variables.fportsz = 1.0 + fortran.constraint_variables.fpsepbqar = 1.0 + fortran.constraint_variables.fpsepr = 1.0 + fortran.constraint_variables.fptemp = 1.0 + fortran.constraint_variables.fptfnuc = 1.0 + fortran.constraint_variables.fq = 1.0 + fortran.constraint_variables.fqval = 1.0 + fortran.constraint_variables.fradpwr = 0.99 + fortran.constraint_variables.fradwall = 1.0 + fortran.constraint_variables.freinke = 1.0 + fortran.constraint_variables.frminor = 1.0 + fortran.constraint_variables.fstrcase = 1.0 + fortran.constraint_variables.fstrcond = 1.0 + fortran.constraint_variables.fstr_wp = 1.0 + fortran.constraint_variables.fmaxvvstress = 1.0 + fortran.constraint_variables.ftbr = 1.0 + fortran.constraint_variables.ft_burn = 1.0 + fortran.constraint_variables.ftcycl = 1.0 + fortran.constraint_variables.ftmargoh = 1.0 + fortran.constraint_variables.ftmargtf = 1.0 + fortran.constraint_variables.ft_current_ramp_up = 1.0 + fortran.constraint_variables.ftpeak = 1.0 + fortran.constraint_variables.fvdump = 1.0 + fortran.constraint_variables.fvs = 1.0 + fortran.constraint_variables.fvvhe = 1.0 + fortran.constraint_variables.fwalld = 1.0 + fortran.constraint_variables.fzeffmax = 1.0 + fortran.constraint_variables.gammax = 2.0 + fortran.constraint_variables.i_q95_fixed = 0 + fortran.constraint_variables.pflux_fw_rad_max = 1.0 + fortran.constraint_variables.mvalim = 40.0 + fortran.constraint_variables.f_p_beam_shine_through_max = 1e-3 + fortran.constraint_variables.nflutfmax = 1.0e23 + fortran.constraint_variables.p_plasma_separatrix_min_mw = 150.0 + fortran.constraint_variables.f_fw_rad_max = 3.33 + fortran.constraint_variables.pflux_fw_rad_max_mw = 0.0 + fortran.constraint_variables.pnetelin = 1.0e3 + fortran.constraint_variables.powfmax = 1.5e3 + fortran.constraint_variables.psepbqarmax = 9.5 + fortran.constraint_variables.pseprmax = 25.0 + fortran.constraint_variables.ptfnucmax = 1e-3 + fortran.constraint_variables.tbrmin = 1.1 + fortran.constraint_variables.t_burn_min = 1.0 + fortran.constraint_variables.tcycmn = 0.0 + fortran.constraint_variables.t_current_ramp_up_min = 1.0 + fortran.constraint_variables.vvhealw = 1.0 + fortran.constraint_variables.walalw = 1.0 + fortran.constraint_variables.f_alpha_energy_confinement_min = 5.0 + fortran.constraint_variables.falpha_energy_confinement = 1.0 + fortran.constraint_variables.fniterpump = 1.0 + fortran.constraint_variables.zeffmax = 3.6 + fortran.constraint_variables.fpoloidalpower = 1.0 + fortran.constraint_variables.fpsep = 1.0 + fortran.constraint_variables.fcqt = 1.0 + fortran.constraint_variables.fecrh_ignition = 1.0 diff --git a/process/current_drive.py b/process/current_drive.py index e6e80f6f04..015612653d 100644 --- a/process/current_drive.py +++ b/process/current_drive.py @@ -10,10 +10,8 @@ heat_transport_variables, physics_variables, ) -from process.fortran import ( - error_handling as eh, -) from process.plasma_profiles import PlasmaProfile +from process.warning_handler import WarningManager class NeutralBeam: @@ -793,7 +791,6 @@ def legend(self, zlocal, arg): Abramowitz and Stegun, equation 8.12.1 """ if abs(arg) > (1.0e0 + 1.0e-10): - eh.fdiags[0] = arg raise ProcessValueError("Invalid argument", arg=arg) arg2 = min(arg, (1.0e0 - 1.0e-10)) @@ -1073,7 +1070,9 @@ def lhrad(self): rat0 = rat1 else: - eh.report_error(16) + WarningManager.create_warning( + "LH penetration radius not found after lapno iterations, using 0.8*rminor" + ) rat0 = 0.8e0 return rat0 diff --git a/process/final.py b/process/final.py index 34304a85e1..4db43f35fa 100644 --- a/process/final.py +++ b/process/final.py @@ -2,13 +2,13 @@ from tabulate import tabulate +import process.constraints as constraints from process import output as op from process import ( process_output as po, ) from process.fortran import ( constants, - constraints, numerics, ) from process.objectives import objective_function @@ -67,20 +67,15 @@ def output_once_through(): f2py_compatible_to_string(i) for i in numerics.lablcc[numerics.icc[: numerics.neqns + numerics.nineqns] - 1] ] - units = [f2py_compatible_to_string(i) for i in units] - physical_constraint = [ - f"{c} {u}" for c, u in zip(value.tolist(), units, strict=False) - ] - physical_residual = [ - f"{c} {u}" for c, u in zip(residual.tolist(), units, strict=False) - ] + physical_constraint = [f"{c} {u}" for c, u in zip(value, units, strict=False)] + physical_residual = [f"{c} {u}" for c, u in zip(residual, units, strict=False)] table_data = { "Constraint Name": labels, - "Constraint Type": symbols.tolist(), + "Constraint Type": symbols, "Physical constraint": physical_constraint, "Constraint residual": physical_residual, - "Normalised residual": residual_error.tolist(), + "Normalised residual": residual_error, } po.write(constants.nout, tabulate(table_data, headers="keys")) diff --git a/process/fw.py b/process/fw.py index 81112008db..c5b5562375 100644 --- a/process/fw.py +++ b/process/fw.py @@ -8,13 +8,10 @@ blanket_library, build_variables, constants, - error_handling, fwbs_variables, ) -from process.fortran import ( - error_handling as eh, -) from process.utilities.f2py_string_patch import f2py_compatible_to_string +from process.warning_handler import WarningManager class Fw: @@ -141,10 +138,12 @@ def fw_temp( # Print debug info if temperature too low/high or NaN/Inf if np.isnan(temp_k): - eh.report_error(223) + WarningManager.create_warning("NaN first wall temperature") elif (temp_k <= 100) or (temp_k > 1500): - eh.fdiags[0] = temp_k - eh.report_error(224) + WarningManager.create_warning( + "First wall temperature (temp_k) out of range : [100-1500] K", + temp_k=temp_k, + ) # Thermal conductivity of first wall material (W/m.K) tkfw = self.fw_thermal_conductivity(temp_k) @@ -397,17 +396,19 @@ def heat_transfer(self, masflx, rhof, radius, cf, viscf, kf): # Check that Reynolds number is in valid range for the Gnielinski correlation if (reynolds <= 3000.0) or (reynolds > 5.0e6): - error_handling.fdiags[0] = reynolds - error_handling.report_error(225) + WarningManager.create_warning( + "Reynolds number out of range : [3e3-5000e3]", reynolds=reynolds + ) # Check that Prandtl number is in valid range for the Gnielinski correlation if (pr < 0.5) or (pr > 2000.0): - error_handling.fdiags[0] = pr - error_handling.report_error(226) + WarningManager.create_warning( + "Prandtl number out of range : [0.5-2000]", pr=pr + ) # Check that the Darcy friction factor is in valid range for the Gnielinski correlation if f <= 0.0: - error_handling.report_error(227) + WarningManager.create_warning("Negative Darcy friction factor (f)") return heat_transfer diff --git a/process/hcpb.py b/process/hcpb.py index ad44d24bed..a8261b54d2 100644 --- a/process/hcpb.py +++ b/process/hcpb.py @@ -18,9 +18,7 @@ primary_pumping_variables, tfcoil_variables, ) -from process.fortran import ( - error_handling as eh, -) +from process.warning_handler import WarningManager class CCFE_HCPB: @@ -578,11 +576,13 @@ def nuclear_heating_blanket(self): ) if fwbs_variables.p_blkt_nuclear_heat_total_mw < 1: - eh.fdiags[0] = fwbs_variables.p_blkt_nuclear_heat_total_mw - eh.fdiags[1] = ccfe_hcpb_module.exp_blanket - eh.fdiags[2] = physics_variables.fusion_power - eh.fdiags[3] = mass - eh.report_error(274) + WarningManager.create_warning( + "Blanket heating is <1 MW or NaN. Is something wrong?", + p_blkt_nuclear_heat_total_mw=fwbs_variables.p_blkt_nuclear_heat_total_mw, + exp_blanket=ccfe_hcpb_module.exp_blanket, + fusion_power=physics_variables.fusion_power, + mass=mass, + ) def nuclear_heating_shield(self): """Nuclear heating in the shield for CCFE HCPB model diff --git a/process/init.py b/process/init.py index bef72bbde6..3978557148 100644 --- a/process/init.py +++ b/process/init.py @@ -12,6 +12,7 @@ from process.blanket_library import init_blanket_library, init_primary_pumping_variables from process.build import init_build_variables from process.buildings import init_buildings_variables +from process.constraints import init_constraint_variables from process.costs import init_cost_variables from process.cs_fatigue import init_cs_fatigue_variables from process.current_drive import init_current_drive_variables @@ -43,6 +44,7 @@ from process.tf_coil import init_tfcoil_variables from process.utilities.f2py_string_patch import f2py_compatible_to_string from process.vacuum import init_vacuum_variables +from process.warning_handler import WarningManager from process.water_use import init_watuse_variables @@ -54,9 +56,6 @@ def init_process(): the default values for the global variables, reads in data from the input file, and checks the run parameters for consistency. """ - # Initialise error handling - fortran.error_handling.initialise_error_list() - # Initialise the program variables iteration_variables.initialise_iteration_variables() @@ -240,11 +239,11 @@ def init_all_module_vars(): run. This matters ever since Process is used as a shared library, rather than a 'run-once' executable. """ + WarningManager.reinitialise() fortran.numerics.init_numerics() init_buildings_variables() init_cost_variables() init_divertor_variables() - fortran.error_handling.init_error_handling() init_fwbs_variables() fortran.global_variables.init_global_variables() init_ccfe_hcpb_module() @@ -268,7 +267,7 @@ def init_all_module_vars(): init_vacuum_variables() init_pf_power_variables() init_build_variables() - fortran.constraint_variables.init_constraint_variables() + init_constraint_variables() init_pulse_variables() init_rebco_variables() init_reinke_variables() @@ -277,8 +276,6 @@ def init_all_module_vars(): init_blanket_library() init_dcll_module() - fortran.init_module.init_fortran_modules() - def check_process(inputs): # noqa: ARG001 """Routine to reset specific variables if certain options are diff --git a/process/io/process_config.py b/process/io/process_config.py index 85ad9c87f5..98682b077c 100644 --- a/process/io/process_config.py +++ b/process/io/process_config.py @@ -125,10 +125,7 @@ def error_status2readme(self, directory="."): if os.path.isfile("MFILE.DAT"): m_file = MFile(filename=directory + "/MFILE.DAT") - error_status = ( - f"Error status: {m_file.data['error_status'].get_scan(-1)} " - f"Error ID: {m_file.data['error_id'].get_scan(-1)}\n" - ) + error_status = f"Error status: {m_file.data['error_status'].get_scan(-1)} " if self.comment != "": with open(directory + "/README.txt", "a") as readme: @@ -1151,7 +1148,7 @@ def write_error_summary(self, sample_index): header += f" n_{label.replace('_(range_normalised)', ''):8s}" # error status, id and ifail - header += " error_status error_id ifail\n" + header += " error_status ifail\n" with open(self.wdir + "/UQ_error_summary.txt", "w") as err_summary: err_summary.write(header) @@ -1165,10 +1162,7 @@ def write_error_summary(self, sample_index): output += f" {m_file.data[f'nitvar{i:03}'].get_scan(-1):10f}" # error status and id - output += ( - f" {int(m_file.data['error_status'].get_scan(-1)):13d} " - f"{int(m_file.data['error_id'].get_scan(-1)):8d}" - ) + output += f" {int(m_file.data['error_status'].get_scan(-1)):13d} " # ifail if m_file.data["error_status"].get_scan(-1) < 3: output += f" {int(m_file.data['ifail'].get_scan(-1)):5d}\n" diff --git a/process/io/process_funcs.py b/process/io/process_funcs.py index 37ddb54dd7..6b0ebd74ac 100644 --- a/process/io/process_funcs.py +++ b/process/io/process_funcs.py @@ -12,7 +12,6 @@ import logging from os.path import join as pjoin -from pathlib import Path from sys import stderr from time import sleep @@ -222,7 +221,6 @@ def check_logfile(logfile="process.log"): Checks the log file of the PROCESS output. Stops, if an error occured that needs to be fixed before rerunning. - XXX should be deprecated!! and replaced by check_input_error! """ with open(logfile) as outlogfile: @@ -236,34 +234,6 @@ def check_logfile(logfile="process.log"): exit() -def check_input_error(wdir="."): - """ - Checks, if an input error has occurred. - Stops as a consequence. - Will also fail if the MFILE.DAT isn't found. - """ - try: - mfile_path = Path(wdir) / "MFILE.DAT" - - if mfile_path.exists(): - mfile_path_str = str(mfile_path) - mfile = MFile(filename=mfile_path_str) - else: - raise FileNotFoundError("MFile doesn't exist") - - error_id = mfile.data["error_id"].get_scan(-1) - - if error_id == 130: - print( - "Error in input file. Please check OUT.DAT for more information.", - file=stderr, - ) - exit() - except Exception: - logger.exception("Check input error exception") - raise - - ######################################## diff --git a/process/main.py b/process/main.py index 8b3c8c5312..d62f3441e6 100644 --- a/process/main.py +++ b/process/main.py @@ -79,7 +79,6 @@ # For VaryRun from process.io.process_config import RunProcessConfig from process.io.process_funcs import ( - check_input_error, get_neqns_itervars, get_variable_range, no_unfeasible_mfile, @@ -102,6 +101,7 @@ from process.tf_coil import TFCoil from process.utilities.f2py_string_patch import string_to_f2py_compatible from process.vacuum import Vacuum +from process.warning_handler import WarningManager from process.water_use import WaterUse os.environ["PYTHON_PROCESS_ROOT"] = os.path.join(os.path.dirname(__file__)) @@ -312,12 +312,6 @@ def run(self): neqns, itervars = get_neqns_itervars() lbs, ubs = get_variable_range(itervars, config.factor) - # If config file contains WDIR, use that. Otherwise, use the directory - # containing the config file (used when running regression tests in - # temp dirs) - # TODO Not sure this is required any more - wdir = config.wdir if config.wdir else Path(self.config_file).parent - # Check IN.DAT exists if not input_path.exists(): raise FileNotFoundError @@ -336,8 +330,6 @@ def run(self): # Run process on an IN.DAT file config.run_process(input_path, self.solver) - check_input_error(wdir=wdir) - if not process_stopped(): no_unfeasible = no_unfeasible_mfile() if no_unfeasible <= config.no_allowed_unfeasible: @@ -500,7 +492,7 @@ def run_scan(self, solver): def show_errors(self): """Report all informational/error messages encountered.""" - fortran.error_handling.show_errors() + WarningManager.show_errors(fortran.constants.nout) def finish(self): """Run the finish subroutine to close files open in the Fortran. diff --git a/process/output.py b/process/output.py index 98c95c4060..d936926f38 100644 --- a/process/output.py +++ b/process/output.py @@ -11,11 +11,6 @@ def write(models, _outfile): :param outfile: Fortran output unit identifier :type outfile: int """ - # Turn on error reporting - # (warnings etc. encountered in previous iterations may have cleared themselves - # during the solution process) - ft.error_handling.errors_on = True - # Call stellarator output routine instead if relevant if ft.stellarator_variables.istell != 0: models.stellarator.run(output=True) diff --git a/process/pfcoil.py b/process/pfcoil.py index 7b13760b45..e6cc925d39 100644 --- a/process/pfcoil.py +++ b/process/pfcoil.py @@ -15,7 +15,6 @@ from process.fortran import constants, numerics from process.fortran import constraint_variables as ctv from process.fortran import cs_fatigue_variables as csfv -from process.fortran import error_handling as eh from process.fortran import fwbs_variables as fwbsv from process.fortran import pfcoil_module as pf from process.fortran import pfcoil_variables as pfv @@ -24,6 +23,7 @@ from process.fortran import tfcoil_variables as tfv from process.fortran import times_variables as tv from process.utilities.f2py_string_patch import f2py_compatible_to_string +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) @@ -514,7 +514,9 @@ def pfcoil(self): else: dics = 0.0e0 pfv.f_j_cs_start_end_flat_top = 1.0e0 - eh.report_error(71) + WarningManager.create_warning( + "OH coil not present; check volt-second calculations..." + ) # Split groups of coils into one set containing ncl coils ncl = 0 @@ -1032,7 +1034,9 @@ def tf_pf_collision_detector(self): pf_tf_collision += 1 if pf_tf_collision >= 1: - eh.report_error(277) + WarningManager.create_warning( + "One or more collision between TF and PF coils. Check PF placement." + ) def solv(self, n_pf_groups_max, n_pf_coil_groups, nrws, gmat, bvec): """Solve a matrix using singular value decomposition. @@ -1844,10 +1848,12 @@ def induct(self, output): ) if noh > nohmax: - eh.idiags[0] = noh - eh.idiags[1] = nohmax - eh.fdiags[0] = bv.dr_cs - eh.report_error(73) + WarningManager.create_warning( + "Max no. of segments noh for OH coil > nohmax; increase dr_cs lower bound", + noh=noh, + nohmax=nohmax, + dr_cs=bv.dr_cs, + ) noh = min(noh, nohmax) @@ -1892,9 +1898,6 @@ def induct(self, output): if bv.dr_cs >= delzoh: deltar = math.sqrt((bv.dr_cs**2 - delzoh**2) / 12.0e0) else: - # eh.fdiags[0] = bv.dr_cs - # eh.fdiags[1] = delzoh - # eh.report_error(74) # Set deltar to something small and +ve instead; allows solver # to continue and hopefully be constrained away from this point deltar = 1.0e-6 @@ -2419,13 +2422,19 @@ def outpf(self): if pfv.temp_cs_margin < 1.01e0 * tfv.tmargmin_cs: pf.cslimit = True if not pf.cslimit: - eh.report_error(135) + WarningManager.create_warning( + "CS not using max current density: further optimisation may be possible" + ) # Check whether CS coil currents are feasible from engineering POV if ctv.fjohc > 0.7: - eh.report_error(286) + WarningManager.create_warning( + "fjohc shouldn't be above 0.7 for engineering reliability" + ) if ctv.fjohc0 > 0.7: - eh.report_error(287) + WarningManager.create_warning( + "fjohc0 shouldn't be above 0.7 for engineering reliability" + ) # REBCO fractures in strains above ~+/- 0.7% if ( @@ -2433,14 +2442,18 @@ def outpf(self): or pfv.i_cs_superconductor == 8 or pfv.i_cs_superconductor == 9 ) and abs(tfv.str_cs_con_res) > 0.7e-2: - eh.report_error(262) + WarningManager.create_warning( + "Non physical strain used in CS. Use superconductor strain < +/- 0.7%" + ) if ( pfv.i_pf_superconductor == 6 or pfv.i_pf_superconductor == 8 or pfv.i_pf_superconductor == 9 ) and abs(tfv.str_pf_con_res) > 0.7e-2: - eh.report_error(263) + WarningManager.create_warning( + "Non physical strain used in PF. Use superconductor strain < +/- 0.7%" + ) else: op.ocmmnt(self.outfile, "Resistive central solenoid") diff --git a/process/physics.py b/process/physics.py index 22fdb94d0e..c34bd17b43 100644 --- a/process/physics.py +++ b/process/physics.py @@ -21,7 +21,6 @@ constraint_variables, current_drive_variables, divertor_variables, - error_handling, fwbs_variables, impurity_radiation_module, numerics, @@ -33,6 +32,7 @@ times_variables, ) from process.utilities.f2py_string_patch import f2py_compatible_to_string +from process.warning_handler import WarningManager @nb.jit(nopython=True, cache=True) @@ -2653,7 +2653,9 @@ def physics(self): ) if reinke_variables.fzmin >= 1.0e0: - error_handling.report_error(217) + WarningManager.create_warning( + "fzmin is greater than or equal to 1.0, this is at least notable" + ) po.write( self.outfile, @@ -2664,8 +2666,8 @@ def physics(self): ), ) - @staticmethod def calculate_density_limit( + self, bt: float, i_density_limit: int, p_plasma_separatrix_mw: float, @@ -2758,9 +2760,10 @@ def calculate_density_limit( denom = (zeff - 1.0) * (1.0 - 4.0 / (3.0 * qcyl)) if denom <= 0.0: if i_density_limit == 4: - error_handling.fdiags[0] = denom - error_handling.fdiags[1] = qcyl - error_handling.report_error(80) + WarningManager.create_warning( + "qcyl < 4/3; dlimit(4) set to zero; model 5 will be enforced instead.", + qcyl=qcyl, + ) i_density_limit = 5 dlimit[3] = 0.0 @@ -3177,8 +3180,8 @@ def phyaux( f_alpha_energy_confinement, ) - @staticmethod def plasma_ohmic_heating( + self, f_c_plasma_inductive: float, kappa95: float, plasma_current: float, @@ -3239,9 +3242,11 @@ def plasma_ohmic_heating( # Check to see if plasma resistance is negative # (possible if aspect ratio is too high) if res_plasma <= 0.0: - error_handling.fdiags[0] = res_plasma - error_handling.fdiags[1] = physics_variables.aspect - error_handling.report_error(83) + WarningManager.create_warning( + "Negative plasma resistance res_plasma", + res_plasma=res_plasma, + aspect=physics_variables.aspect, + ) # Ohmic heating power per unit volume # Corrected from: pden_plasma_ohmic_mw = (f_c_plasma_inductive*plasma_current)**2 * ... @@ -4344,7 +4349,9 @@ def outplas(self): if physics_variables.ipedestal >= 1: if physics_variables.ne0 < physics_variables.neped: - error_handling.report_error(213) + WarningManager.create_warning( + "Central density is less than pedestal density" + ) po.ocmmnt(self.outfile, "Pedestal profiles are used.") po.ovarrf( @@ -5052,8 +5059,10 @@ def outplas(self): ) if physics_variables.p_plasma_separatrix_mw <= 0.001e0: - error_handling.fdiags[0] = physics_variables.p_plasma_separatrix_mw - error_handling.report_error(87) + WarningManager.create_warning( + "Possible problem with high radiation power, forcing p_plasma_separatrix_mw to odd values", + p_plasma_separatrix_mw=physics_variables.p_plasma_separatrix_mw, + ) po.oblnkl(self.outfile) po.ocmmnt( self.outfile, " BEWARE: possible problem with high radiation power" @@ -5275,13 +5284,15 @@ def outplas(self): self.outfile, "(physics_variables.bt outside Snipes 2000 fitted range)", ) - error_handling.report_error(201) + WarningManager.create_warning("bt outside Snipes 2000 fitted range") if (physics_variables.rminor < 0.15e0) or ( physics_variables.rminor > 1.15e0 ): po.ocmmnt(self.outfile, "(rminor outside Snipes 2000 fitted range)") - error_handling.report_error(202) + WarningManager.create_warning( + "rminor outside Snipes 2000 fitted range" + ) if (physics_variables.rmajor < 0.55e0) or ( physics_variables.rmajor > 3.37e0 @@ -5290,7 +5301,9 @@ def outplas(self): self.outfile, "(physics_variables.rmajor outside Snipes 2000 fitted range)", ) - error_handling.report_error(203) + WarningManager.create_warning( + "rmajor outside Snipes 2000 fitted range" + ) if (physics_variables.dnla < 0.09e20) or ( physics_variables.dnla > 3.16e20 @@ -5299,7 +5312,9 @@ def outplas(self): self.outfile, "(physics_variables.dnla outside Snipes 2000 fitted range)", ) - error_handling.report_error(204) + WarningManager.create_warning( + "dnla outside Snipes 2000 fitted range" + ) if (physics_variables.kappa < 1.0e0) or ( physics_variables.kappa > 2.04e0 @@ -5308,13 +5323,17 @@ def outplas(self): self.outfile, "(physics_variables.kappa outside Snipes 2000 fitted range)", ) - error_handling.report_error(205) + WarningManager.create_warning( + "kappa outside Snipes 2000 fitted range" + ) if (physics_variables.triang < 0.07e0) or ( physics_variables.triang > 0.74e0 ): po.ocmmnt(self.outfile, "(triang outside Snipes 2000 fitted range)") - error_handling.report_error(206) + WarningManager.create_warning( + "triang outside Snipes 2000 fitted range" + ) po.oblnkl(self.outfile) @@ -5324,7 +5343,9 @@ def outplas(self): "(L-H threshold for closed divertor only. Limited data used in Snipes fit)", ) po.oblnkl(self.outfile) - error_handling.report_error(207) + WarningManager.create_warning( + "Closed divertor only. Limited data used in Snipes fit" + ) if (numerics.ioptimz > 0) and (numerics.active_constraints[14]): po.ovarre( @@ -5790,11 +5811,13 @@ def outplas(self): ) # Error to catch if bootstap fraction limit has been enforced if physics_module.err242 == 1: - error_handling.report_error(242) + WarningManager.create_warning("Bootstrap fraction upper limit enforced") # Error to catch if self-driven current fraction limit has been enforced if physics_module.err243 == 1: - error_handling.report_error(243) + WarningManager.create_warning( + "Predicted plasma driven current is more than upper limit on non-inductive fraction" + ) if current_drive_variables.f_c_plasma_bootstrap_max < 0.0e0: po.ocmmnt( @@ -5860,7 +5883,10 @@ def outplas(self): ) # Error to show if diamagnetic current is above 1% but not used if current_drive_variables.f_c_plasma_diamagnetic_scene > 0.01e0: - error_handling.report_error(244) + WarningManager.create_warning( + "Diamagnetic fraction is more than 1%, but not calculated. " + "Consider using i_diamagnetic_current=2 and i_pfirsch_schluter_current=1." + ) elif physics_variables.i_diamagnetic_current == 1: po.ocmmnt( diff --git a/process/power.py b/process/power.py index 05a020a3f1..75d7d18591 100644 --- a/process/power.py +++ b/process/power.py @@ -12,7 +12,6 @@ constraint_variables, cost_variables, current_drive_variables, - error_handling, fwbs_variables, heat_transport_variables, numerics, @@ -25,6 +24,7 @@ times_variables, ) from process.variables import AnnotatedVariable +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) @@ -2474,9 +2474,10 @@ def plant_thermal_efficiency(self, etath): if (heat_transport_variables.tturb < 657.0e0) or ( heat_transport_variables.tturb > 915.0e0 ): - error_handling.idiags[0] = 2 - error_handling.fdiags[0] = heat_transport_variables.tturb - error_handling.report_error(166) + WarningManager.create_warning( + "Turbine temperature tturb out of range of validity", + tturb=heat_transport_variables.tturb, + ) etath = ( 0.1802e0 * np.log(heat_transport_variables.tturb) @@ -2493,9 +2494,10 @@ def plant_thermal_efficiency(self, etath): if (heat_transport_variables.tturb < 657.0e0) or ( heat_transport_variables.tturb > 915.0e0 ): - error_handling.idiags[0] = 2 - error_handling.fdiags[0] = heat_transport_variables.tturb - error_handling.report_error(166) + WarningManager.create_warning( + "Turbine temperature tturb out of range of validity", + tturb=heat_transport_variables.tturb, + ) etath = ( 0.1802e0 * np.log(heat_transport_variables.tturb) @@ -2522,9 +2524,10 @@ def plant_thermal_efficiency(self, etath): if (heat_transport_variables.tturb < 408.0e0) or ( heat_transport_variables.tturb > 1023.0e0 ): - error_handling.idiags[0] = 3 - error_handling.fdiags[0] = heat_transport_variables.tturb - error_handling.report_error(166) + WarningManager.create_warning( + "Turbine temperature tturb out of range of validity", + tturb=heat_transport_variables.tturb, + ) etath = 0.4347e0 * np.log(heat_transport_variables.tturb) - 2.5043e0 @@ -2551,9 +2554,10 @@ def plant_thermal_efficiency_2(self, etath_liq): if (heat_transport_variables.tturb < 408.0e0) or ( heat_transport_variables.tturb > 1023.0e0 ): - error_handling.idiags[0] = 3 - error_handling.fdiags[0] = heat_transport_variables.tturb - error_handling.report_error(166) + WarningManager.create_warning( + "Turbine temperature tturb out of range of validity", + tturb=heat_transport_variables.tturb, + ) return 0.4347e0 * np.log(heat_transport_variables.tturb) - 2.5043e0 diff --git a/process/profiles.py b/process/profiles.py index 758ee1930f..a2db4c8347 100644 --- a/process/profiles.py +++ b/process/profiles.py @@ -4,7 +4,8 @@ import numpy as np import scipy as sp -from process.fortran import error_handling, physics_variables +from process.fortran import physics_variables +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) # Logging handler for console output @@ -211,7 +212,9 @@ def ncore( if ncore < 0.0: # Allows solver to continue and # warns the user to raise the lower bound on dene if the run did not converge - error_handling.report_error(282) + WarningManager.create_warning( + "ncore is going negative when solving. Please raise the value of dene and or its lower limit" + ) ncore = 1.0e-6 return ncore diff --git a/process/pulse.py b/process/pulse.py index 669dac2069..e9ddb9fa2e 100644 --- a/process/pulse.py +++ b/process/pulse.py @@ -2,7 +2,6 @@ from process.fortran import ( constants, constraint_variables, - error_handling, numerics, pf_power_variables, pfcoil_variables, @@ -10,6 +9,7 @@ pulse_variables, times_variables, ) +from process.warning_handler import WarningManager class Pulse: @@ -185,12 +185,13 @@ def burn(self, output: bool): tb = vsmax / v_plasma_loop_burn - times_variables.t_fusion_ramp if tb < 0.0e0: - error_handling.fdiags[0] = tb - error_handling.fdiags[1] = vsmax - error_handling.fdiags[2] = v_plasma_loop_burn - error_handling.fdiags[3] = times_variables.t_fusion_ramp - error_handling.report_error(93) - + WarningManager.create_warning( + "Negative burn time available; reduce t_fusion_ramp or raise PF coil V-s capability", + tb=tb, + vsmax=vsmax, + v_plasma_loop_burn=v_plasma_loop_burn, + t_fusion_ramp=times_variables.t_fusion_ramp.item(), + ) times_variables.t_burn = max(0.0e0, tb) # Output section diff --git a/process/resistive_tf_coil.py b/process/resistive_tf_coil.py index 5426dd60d9..c8f5598433 100644 --- a/process/resistive_tf_coil.py +++ b/process/resistive_tf_coil.py @@ -6,13 +6,13 @@ from process.fortran import ( build_variables, constants, - error_handling, pfcoil_variables, physics_variables, sctfcoil_module, tfcoil_variables, ) from process.tf_coil import TFCoil +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) @@ -233,7 +233,9 @@ def run(self, output: bool): ) except ValueError as e: if e.args[1] == 245 and e.args[2] == 0: - error_handling.report_error(245) + WarningManager.create_warning( + "Invalid stress model (r_tf_inboard = 0), stress constraint switched off" + ) tfcoil_variables.sig_tf_case = 0.0e0 tfcoil_variables.sig_tf_wp = 0.0e0 @@ -367,13 +369,16 @@ def res_tf_internal_geom(self): # Reporting negative WP areas issues if sctfcoil_module.awpc < 0.0e0: - error_handling.fdiags[0] = sctfcoil_module.awpc - error_handling.fdiags[0] = tfcoil_variables.dr_tf_wp - error_handling.report_error(99) + WarningManager.create_warning( + "Winding pack cross-section problem", + awpc=sctfcoil_module.awpc, + dr_tf_wp=tfcoil_variables.dr_tf_wp, + ) elif sctfcoil_module.awptf < 0.0e0: - error_handling.fdiags[0] = sctfcoil_module.awptf - error_handling.report_error(101) + WarningManager.create_warning( + "Negative cable space dimension", awptf=sctfcoil_module.awptf + ) def tf_res_heating(self) -> None: """ @@ -612,38 +617,6 @@ def cpost( r_tfin_inleg = r_tf_inboard_in + cas_in_th + gr_ins_th # -# - # Error traps - # ------------ - # if rtop <= 0.0e0: - # error_handling.fdiags[0] = rtop - # error_handling.report_error(115) - - # if ztop <= 0.0e0: - # error_handling.fdiags[0] = ztop - # error_handling.report_error(116) - - # if rmid <= 0.0e0: - # error_handling.fdiags[0] = rmid - # error_handling.report_error(117) - - # if build_variables.hmax <= 0.0e0: - # error_handling.fdiags[0] = build_variables.hmax - # error_handling.report_error(118) - - # if (fcool < 0.0e0) or (fcool > 1.0e0): - # error_handling.fdiags[0] = fcool - # error_handling.report_error(119) - - # if rtop < rmid: - # error_handling.fdiags[0] = rtop - # error_handling.fdiags[1] = rmid - # error_handling.report_error(120) - - # if build_variables.hmax < ztop: - # error_handling.fdiags[0] = build_variables.hmax - # error_handling.fdiags[1] = ztop - # error_handling.report_error(121) - # ------------ # Mid-plane area calculations @@ -688,7 +661,6 @@ def cpost( vol_case_cp = 0.0e0 vol_gr_ins_cp = 0.0e0 vol_ins_cp = 0.0e0 - # error_handling.report_error(122) return ( a_cp_cool, vol_cond_cp, @@ -748,14 +720,6 @@ def cpost( r = rc - np.sqrt((rc - rmid) ** 2 - z * z) - # if r <= 0.0e0: - # error_handling.fdiags[0] = r - # error_handling.fdiags[1] = rc - # error_handling.fdiags[2] = rmid - # error_handling.fdiags[3] = z - - # error_handling.report_error(123) - # Insulation cross-sectional area at z yy_ins[ii] = ( np.pi * ((r_tfin_inleg + ins_th) ** 2 - r_tfin_inleg**2) diff --git a/process/scan.py b/process/scan.py index 7f85c67681..d1634b393a 100644 --- a/process/scan.py +++ b/process/scan.py @@ -3,6 +3,7 @@ import numpy as np from tabulate import tabulate +import process.constraints as constraints import process.process_output as process_output from process.caller import write_output_files from process.exceptions import ProcessValueError @@ -10,12 +11,10 @@ build_variables, constants, constraint_variables, - constraints, cost_variables, cs_fatigue_variables, current_drive_variables, divertor_variables, - error_handling, fwbs_variables, global_variables, heat_transport_variables, @@ -32,6 +31,7 @@ f2py_compatible_to_string, string_to_f2py_compatible, ) +from process.warning_handler import WarningManager @dataclass @@ -164,13 +164,10 @@ def run_scan(self): number of output variable values are written to the MFILE.DAT file at each scan point, for plotting or other post-processing purposes. """ - # Turn off error reporting (until next output) - error_handling.errors_on = False - if scan_module.isweep == 0: ifail = self.doopt() write_output_files(models=self.models, ifail=ifail) - error_handling.show_errors() + WarningManager.show_errors(constants.nout) return if scan_module.isweep > scan_module.ipnscns: @@ -203,8 +200,6 @@ def post_optimise(self, ifail: int): """ numerics.sqsumsq = (numerics.rcm[: numerics.neqns] ** 2).sum() ** 0.5 - error_handling.errors_on = True - process_output.oheadr(constants.nout, "Numerics") process_output.ocmmnt( constants.nout, "PROCESS has performed a VMCON (optimisation) run." @@ -216,8 +211,9 @@ def post_optimise(self, ifail: int): ) process_output.oblnkl(constants.iotty) - error_handling.idiags[0] = ifail - error_handling.report_error(132) + WarningManager.create_warning( + "Optimisation solver VMCON returns with ifail != 1", ifail=ifail + ) self.verror(ifail) process_output.oblnkl(constants.nout) @@ -259,8 +255,9 @@ def post_optimise(self, ifail: int): ) process_output.oblnkl(constants.iotty) - error_handling.fdiags[0] = numerics.sqsumsq - error_handling.report_error(134) + WarningManager.create_warning( + "High final VMCON constraint residues", sqsumsq=numerics.sqsumsq + ) process_output.ovarin( constants.nout, "Number of iteration variables", "(nvar)", numerics.nvar @@ -429,8 +426,8 @@ def post_optimise(self, ifail: int): equality_constraint_table.append([ name, sym[i], - f"{con2[i]} {f2py_compatible_to_string(lab[i])}", - f"{err[i]} {f2py_compatible_to_string(lab[i])}", + f"{con2[i]} {lab[i]}", + f"{err[i]} {lab[i]}", con1[i], ]) process_output.ovarre( @@ -468,8 +465,8 @@ def post_optimise(self, ifail: int): inequality_constraint_table.append([ name, sym[i], - f"{con2[i]} {f2py_compatible_to_string(lab[i])}", - f"{err[i]} {f2py_compatible_to_string(lab[i])}", + f"{con2[i]} {lab[i]}", + f"{err[i]} {lab[i]}", ]) process_output.ovarre( constants.mfile, @@ -656,8 +653,8 @@ def scan_1d(self): scan_1d_ifail_dict[iscan] = ifail write_output_files(models=self.models, ifail=ifail) - error_handling.show_errors() - error_handling.init_error_handling() + WarningManager.show_errors(constants.nout) + WarningManager.reinitialise() # outvar now contains results self.scan_1d_write_plot() @@ -709,8 +706,8 @@ def scan_2d(self): write_output_files(models=self.models, ifail=ifail) - error_handling.show_errors() - error_handling.init_error_handling() + WarningManager.show_errors(constants.nout) + WarningManager.reinitialise() scan_2d_ifail_list[iscan_1][iscan_2] = ifail iscan = iscan + 1 diff --git a/process/stellarator.py b/process/stellarator.py index eb86689989..843c5b7b00 100644 --- a/process/stellarator.py +++ b/process/stellarator.py @@ -19,7 +19,6 @@ cost_variables, current_drive_variables, divertor_variables, - error_handling, fwbs_variables, global_variables, heat_transport_variables, @@ -43,6 +42,7 @@ from process.physics import rether from process.stellarator_config import load_stellarator_config from process.utilities.f2py_string_patch import f2py_compatible_to_string +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) # Logging handler for console output @@ -3378,11 +3378,13 @@ def intersect(self, x1, y1, x2, y2, xin): xmax = min(np.max(x1), np.amax(x2)) if xmin >= xmax: - error_handling.fdiags[0] = np.amin(x1) - error_handling.fdiags[1] = np.amin(x2) - error_handling.fdiags[2] = np.amax(x1) - error_handling.fdiags[3] = np.amax(x2) - error_handling.report_error(111) + WarningManager.create_warning( + "X ranges not overlapping", + x1_min=np.amin(x1), + x1_max=np.amax(x1), + x2_min=np.amin(x2), + x2_max=np.amax(x2), + ) # Ensure input guess for x is within this range @@ -3430,20 +3432,24 @@ def intersect(self, x1, y1, x2, y2, xin): x = x - 2.0e0 * dx * y / (yright - yleft) if x < xmin: - error_handling.fdiags[0] = x - error_handling.fdiags[1] = xmin - error_handling.report_error(112) + WarningManager.create_warning( + "X has dropped below Xmin; X has been set equal to Xmin", + x=x, + xmin=xmin, + ) x = xmin break if x > xmax: - error_handling.fdiags[0] = x - error_handling.fdiags[1] = xmax - error_handling.report_error(113) + WarningManager.create_warning( + "X has risen above Xmax; X has been set equal to Xmax", + x=x, + xmax=xmax, + ) x = xmax break else: - error_handling.report_error(114) + WarningManager.create_warning("Convergence too slow; X may be wrong") return x diff --git a/process/superconducting_tf_coil.py b/process/superconducting_tf_coil.py index 4071bf282a..298d1f2818 100644 --- a/process/superconducting_tf_coil.py +++ b/process/superconducting_tf_coil.py @@ -11,7 +11,6 @@ build_variables, constants, divertor_variables, - error_handling, global_variables, numerics, pfcoil_variables, @@ -22,6 +21,7 @@ ) from process.tf_coil import TFCoil from process.utilities.f2py_string_patch import f2py_compatible_to_string +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) @@ -260,7 +260,9 @@ def run(self, output: bool): ) except ValueError as e: if e.args[1] == 245 and e.args[2] == 0: - error_handling.report_error(245) + WarningManager.create_warning( + "Invalid stress model (r_tf_inboard = 0), stress constraint switched off" + ) tfcoil_variables.sig_tf_case = 0.0e0 tfcoil_variables.sig_tf_wp = 0.0e0 peaktfflag = 0 @@ -902,8 +904,11 @@ def supercon( tc0m = 16.06e0 # If strain limit achieved, throw a warning and use the lower strain if abs(strain) > 0.5e-2: - error_handling.fdiags[0] = strain - error_handling.report_error(261) + WarningManager.create_warning( + "TF strain was outside the region of applicability. Used lower strain", + strain=strain, + ) + strain = np.sign(strain) * 0.5e-2 # j_crit_sc returned by superconductors.itersc is the critical current density in the @@ -955,8 +960,10 @@ def supercon( tc0m = tcritsc # If strain limit achieved, throw a warning and use the lower strain if abs(strain) > 0.5e-2: - error_handling.fdiags[0] = strain - error_handling.report_error(261) + WarningManager.create_warning( + "TF strain was outside the region of applicability. Used lower strain", + strain=strain, + ) strain = np.sign(strain) * 0.5e-2 j_crit_sc, _, _ = superconductors.itersc(thelium, bmax, strain, bc20m, tc0m) @@ -974,8 +981,10 @@ def supercon( tc0m = 16.06e0 # If strain limit achieved, throw a warning and use the lower strain if abs(strain) > 0.5e-2: - error_handling.fdiags[0] = strain - error_handling.report_error(261) + WarningManager.create_warning( + "TF strain was outside the region of applicability. Used lower strain", + strain=strain, + ) strain = np.sign(strain) * 0.5e-2 # j_crit_sc returned by superconductors.itersc is the critical current density in the @@ -1015,8 +1024,10 @@ def supercon( tc0m = 185 # If strain limit achieved, throw a warning and use the lower strain if abs(strain) > 0.7e-2: - error_handling.fdiags[0] = strain - error_handling.report_error(261) + WarningManager.create_warning( + "TF strain was outside the region of applicability. Used lower strain", + strain=strain, + ) strain = np.sign(strain) * 0.7e-2 j_crit_sc, _, _ = superconductors.gl_rebco( @@ -1039,8 +1050,10 @@ def supercon( tc0m = 92 # If strain limit achieved, throw a warning and use the lower strain if abs(strain) > 0.7e-2: - error_handling.fdiags[0] = strain - error_handling.report_error(261) + WarningManager.create_warning( + "TF strain was outside the region of applicability. Used lower strain", + strain=strain, + ) strain = np.sign(strain) * 0.7e-2 # 'high current density' as per parameterisation described in Wolf, @@ -1087,7 +1100,9 @@ def supercon( # REBCO measurements from 2 T to 14 T, extrapolating outside this if (isumat == 8) and (tfcoil_variables.bmaxtfrp >= 14): - error_handling.report_error(266) + WarningManager.create_warning( + "Field on superconductor > 14 T (outside of interpolation range)" + ) # Temperature margin (already calculated in superconductors.bi2212 for isumat=2) @@ -1742,15 +1757,17 @@ def sc_tf_internal_geom(self, i_tf_wp_geom, i_tf_case_geom, i_tf_turns_integer): or sctfcoil_module.a_tf_ins <= 0.0e0 or sctfcoil_module.f_tf_ins <= 0.0e0 ): - error_handling.fdiags[0] = tfcoil_variables.acond - error_handling.fdiags[1] = tfcoil_variables.avwp - error_handling.fdiags[2] = tfcoil_variables.a_tf_coil_wp_turn_insulation - error_handling.fdiags[3] = tfcoil_variables.aswp - error_handling.fdiags[4] = sctfcoil_module.a_tf_steel - error_handling.fdiags[5] = sctfcoil_module.f_tf_steel - error_handling.fdiags[6] = sctfcoil_module.a_tf_ins - error_handling.fdiags[7] = sctfcoil_module.f_tf_ins - error_handling.report_error(276) + WarningManager.create_warning( + "One of the areas or fractions is negative in the internal SC TF coil geometry", + acond=tfcoil_variables.acond, + avwp=tfcoil_variables.avwp, + a_tf_coil_wp_turn_insulation=tfcoil_variables.a_tf_coil_wp_turn_insulation, + aswp=tfcoil_variables.aswp, + a_tf_steel=sctfcoil_module.a_tf_steel, + f_tf_steel=sctfcoil_module.f_tf_steel, + a_tf_ins=sctfcoil_module.a_tf_ins, + f_tf_ins=sctfcoil_module.f_tf_ins, + ) def tf_wp_geom(self, i_tf_wp_geom): """ @@ -1911,9 +1928,11 @@ def tf_wp_geom(self, i_tf_wp_geom): # Negative WP area error reporting if sctfcoil_module.awptf <= 0.0e0 or sctfcoil_module.awpc <= 0.0e0: - error_handling.fdiags[0] = sctfcoil_module.awptf - error_handling.fdiags[1] = sctfcoil_module.awpc - error_handling.report_error(99) + WarningManager.create_warning( + "Winding pack cross-section problem", + awptf=sctfcoil_module.awptf, + awpc=sctfcoil_module.awpc, + ) def tf_case_geom(self, i_tf_wp_geom, i_tf_case_geom): """ @@ -1952,9 +1971,11 @@ def tf_case_geom(self, i_tf_wp_geom, i_tf_case_geom): # Report error if the casing area is negative if tfcoil_variables.acasetf <= 0.0e0 or tfcoil_variables.acasetfo <= 0.0e0: - error_handling.fdiags[0] = tfcoil_variables.acasetf - error_handling.fdiags[1] = tfcoil_variables.acasetfo - error_handling.report_error(99) + WarningManager.create_warning( + "Winding pack cross-section problem", + acasetf=tfcoil_variables.acasetf, + acasetfo=tfcoil_variables.acasetfo, + ) # Average lateral casing thickness # -------------- @@ -1998,10 +2019,12 @@ def tf_integer_turn_geom(self, n_layer, n_pancake, thwcndut, thicndut): ) / n_layer if sctfcoil_module.t_turn_radial <= (2.0e0 * thicndut + 2.0e0 * thwcndut): - error_handling.fdiags[0] = sctfcoil_module.t_turn_radial - error_handling.fdiags[1] = thicndut - error_handling.fdiags[2] = thwcndut - error_handling.report_error(100) + WarningManager.create_warning( + "Negative cable space dimension; reduce conduit thicknesses or raise cpttf", + t_turn_radial=sctfcoil_module.t_turn_radial, + thicndut=thicndut, + thwcndut=thwcndut, + ) # Toroidal turn dimension [m] sctfcoil_module.t_turn_toroidal = ( @@ -2010,10 +2033,12 @@ def tf_integer_turn_geom(self, n_layer, n_pancake, thwcndut, thicndut): ) / n_pancake if sctfcoil_module.t_turn_toroidal <= (2.0e0 * thicndut + 2.0e0 * thwcndut): - error_handling.fdiags[0] = sctfcoil_module.t_turn_toroidal - error_handling.fdiags[1] = thicndut - error_handling.fdiags[2] = thwcndut - error_handling.report_error(100) + WarningManager.create_warning( + "Negative cable space dimension; reduce conduit thicknesses or raise cpttf", + t_turn_radial=sctfcoil_module.t_turn_toroidal, + thicndut=thicndut, + thwcndut=thwcndut, + ) tfcoil_variables.t_turn_tf = np.sqrt( sctfcoil_module.t_turn_radial * sctfcoil_module.t_turn_toroidal @@ -2057,15 +2082,19 @@ def tf_integer_turn_geom(self, n_layer, n_pancake, thwcndut, thicndut): if (sctfcoil_module.t_cable_radial < 0.0e0) or ( sctfcoil_module.t_cable_toroidal < 0.0e0 ): - error_handling.fdiags[0] = acstf - error_handling.fdiags[1] = sctfcoil_module.t_cable_radial - error_handling.fdiags[2] = sctfcoil_module.t_cable_toroidal - error_handling.report_error(101) + WarningManager.create_warning( + "Negative cable space dimension", + acstf=acstf, + t_cable_radial=sctfcoil_module.t_cable_radial, + t_cable_toroidal=sctfcoil_module.t_cable_toroidal, + ) else: - error_handling.fdiags[0] = acstf - error_handling.fdiags[1] = sctfcoil_module.t_cable_radial - error_handling.fdiags[1] = sctfcoil_module.t_cable_toroidal - error_handling.report_error(102) + WarningManager.create_warning( + "Cable space area problem; artificially set rounded corner radius to 0", + acstf=acstf, + t_cable_radial=sctfcoil_module.t_cable_radial, + t_cable_toroidal=sctfcoil_module.t_cable_toroidal, + ) sctfcoil_module.rbcndut = 0.0e0 acstf = ( sctfcoil_module.t_cable_radial * sctfcoil_module.t_cable_toroidal @@ -3124,9 +3153,13 @@ def outtf(self, peaktfflag): po.osubhd(self.outfile, "Ripple information:") if tfcoil_variables.i_tf_shape == 1: if peaktfflag == 1: - error_handling.report_error(144) + WarningManager.create_warning( + "(TF coil peak field calculation) Winding pack width out of fitted range" + ) elif peaktfflag == 2: - error_handling.report_error(145) + WarningManager.create_warning( + "(TF coil peak field calculation) Winding pack radial thickness out of fitted range" + ) po.ovarre( self.outfile, @@ -3579,13 +3612,17 @@ def tf_averaged_turn_geom(self, j_tf_wp, thwcndut, thicndut, i_tf_sc_mat): if acstf <= 0.0e0: if tfcoil_variables.t_conductor < 0.0e0: - error_handling.fdiags[0] = acstf - error_handling.fdiags[1] = sctfcoil_module.t_cable - error_handling.report_error(101) + WarningManager.create_warning( + "Negative cable space dimension", + acstf=acstf, + t_cable=sctfcoil_module.t_cable, + ) else: - error_handling.fdiags[0] = acstf - error_handling.fdiags[1] = sctfcoil_module.t_cable - error_handling.report_error(102) + WarningManager.create_warning( + "Cable space area problem; artificially set rounded corner radius to 0", + acstf=acstf, + t_cable=sctfcoil_module.t_cable, + ) rbcndut = 0.0e0 acstf = sctfcoil_module.t_cable**2 diff --git a/process/superconductors.py b/process/superconductors.py index 06fa825623..8999451738 100644 --- a/process/superconductors.py +++ b/process/superconductors.py @@ -4,8 +4,8 @@ from scipy import optimize from process.exceptions import ProcessValueError -from process.fortran import error_handling as eh from process.fortran import rebco_variables +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) @@ -545,18 +545,20 @@ def bottura_scaling( tc0eps = tc0max * strfun ** (1 / 3) if temperature / tc0eps >= 1.0: - eh.fdiags[0] = temperature - eh.fdiags[1] = tc0eps - eh.report_error(159) + WarningManager.create_warning( + "Reduced temperature t artificially lowered", + temperature=temperature, + tc0eps=tc0eps, + ) # Reduced temperature at zero field, corrected for strain # t > 1 is permitted, indicating the temperature is above the critical value at zero field. t = temperature / tc0eps if bmax / bc20eps >= 1.0: - eh.fdiags[0] = bmax - eh.fdiags[1] = bc20eps - eh.report_error(160) + WarningManager.create_warning( + "Reduced field bzero artificially lowered", bmax=bmax, bc20eps=bc20eps + ) # Reduced field at zero temperature, taking account of strain bzero = bmax / bc20eps diff --git a/process/tf_coil.py b/process/tf_coil.py index 7c2bb767dc..d4f0386bd7 100644 --- a/process/tf_coil.py +++ b/process/tf_coil.py @@ -11,7 +11,6 @@ from process.fortran import ( build_variables, constants, - error_handling, fwbs_variables, global_variables, pfcoil_variables, @@ -20,13 +19,13 @@ tfcoil_variables, ) from process.fortran import build_variables as bv -from process.fortran import error_handling as eh from process.fortran import fwbs_variables as fwbsv from process.fortran import tfcoil_variables as tfv from process.utilities.f2py_string_patch import ( f2py_compatible_to_string, string_to_f2py_compatible, ) +from process.warning_handler import WarningManager RMU0 = constants.rmu0 @@ -247,7 +246,9 @@ def run(self, output): ) except ValueError as e: if e.args[1] == 245 and e.args[2] == 0: - error_handling.report_error(245) + WarningManager.create_warning( + "Invalid stress model (r_tf_inboard = 0), stress constraint switched off" + ) tfcoil_variables.sig_tf_case = 0.0e0 tfcoil_variables.sig_tf_wp = 0.0e0 @@ -719,12 +720,18 @@ def cntrpst(self): # If the average conductor temperature difference is negative, set it to 0 if dtcncpav < 0.0e0: - eh.report_error(249) + WarningManager.create_warning( + "Negative conductor average temperature difference, set to 0", + dtcncpav=dtcncpav, + ) dtcncpav = 0.0e0 # If the average conductor temperature difference is negative, set it to 0 if dtconcpmx < 0.0e0: - eh.report_error(250) + WarningManager.create_warning( + "Negative conductor peak temperature difference, set to 0", + dtconcpmx=dtconcpmx, + ) dtconcpmx = 0.0e0 # Average conductor temperature @@ -1008,8 +1015,10 @@ def he_density(temp: float) -> float: # Fit range validation if temp < 4.0e0 or temp > 50.0e0: - eh.fdiags[0] = temp - eh.report_error(257) + WarningManager.create_warning( + "Helium temperature out of helium property fiting range [4-50] K", + temp=temp, + ) # Oder 3 polynomial fit if temp < 29.5e0: @@ -1047,8 +1056,10 @@ def he_cp(temp: float) -> float: # Fit range validation if temp < 4.0e0 or temp > 50.0e0: - eh.fdiags[0] = temp - eh.report_error(257) + WarningManager.create_warning( + "Helium temperature out of helium property fiting range [4-50] K", + temp=temp, + ) # Order 3 polynomial fit in [4-30] K on the dimenion [K/(g.K)] if temp < 29.5e0: @@ -1088,8 +1099,10 @@ def he_visco(temp: float) -> float: """ if temp < 4.0e0 or temp > 50.0e0: - eh.fdiags[0] = temp - eh.report_error(257) + WarningManager.create_warning( + "Helium temperature out of helium property fiting range [4-50] K", + temp=temp, + ) # Order 4 polynomial exponential fit in [4-25] K if temp < 22.5e0: @@ -1127,8 +1140,10 @@ def he_th_cond(temp: float) -> float: # Fit range validation if temp < 4.0e0 or temp > 50.0e0: - eh.fdiags[0] = temp - eh.report_error(257) + WarningManager.create_warning( + "Helium temperature out of helium property fiting range [4-50] K", + temp=temp, + ) # Order 4 polynomial fit if temp < 24.0e0: @@ -1177,8 +1192,10 @@ def al_th_cond(temp: float) -> float: # Fiting range verification if temp < 15.0e0 or temp > 150.0e0: - eh.fdiags[0] = temp - eh.report_error(258) + WarningManager.create_warning( + "Aluminium temperature out of the th conductivity fit range [15-60] K", + temp=temp, + ) # fit 15 < T < 60 K (order 3 poly) if temp < 60.0e0: diff --git a/process/uncertainties/evaluate_uncertainties.py b/process/uncertainties/evaluate_uncertainties.py index 378ac952b6..ec953e28db 100644 --- a/process/uncertainties/evaluate_uncertainties.py +++ b/process/uncertainties/evaluate_uncertainties.py @@ -35,7 +35,6 @@ from process.io.in_dat import InDat from process.io.process_config import UncertaintiesConfig from process.io.process_funcs import ( - check_input_error, get_neqns_itervars, get_variable_range, no_unfeasible_mfile, @@ -110,9 +109,6 @@ def run_monte_carlo(args): input_path = Path(config.wdir) / "IN.DAT" config.run_process(input_path) - # Check for produced MFILE.DAT in working dir - check_input_error(wdir=config.wdir) - if not process_stopped(): no_unfeasible = no_unfeasible_mfile() diff --git a/process/utilities/errorlist.json b/process/utilities/errorlist.json deleted file mode 100644 index 63e10f361e..0000000000 --- a/process/utilities/errorlist.json +++ /dev/null @@ -1,1459 +0,0 @@ -{ - "version": [ - "PROCESS version 2.1.1" - ], - "comment1": [ - "List of error messages used via error_handling.f90" - ], - "comment2": [ - "Increment n_errortypes if an error is added to this list" - ], - "n_errortypes": 289, - "errors": [ - { - "no": 1, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 7 if i_plasma_ignited=1" - }, - { - "no": 2, - "level": 0, - "message": "REMOVED" - }, - { - "no": 3, - "level": 0, - "message": "REMOVED" - }, - { - "no": 4, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 28 if i_plasma_ignited=1" - }, - { - "no": 5, - "level": 3, - "message": "CONSTRAINTS: temp_fw_peak = 0 ==> i_pulsed_plant=0; do not use constraint 39 if i_pulsed_plant=0" - }, - { - "no": 6, - "level": 3, - "message": "CONSTRAINTS: tcycmn = 0 ==> i_pulsed_plant=0; do not use constraint 42 if i_pulsed_plant=0" - }, - { - "no": 7, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 43 if itart=0" - }, - { - "no": 8, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 44 if itart=0" - }, - { - "no": 9, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 45 if itart=0" - }, - { - "no": 10, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 46 if itart=0" - }, - { - "no": 11, - "level": 0, - "message": "REMOVED" - }, - { - "no": 12, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 50 if ife=0" - }, - { - "no": 13, - "level": 3, - "message": "CONSTRAINTS: No such constraint equation number..." - }, - { - "no": 14, - "level": 3, - "message": "CONSTRAINTS: NaN/infty error for constraint equation..." - }, - { - "no": 15, - "level": 0, - "message": "REMOVED" - }, - { - "no": 16, - "level": 2, - "message": "LHRAD: LH penetration radius not found after lapno iterations, using 0.8*rminor" - }, - { - "no": 17, - "level": 0, - "message": "REMOVED" - }, - { - "no": 18, - "level": 0, - "message": "REMOVED" - }, - { - "no": 19, - "level": 0, - "message": "REMOVED" - }, - { - "no": 20, - "level": 0, - "message": "REMOVED" - }, - { - "no": 21, - "level": 0, - "message": "REMOVED" - }, - { - "no": 22, - "level": 0, - "message": "REMOVED" - }, - { - "no": 23, - "level": 0, - "message": "REMOVED" - }, - { - "no": 24, - "level": 0, - "message": "REMOVED" - }, - { - "no": 25, - "level": 0, - "message": "REMOVED" - }, - { - "no": 26, - "level": 0, - "message": "REMOVED" - }, - { - "no": 27, - "level": 0, - "message": "REMOVED" - }, - { - "no": 28, - "level": 0, - "message": "REMOVED" - }, - { - "no": 29, - "level": 2, - "message": "REMOVED" - }, - { - "no": 30, - "level": 0, - "message": "REMOVED" - }, - { - "no": 31, - "level": 0, - "message": "REMOVED" - }, - { - "no": 32, - "level": 0, - "message": "REMOVED" - }, - { - "no": 33, - "level": 0, - "message": "REMOVED" - }, - { - "no": 34, - "level": 0, - "message": "REMOVED" - }, - { - "no": 35, - "level": 2, - "message": "REMOVED" - }, - { - "no": 36, - "level": 0, - "message": "REMOVED" - }, - { - "no": 37, - "level": 2, - "message": "REMOVED" - }, - { - "no": 38, - "level": 0, - "message": "REMOVED" - }, - { - "no": 39, - "level": 1, - "message": "REMOVED" - }, - { - "no": 40, - "level": 2, - "message": "REMOVED" - }, - { - "no": 41, - "level": 0, - "message": "REMOVED" - }, - { - "no": 42, - "level": 0, - "message": "REMOVED" - }, - { - "no": 43, - "level": 0, - "message": "REMOVED" - }, - { - "no": 44, - "level": 0, - "message": "REMOVED" - }, - { - "no": 45, - "level": 0, - "message": "REMOVED" - }, - { - "no": 46, - "level": 0, - "message": "REMOVED" - }, - { - "no": 47, - "level": 0, - "message": "REMOVED" - }, - { - "no": 48, - "level": 0, - "message": "REMOVED" - }, - { - "no": 49, - "level": 0, - "message": "REMOVED" - }, - { - "no": 50, - "level": 3, - "message": "REMOVED" - }, - { - "no": 51, - "level": 0, - "message": "REMOVED" - }, - { - "no": 52, - "level": 0, - "message": "REMOVED" - }, - { - "no": 53, - "level": 0, - "message": "REMOVED" - }, - { - "no": 54, - "level": 0, - "message": "REMOVED" - }, - { - "no": 55, - "level": 0, - "message": "REMOVED" - }, - { - "no": 56, - "level": 0, - "message": "REMOVED" - }, - { - "no": 57, - "level": 3, - "message": "CONVXC: Illegal iteration variable number" - }, - { - "no": 58, - "level": 3, - "message": "CONVXC: Iteration variable is zero; change its initial value or lower bound" - }, - { - "no": 59, - "level": 3, - "message": "CONVXC: NaN error for iteration variable" - }, - { - "no": 60, - "level": 3, - "message": "CONVXC: scale(i) = 0 for iteration variable" - }, - { - "no": 61, - "level": 0, - "message": "REMOVED" - }, - { - "no": 62, - "level": 1, - "message": "RADIALB: Ripple result may be inaccurate, as the fit has been extrapolated" - }, - { - "no": 63, - "level": 2, - "message": "PORTSZ: Max beam tangency radius set =0 temporarily; change beamwd" - }, - { - "no": 64, - "level": 0, - "message": "REMOVED" - }, - { - "no": 65, - "level": 0, - "message": "REMOVED" - }, - { - "no": 66, - "level": 0, - "message": "REMOVED" - }, - { - "no": 67, - "level": 0, - "message": "REMOVED" - }, - { - "no": 68, - "level": 0, - "message": "REMOVED" - }, - { - "no": 69, - "level": 0, - "message": "REMOVED" - }, - { - "no": 70, - "level": 0, - "message": "REMOVED" - }, - { - "no": 71, - "level": 2, - "message": "PFCOIL: OH coil not present; check volt-second calculations..." - }, - { - "no": 72, - "level": 0, - "message": "REMOVED" - }, - { - "no": 73, - "level": 2, - "message": "INDUCT: Max no. of segments noh for OH coil > nohmax; increase dr_cs lower bound" - }, - { - "no": 74, - "level": 0, - "message": "REMOVED" - }, - { - "no": 75, - "level": 0, - "message": "REMOVED" - }, - { - "no": 76, - "level": 0, - "message": "REMOVED" - }, - { - "no": 77, - "level": 0, - "message": "REMOVED" - }, - { - "no": 78, - "level": 2, - "message": "REMOVED" - }, - { - "no": 79, - "level": 0, - "message": "REMOVED" - }, - { - "no": 80, - "level": 2, - "message": "calculate_density_limit: qcyl < 4/3; dlimit(4) set to zero; model 5 will be enforced instead" - }, - { - "no": 81, - "level": 0, - "message": "REMOVED" - }, - { - "no": 82, - "level": 0, - "message": "REMOVED" - }, - { - "no": 83, - "level": 2, - "message": "POHM: Negative plasma resistance res_plasma" - }, - { - "no": 84, - "level": 0, - "message": "REMOVED" - }, - { - "no": 85, - "level": 0, - "message": "REMOVED" - }, - { - "no": 86, - "level": 0, - "message": "REMOVED" - }, - { - "no": 87, - "level": 2, - "message": "OUTPLAS: Possible problem with high radiation power, forcing p_plasma_separatrix_mw to odd values" - }, - { - "no": 88, - "level": 2, - "message": "REMOVED" - }, - { - "no": 89, - "level": 0, - "message": "REMOVED" - }, - { - "no": 90, - "level": 2, - "message": "REMOVED" - }, - { - "no": 91, - "level": 0, - "message": "REMOVED" - }, - { - "no": 92, - "level": 2, - "message": "REMOVED" - }, - { - "no": 93, - "level": 2, - "message": "BURN: Negative burn time available; reduce t_fusion_ramp or raise PF coil V-s capability" - }, - { - "no": 94, - "level": 0, - "message": "REMOVED" - }, - { - "no": 95, - "level": 0, - "message": "REMOVED" - }, - { - "no": 96, - "level": 0, - "message": "REMOVED" - }, - { - "no": 97, - "level": 2, - "message": "REMOVED" - }, - { - "no": 98, - "level": 2, - "message": "REMOVED" - }, - { - "no": 99, - "level": 2, - "message": "SCTFCOIL: Winding pack cross-section problem..." - }, - { - "no": 100, - "level": 2, - "message": "SCTFCOIL: Negative cable space dimension; reduce conduit thicknesses or raise cpttf" - }, - { - "no": 101, - "level": 2, - "message": "SCTFCOIL: Negative cable space dimension" - }, - { - "no": 102, - "level": 2, - "message": "SCTFCOIL: Cable space area problem; artificially set rounded corner radius to 0" - }, - { - "no": 103, - "level": 2, - "message": "REMOVED" - }, - { - "no": 104, - "level": 0, - "message": "REMOVED" - }, - { - "no": 105, - "level": 0, - "message": "REMOVED" - }, - { - "no": 106, - "level": 2, - "message": "REMOVED" - }, - { - "no": 107, - "level": 0, - "message": "REMOVED" - }, - { - "no": 108, - "level": 0, - "message": "REMOVED" - }, - { - "no": 109, - "level": 0, - "message": "REMOVED" - }, - { - "no": 110, - "level": 0, - "message": "REMOVED" - }, - { - "no": 111, - "level": 2, - "message": "INTERSECT: X ranges not overlapping" - }, - { - "no": 112, - "level": 2, - "message": "INTERSECT: X has dropped below Xmin; X has been set equal to Xmin" - }, - { - "no": 113, - "level": 2, - "message": "INTERSECT: X has risen above Xmax; X has been set equal to Xmax" - }, - { - "no": 114, - "level": 2, - "message": "INTERSECT: Convergence too slow; X may be wrong..." - }, - { - "no": 115, - "level": 0, - "message": "REMOVED" - }, - { - "no": 116, - "level": 0, - "message": "REMOVED" - }, - { - "no": 117, - "level": 0, - "message": "REMOVED" - }, - { - "no": 118, - "level": 0, - "message": "REMOVED" - }, - { - "no": 119, - "level": 0, - "message": "REMOVED" - }, - { - "no": 120, - "level": 0, - "message": "REMOVED" - }, - { - "no": 121, - "level": 0, - "message": "REMOVED" - }, - { - "no": 122, - "level": 2, - "message": "REMOVED" - }, - { - "no": 123, - "level": 0, - "message": "REMOVED" - }, - { - "no": 124, - "level": 2, - "message": "VACUUM: Newton's method not converging; check fusion power, te" - }, - { - "no": 125, - "level": 0, - "message": "REMOVED" - }, - { - "no": 126, - "level": 0, - "message": "REMOVED" - }, - { - "no": 127, - "level": 0, - "message": "REMOVED" - }, - { - "no": 128, - "level": 0, - "message": "REMOVED" - }, - { - "no": 129, - "level": 0, - "message": "REMOVED" - }, - { - "no": 130, - "level": 0, - "message": "REMOVED" - }, - { - "no": 131, - "level": 2, - "message": "REMOVED" - }, - { - "no": 132, - "level": 2, - "message": "DOOPT: Optimisation solver VMCON returns with ifail /= 1" - }, - { - "no": 133, - "level": 2, - "message": "REMOVED" - }, - { - "no": 134, - "level": 2, - "message": "DOOPT: High final VMCON constraint residues" - }, - { - "no": 135, - "level": 1, - "message": "OUTPF: CS not using max current density: further optimisation may be possible" - }, - { - "no": 136, - "level": 2, - "message": "REMOVED" - }, - { - "no": 137, - "level": 0, - "message": "REMOVED" - }, - { - "no": 138, - "level": 2, - "message": "REMOVED" - }, - { - "no": 139, - "level": 0, - "message": "REMOVED" - }, - { - "no": 140, - "level": 0, - "message": "REMOVED" - }, - { - "no": 141, - "level": 1, - "message": "RADIALB: (TF coil ripple calculation) Dimensionless coil width X out of fitted range" - }, - { - "no": 142, - "level": 1, - "message": "RADIALB: (TF coil ripple calculation) No of TF coils not between 16 and 20 inclusive" - }, - { - "no": 143, - "level": 1, - "message": "RADIALB: (TF coil ripple calculation) (R+a)/rtot out of fitted range" - }, - { - "no": 144, - "level": 1, - "message": "OUTTF: (TF coil peak field calculation) Winding pack width out of fitted range" - }, - { - "no": 145, - "level": 1, - "message": "OUTTF: (TF coil peak field calculation) Winding pack radial thickness out of fitted range" - }, - { - "no": 146, - "level": 0, - "message": "REMOVED" - }, - { - "no": 147, - "level": 2, - "message": "REMOVED" - }, - { - "no": 148, - "level": 2, - "message": "REMOVED" - }, - { - "no": 149, - "level": 2, - "message": "REMOVED" - }, - { - "no": 150, - "level": 2, - "message": "REMOVED" - }, - { - "no": 151, - "level": 0, - "message": "REMOVED" - }, - { - "no": 152, - "level": 2, - "message": "REMOVED" - }, - { - "no": 153, - "level": 2, - "message": "REMOVED" - }, - { - "no": 154, - "level": 2, - "message": "REMOVED" - }, - { - "no": 155, - "level": 2, - "message": "REMOVED" - }, - { - "no": 156, - "level": 0, - "message": "REMOVED" - }, - { - "no": 157, - "level": 2, - "message": "REMOVED" - }, - { - "no": 158, - "level": 2, - "message": "REMOVED" - }, - { - "no": 159, - "level": 2, - "message": "ITERSC: Reduced temperature t artificially lowered" - }, - { - "no": 160, - "level": 2, - "message": "ITERSC: Reduced field bzero artificially lowered" - }, - { - "no": 161, - "level": 2, - "message": "REMOVED" - }, - { - "no": 162, - "level": 0, - "message": "REMOVED" - }, - { - "no": 163, - "level": 0, - "message": "REMOVED" - }, - { - "no": 164, - "level": 0, - "message": "REMOVED" - }, - { - "no": 165, - "level": 0, - "message": "REMOVED" - }, - { - "no": 166, - "level": 2, - "message": "PLANT_THERMAL_EFFICIENCY: Turbine temperature tturb out of range of validity" - }, - { - "no": 167, - "level": 2, - "message": "REMOVED" - }, - { - "no": 168, - "level": 2, - "message": "REMOVED" - }, - { - "no": 169, - "level": 2, - "message": "REMOVED" - }, - { - "no": 170, - "level": 2, - "message": "REMOVED" - }, - { - "no": 171, - "level": 2, - "message": "REMOVED" - }, - { - "no": 172, - "level": 2, - "message": "REMOVED" - }, - { - "no": 173, - "level": 3, - "message": "CONSTRAINTS: Do not use constraint 55 if i_blanket_type != 2" - }, - { - "no": 174, - "level": 0, - "message": "REMOVED" - }, - { - "no": 175, - "level": 0, - "message": "REMOVED" - }, - { - "no": 176, - "level": 0, - "message": "REMOVED" - }, - { - "no": 177, - "level": 0, - "message": "REMOVED" - }, - { - "no": 178, - "level": 0, - "message": "REMOVED" - }, - { - "no": 179, - "level": 0, - "message": "REMOVED" - }, - { - "no": 180, - "level": 0, - "message": "REMOVED" - }, - { - "no": 181, - "level": 0, - "message": "REMOVED" - }, - { - "no": 182, - "level": 0, - "message": "REMOVED" - }, - { - "no": 183, - "level": 0, - "message": "REMOVED" - }, - { - "no": 184, - "level": 0, - "message": "REMOVED" - }, - { - "no": 185, - "level": 0, - "message": "REMOVED" - }, - { - "no": 186, - "level": 0, - "message": "REMOVED" - }, - { - "no": 187, - "level": 0, - "message": "REMOVED" - }, - { - "no": 188, - "level": 0, - "message": "REMOVED" - }, - { - "no": 189, - "level": 0, - "message": "REMOVED" - }, - { - "no": 190, - "level": 0, - "message": "REMOVED" - }, - { - "no": 191, - "level": 0, - "message": "REMOVED" - }, - { - "no": 192, - "level": 0, - "message": "REMOVED" - }, - { - "no": 193, - "level": 0, - "message": "REMOVED" - }, - { - "no": 194, - "level": 0, - "message": "REMOVED" - }, - { - "no": 195, - "level": 0, - "message": "REMOVED" - }, - { - "no": 196, - "level": 0, - "message": "REMOVED" - }, - { - "no": 197, - "level": 0, - "message": "REMOVED" - }, - { - "no": 198, - "level": 0, - "message": "REMOVED" - }, - { - "no": 199, - "level": 0, - "message": "REMOVED" - }, - { - "no": 200, - "level": 0, - "message": "REMOVED" - }, - { - "no": 201, - "level": 1, - "message": "LH THRESHOLD: bt outside Snipes 2000 fitted range" - }, - { - "no": 202, - "level": 1, - "message": "LH THRESHOLD: rminor outside Snipes 2000 fitted range" - }, - { - "no": 203, - "level": 1, - "message": "LH THRESHOLD: rmajor outside Snipes 2000 fitted range" - }, - { - "no": 204, - "level": 1, - "message": "LH THRESHOLD: dnla outside Snipes 2000 fitted range" - }, - { - "no": 205, - "level": 1, - "message": "LH THRESHOLD: kappa outside Snipes 2000 fitted range" - }, - { - "no": 206, - "level": 1, - "message": "LH THRESHOLD: triang outside Snipes 2000 fitted range" - }, - { - "no": 207, - "level": 1, - "message": "LH THRESHOLD: Closed divertor only. Limited data used in Snipes fit" - }, - { - "no": 208, - "level": 0, - "message": "REMOVED" - }, - { - "no": 209, - "level": 0, - "message": "REMOVED" - }, - { - "no": 210, - "level": 0, - "message": "REMOVED" - }, - { - "no": 211, - "level": 0, - "message": "REMOVED" - }, - { - "no": 212, - "level": 0, - "message": "REMOVED" - }, - { - "no": 213, - "level": 2, - "message": "NCORE: Central density is less than pedestal density" - }, - { - "no": 214, - "level": 0, - "message": "REMOVED" - }, - { - "no": 215, - "level": 0, - "message": "REMOVED" - }, - { - "no": 216, - "level": 0, - "message": "REMOVED" - }, - { - "no": 217, - "level": 2, - "message": "REINKE IMPURITY MODEL: fzmin is greater than or equal to 1.0, this is at least notable" - }, - { - "no": 218, - "level": 2, - "message": "REMOVED" - }, - { - "no": 219, - "level": 0, - "message": "REMOVED" - }, - { - "no": 220, - "level": 1, - "message": "REMOVED" - }, - { - "no": 221, - "level": 2, - "message": "REMOVED" - }, - { - "no": 222, - "level": 0, - "message": "REMOVED" - }, - { - "no": 223, - "level": 2, - "message": "[fw.f90][fw_temp] NaN first wall temperature " - }, - { - "no": 224, - "level": 2, - "message": "[fw.f90][fw_temp] First wall temperature (temp_k) out of range : [100-1500] K" - }, - { - "no": 225, - "level": 2, - "message": "[fw.f90][heat_transfer] Reynolds number out of range : [3e3-5000e3]" - }, - { - "no": 226, - "level": 2, - "message": "[fw.f90][heat_transfer] Prandtl number out of range : [0.5-2000]" - }, - { - "no": 227, - "level": 2, - "message": "[fw.f90][heat_transfer] Negative Darcy friction factor (f)" - }, - { - "no": 228, - "level": 0, - "message": "REMOVED" - }, - { - "no": 229, - "level": 2, - "message": "REMOVED" - }, - { - "no": 230, - "level": 0, - "message": "REMOVED" - }, - { - "no": 231, - "level": 0, - "message": "REMOVED" - }, - { - "no": 232, - "level": 0, - "message": "REMOVED" - }, - { - "no": 233, - "level": 1, - "message": "REMOVED" - }, - { - "no": 234, - "level": 0, - "message": "REMOVED" - }, - { - "no": 235, - "level": 0, - "message": "REMOVED" - }, - { - "no": 236, - "level": 3, - "message": "CHECK: The 1/R toroidal B field dependency constraint is being depreciated." - }, - { - "no": 237, - "level": 2, - "message": "REMOVED" - }, - { - "no": 238, - "level": 0, - "message": "REMOVED" - }, - { - "no": 239, - "level": 0, - "message": "REMOVED" - }, - { - "no": 240, - "level": 0, - "message": "REMOVED" - }, - { - "no": 241, - "level": 0, - "message": "REMOVED" - }, - { - "no": 242, - "level": 2, - "message": "PHYSICS: Bootstrap fraction upper limit enforced" - }, - { - "no": 243, - "level": 2, - "message": "PHYSICS: Predicted plasma driven current is more than upper limit on non-inductive fraction." - }, - { - "no": 244, - "level": 2, - "message": "PHYSICS: Diamagnetic fraction is more than 1%, but not calculated. Consider using i_diamagnetic_current=2 and i_pfirsch_schluter_current=1." - }, - { - "no": 245, - "level": 2, - "message": "[sctfcoil] Invalid stress model (r_tf_inboard = 0), stress constraint switched off" - }, - { - "no": 246, - "level": 0, - "message": "REMOVED" - }, - { - "no": 247, - "level": 0, - "message": "REMOVED" - }, - { - "no": 248, - "level": 0, - "message": "REMOVED" - }, - { - "no": 249, - "level": 2, - "message": "[tfcoil][cntrpst] Negative conductor average temperature difference, set to 0" - }, - { - "no": 250, - "level": 2, - "message": "[tfcoil][cntrpst] Negative conductor peak temperature difference, set to 0" - }, - { - "no": 251, - "level": 0, - "message": "REMOVED" - }, - { - "no": 252, - "level": 0, - "message": "REMOVED" - }, - { - "no": 253, - "level": 0, - "message": "REMOVED" - }, - { - "no": 254, - "level": 0, - "message": "REMOVED" - }, - { - "no": 255, - "level": 0, - "message": "REMOVED" - }, - { - "no": 256, - "level": 2, - "message": "CHECK: Top CP radius larger that its value determined with plasma shape" - }, - { - "no": 257, - "level": 2, - "message": "[tfcoil] : Helium temperature out of helium property fiting range [4-50] K" - }, - { - "no": 258, - "level": 2, - "message": "[tfcoil] : Aluminium temperature out of the th conductivity fit range [15-60] K" - }, - { - "no": 259, - "level": 0, - "message": "REMOVED" - }, - { - "no": 260, - "level": 0, - "message": "REMOVED" - }, - { - "no": 261, - "level": 2, - "message": "[tfcoil]: TF strain was outside the region of applicability. Used lower strain" - }, - { - "no": 262, - "level": 2, - "message": "REBCO : Non physical strain used in CS. Use superconductor strain < +/- 0.7% " - }, - { - "no": 263, - "level": 2, - "message": "REBCO : Non physical strain used in PF. Use superconductor strain < +/- 0.7% " - }, - { - "no": 264, - "level": 0, - "message": "REMOVED" - }, - { - "no": 265, - "level": 0, - "message": "REMOVED" - }, - { - "no": 266, - "level": 2, - "message": "REBCO : Field on superconductor > 14 T (outside of interpolation range)" - }, - { - "no": 267, - "level": 0, - "message": "REMOVED" - }, - { - "no": 268, - "level": 2, - "message": "[radialb]: TF CP top radius (r_cp_top) replaced by 1.01*r_tf_inboard_out -> potential top rbuild issue" - }, - { - "no": 269, - "level": 0, - "message": "REMOVED" - }, - { - "no": 270, - "level": 0, - "message": "REMOVED" - }, - { - "no": 271, - "level": 0, - "message": "REMOVED" - }, - { - "no": 272, - "level": 2, - "message": "REMOVED" - }, - { - "no": 273, - "level": 0, - "message": "REMOVED" - }, - { - "no": 274, - "level": 2, - "message": "[HCPB]: Blanket heating is <1 MW or NaN. Is something wrong?" - }, - { - "no": 275, - "level": 0, - "message": "REMOVED" - }, - { - "no": 276, - "level": 2, - "message": "CHECK: One of the areas or fractions is negative in the internal SC TF coil geometry" - }, - { - "no": 277, - "level": 2, - "message": "CHECK: One or more collision between TF and PF coils. Check PF placement." - }, - { - "no": 278, - "level": 2, - "message": "CHECK: Your blanket modules are too small for the Liquid Metal pipes." - }, - { - "no": 279, - "level": 0, - "message": "REMOVED" - }, - { - "no": 280, - "level": 2, - "message": "CHECK: Outside temperature limit for one or more liquid metal breeder properties." - }, - { - "no": 281, - "level": 0, - "message": "REMOVED" - }, - { - "no": 282, - "level": 2, - "message": "CHECK: ncore is going negative when solving. Please raise the value of dene and or its lower limit." - }, - { - "no": 283, - "level": 0, - "message": "REMOVED" - }, - { - "no": 284, - "level": 0, - "message": "REMOVED" - }, - { - "no": 285, - "level": 2, - "message": "[tfcoil]: fiooic shouldn't be above 0.7 for engineering reliability." - }, - { - "no": 286, - "level": 2, - "message": "[pfcoil][cntrpost]: fjohc shouldn't be above 0.7 for engineering reliability." - }, - { - "no": 287, - "level": 2, - "message": "[pfcoil][cntrpost]: fjohc0 shouldn't be above 0.7 for engineering reliability." - }, - { - "no": 288, - "level": 0, - "message": "REMOVED" - }, - { - "no": 289, - "level": 3, - "message": "CHECK: Constraint equation 22 has been deprecated." - } - ] -} diff --git a/process/vacuum.py b/process/vacuum.py index 1ec7163b05..d1873c02aa 100644 --- a/process/vacuum.py +++ b/process/vacuum.py @@ -6,7 +6,6 @@ from process import process_output as po from process.fortran import build_variables as buv from process.fortran import constants -from process.fortran import error_handling as eh from process.fortran import physics_variables as pv from process.fortran import tfcoil_variables as tfv from process.fortran import times_variables as tv @@ -15,6 +14,7 @@ f2py_compatible_to_string, string_to_f2py_compatible, ) +from process.warning_handler import WarningManager logger = logging.getLogger(__name__) @@ -448,9 +448,11 @@ def vacuum( break else: - eh.fdiags[0] = pv.fusion_power - eh.fdiags[1] = pv.te - eh.report_error(124) + WarningManager.create_warning( + "Newton's method not converging; check fusion power, te", + fusion_power=pv.fusion_power, + te=pv.te, + ) theta = math.pi / ntf diff --git a/process/warning_handler.py b/process/warning_handler.py new file mode 100644 index 0000000000..b0c6b33f28 --- /dev/null +++ b/process/warning_handler.py @@ -0,0 +1,65 @@ +import inspect +from dataclasses import dataclass +from typing import ClassVar + +import process.process_output as process_output +from process.fortran import constants + + +class ProcessUserWarning(UserWarning): + pass + + +@dataclass +class ProcessWarning: + msg: str + location: str + + +class WarningManager: + _warnings: ClassVar[list[ProcessWarning]] = [] + + def __init__(self): + raise NotImplementedError(f"{self.__class__.__name__} cannot be instantiated.") + + @classmethod + def create_warning(cls, msg: str, **diagnostics): + caller = inspect.stack()[1] + warning = ProcessWarning( + f"{msg}" + + ("\n\t" if diagnostics else "") + + "\n\t".join([f"\t{d}: {v!r}" for d, v in [*diagnostics.items()]]), + f"{caller.filename} @ line {caller.lineno}", + ) + + # will not add the warning again if it has the same message and line number + if warning not in cls._warnings: + cls._warnings.append(warning) + + @classmethod + def warnings(cls): + return cls._warnings + + @classmethod + def reinitialise(cls): + cls._warnings = [] + + @classmethod + def show_errors(cls, file_unit: int): + warning_string = ( + "******************************************** Errors and Warnings *********************************************" + f"\n{cls.warning_string()}" + ) + print(warning_string) + process_output.write(file_unit, warning_string) + process_output.ovarre( + constants.mfile, + "Error status", + "error_status", + 2 if len(WarningManager.warnings()) > 0 else 0, + "OP", + ) + + @classmethod + def warning_string(cls): + return "\n\n".join([f"({w.location}) {w.msg}" for w in cls._warnings]) diff --git a/source/fortran/constraint_equations.f90 b/source/fortran/constraint_equations.f90 deleted file mode 100755 index 1a0bb7e502..0000000000 --- a/source/fortran/constraint_equations.f90 +++ /dev/null @@ -1,3442 +0,0 @@ -! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -module constraints - !! author: J Morris - !! - !! Module defining the constraint equations and the routine that evaluates the - !! constraint equations. - - ! Import modules -#ifndef dp - use, intrinsic :: iso_fortran_env, only: dp=>real64 -#endif - use error_handling, only: report_error, idiags, fdiags - - implicit none - - public :: constraint_eqns - -! type constraint_args_type -! real(dp) :: cc -! !! Residual error in constraint equation -! real(dp) :: con -! !! constraint value for constraint equation in physical units -! real(dp) :: err -! !! residual error in constraint equation in physical units -! character(len=1) :: symbol -! !! `=<`, `>`, `<` symbol for constraint equation denoting its type -! character(len=10) :: units -! !! constraint units in constraint equation -! end type - -contains - - subroutine constraint_eqns(m,ieqn,cc,con,err,symbol,units) - !! Routine that formulates the constraint equations - !! - !! **author: P J Knight** (UKAEA) - !! - !! **author: J Morris** (UKAEA) - !! - !! if `ieqn` is zero or negative, evaluate all the constraint equations, otherwise - !! evaluate only the `ieqn`th equation. The code attempts to make \( cc(i) = 0 \) for all - !! \( i \in \{1,\cdots,m\} \) equations. All relevant consistency equations should be active in - !! order to make a self-consistent machine. - !! - !! **References** - !! - !! 1. - use numerics, only: icc - - implicit none - - integer, intent(in) :: m - !! Number of constraint equations to solve - - integer, intent(in) :: ieqn - !! Switch for constraint equations to evaluate; - - real(dp), dimension(m), intent(out) :: cc - !! Residual error in equation i - - real(dp), optional, dimension(m), intent(out) :: con - !! constraint value for equation i in physical units - - real(dp), optional, dimension(m), intent(out) :: err - !! residual error in equation i in physical units - - character(len=1), optional, dimension(m), intent(out) :: symbol - !! `=<`, `>`, `<` symbol for equation i denoting its type - - character*10, optional, dimension(m), intent(out) :: units - !! constraint units in equation i - - ! Local variables - integer :: i, i1, i2 - - real(dp) :: tmp_cc = 0 - !! Residual error in constraint equation - real(dp) :: tmp_con = 0 - !! constraint value for constraint equation in physical units - real(dp) :: tmp_err = 0 - !! residual error in constraint equation in physical units - character(len=1) :: tmp_symbol = ' ' - !! `=<`, `>`, `<` symbol for constraint equation denoting its type - character(len=10) :: tmp_units = ' ' - !! constraint units in constraint equation - - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - ! If ieqn is positive, only evaluate the 'ieqn'th constraint residue, - ! otherwise evaluate all m constraint residues - if (ieqn > 0) then - i1 = ieqn ; i2 = ieqn - else - i1 = 1 ; i2 = m - end if - - ! Consistency (equality) constraints should converge to zero. - do i = i1,i2 - - ! The constraint value in physical units is - ! a) for consistency equations, the quantity to be equated, or - ! b) for limit equations, the limiting value. - ! The symbol is = for a consistency equation, < for an upper limit - ! or > for a lower limit. - select case (icc(i)) - - ! Relationship between beta, temperature (keV) and density - case (1); call constraint_eqn_001(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Global plasma power balance equation - case (2); call constraint_eqn_002(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Global power balance equation for ions - case (3); call constraint_eqn_003(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Global power balance equation for electrons - case (4); call constraint_eqn_004(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for density upper limit - case (5); call constraint_eqn_005(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for epsilon beta-poloidal upper limit - case (6); call constraint_eqn_006(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for hot beam ion density - case (7); call constraint_eqn_007(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for neutron wall load upper limit - case (8); call constraint_eqn_008(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for fusion power upper limit - case (9); call constraint_eqn_009(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Obsolete - case (10); call constraint_eqn_010(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for radial build - case (11); call constraint_eqn_011(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for volt-second capability lower limit - case (12); call constraint_eqn_012(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for burn time lower limit - case (13); call constraint_eqn_013(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation to fix number of NBI decay lengths to plasma centre - case (14); call constraint_eqn_014(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for L-H power threshold limit - case (15); call constraint_eqn_015(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for net electric power lower limit - case (16); call constraint_eqn_016(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for radiation power upper limit - case (17); call constraint_eqn_017(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for divertor heat load upper limit - case (18); call constraint_eqn_018(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for MVA upper limit - case (19); call constraint_eqn_019(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for neutral beam tangency radius upper limit - case (20); call constraint_eqn_020(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for minor radius lower limit - case (21); call constraint_eqn_021(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Obsolete - case (22); call constraint_eqn_022(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for conducting shell radius / rminor upper limit - case (23); call constraint_eqn_023(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for beta upper limit - case (24); call constraint_eqn_024(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for peak toroidal field upper limit - case (25); call constraint_eqn_025(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for Central Solenoid current density upper limit at EOF - case (26); call constraint_eqn_026(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for Central Solenoid current density upper limit at BOP - case (27); call constraint_eqn_027(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for fusion gain (big Q) lower limit - case (28); call constraint_eqn_028(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for inboard major radius - case (29); call constraint_eqn_029(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for injection power upper limit - case (30); call constraint_eqn_030(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for TF coil case stress upper limit (SCTF) - case (31); call constraint_eqn_031(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for TF coil conduit stress upper limit (SCTF) - case (32); call constraint_eqn_032(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for TF coil operating/critical J upper limit (SCTF) - case (33); call constraint_eqn_033(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for TF coil dump voltage upper limit (SCTF) - case (34); call constraint_eqn_034(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for TF coil J_wp/J_prot upper limit (SCTF) - case (35); call constraint_eqn_035(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for TF coil s/c temperature margin lower limit (SCTF) - case (36); call constraint_eqn_036(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for current drive gamma upper limit - case (37); call constraint_eqn_037(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for first wall temperature upper limit - case (39); call constraint_eqn_039(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for auxiliary power lower limit - case (40); call constraint_eqn_040(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for plasma current ramp-up time lower limit - case (41); call constraint_eqn_041(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for cycle time lower limit - case (42); call constraint_eqn_042(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for average centrepost temperature - case (43); call constraint_eqn_043(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for centrepost temperature upper limit (TART) - case (44); call constraint_eqn_044(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for edge safety factor lower limit (TART) - case (45); call constraint_eqn_045(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for Ip/Irod upper limit (TART) - case (46); call constraint_eqn_046(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for TF coil toroidal thickness upper limit - case (47); call constraint_eqn_047(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for poloidal beta upper limit - case (48); call constraint_eqn_048(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Issue #508 Remove RFP option Equation to ensure reversal parameter F is negative - case (49); call constraint_eqn_049(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! IFE option: Equation for repetition rate upper limit - case (50); call constraint_eqn_050(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation to enforce startup flux = available startup flux - case (51); call constraint_eqn_051(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for tritium breeding ratio lower limit - case (52); call constraint_eqn_052(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for fast neutron fluence on TF coil upper limit - case (53); call constraint_eqn_053(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for peak TF coil nuclear heating upper limit - case (54); call constraint_eqn_054(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for helium concentration in vacuum vessel upper limit - case (55); call constraint_eqn_055(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for power through separatrix / major radius upper limit - case (56); call constraint_eqn_056(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Obsolete - case (57); call constraint_eqn_057(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Obsolete - case (58); call constraint_eqn_058(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for neutral beam shine-through fraction upper limit - case (59); call constraint_eqn_059(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for Central Solenoid s/c temperature margin lower limit - case (60); call constraint_eqn_060(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for availability limit - case (61); call constraint_eqn_061(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement times - case (62); call constraint_eqn_062(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Upper limit on niterpump (vacuum_model = simple) - case (63); call constraint_eqn_063(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Upper limit on Zeff - case (64); call constraint_eqn_064(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Limit TF dump time to calculated quench time - case (65); call constraint_eqn_065(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Limit on rate of change of energy in poloidal field - case (66); call constraint_eqn_066(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Simple upper limit on radiation wall load - case (67); call constraint_eqn_067(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! New Psep scaling (PsepB/qAR) - case (68); call constraint_eqn_068(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Ensure separatrix power is less than value from Kallenbach divertor - case (69); call constraint_eqn_069(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Separatrix temperature consistency - case (70); call constraint_eqn_070(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Separatrix density consistency - case (71); call constraint_eqn_071(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Central Solenoid Tresca yield criterion - case (72); call constraint_eqn_072(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! ensure separatrix power is greater than the L-H power + auxiliary power - case (73); call constraint_eqn_073(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! ensure TF coil quench temperature < tmax_croco - case (74); call constraint_eqn_074(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! ensure that TF coil current / copper area < Maximum value ONLY used for croco HTS coil - case (75); call constraint_eqn_075(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Eich critical separatrix density model - case (76); call constraint_eqn_076(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for maximum TF current per turn upper limit - case (77); call constraint_eqn_077(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for Reinke criterion, divertor impurity fraction lower limit - case (78); call constraint_eqn_078(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Equation for maximum CS field - case (79); call constraint_eqn_079(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Lower limit p_plasma_separatrix_mw - case (80); call constraint_eqn_080(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint equation making sure that ne(0) > ne(ped) - case (81); call constraint_eqn_081(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint equation making sure that stellarator coils dont touch in toroidal direction - case (82); call constraint_eqn_082(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint ensuring radial build consistency for stellarators - case (83); call constraint_eqn_083(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for lower limit of beta - case (84); call constraint_eqn_084(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for CP lifetime - case (85); call constraint_eqn_085(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for turn dimension - case (86); call constraint_eqn_086(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for cryogenic power - case (87); call constraint_eqn_087(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for TF coil strain - case (88); call constraint_eqn_088(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! ensure that OH coil current / copper area < Maximum value ONLY used for croco HTS coil - case (89); call constraint_eqn_089(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for minimum CS stress load cycles - case (90); call constraint_eqn_090(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for indication of ECRH ignitability - case (91); call constraint_eqn_091(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - ! Constraint for D/T ratio - case (92); call constraint_eqn_092(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - case default - - idiags(1) = icc(i) - call report_error(13) - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = ' ' - tmp_units = ' ' - - end select - - cc(i) = tmp_cc - if (present(con)) then - con(i) = tmp_con - if (present(err)) err(i) = tmp_err - if (present(symbol)) symbol(i) = tmp_symbol - if (present(units)) units(i) = tmp_units - end if - - if (isnan(cc(i)).or.(abs(cc(i))>9.99D99)) then - - ! Add debugging lines as appropriate... - select case (icc(i)) - - ! Relationship between beta, temperature (keV) and density (consistency equation) - case (1); call constraint_err_001() - ! Equation for net electric power lower limit - case (16); call constraint_err_016() - ! Equation for injection power upper limit - case (30); call constraint_err_030() - ! Limit on rate of change of energy in poloidal field - case (66); call constraint_err_066() - - end select - - idiags(1) = icc(i) ; fdiags(1) = cc(i) - call report_error(14) - - end if - - end do - ! Issue 505 Reverse the sign so it works as an inequality constraint (cc(i) > 0) - ! This will have no effect if it is used as an equality constraint because it will be squared. - cc = -cc - - end subroutine constraint_eqns - - !--- Error-handling routines - - subroutine constraint_err_001() - !! Error in: Relationship between beta, temperature (keV) and density (consistency equation) - !! author: P B Lloyd, CCFE, Culham Science Centre - use physics_variables, only: beta_fast_alpha, beta_beam, dene, ten, nd_ions_total, tin, btot, beta - write(*,*) 'beta_fast_alpha = ', beta_fast_alpha - write(*,*) 'beta_beam = ', beta_beam - write(*,*) 'dene = ', dene - write(*,*) 'ten = ', ten - write(*,*) 'nd_ions_total = ', nd_ions_total - write(*,*) 'tin = ', tin - write(*,*) 'btot = ',btot - write(*,*) 'beta = ', beta - end subroutine - - subroutine constraint_err_016() - !! Error in: Equation for net electric power lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - use constraint_variables, only: fpnetel, pnetelin - use heat_transport_variables, only: pnetelmw - implicit none - write(*,*) 'fpnetel = ', fpnetel - write(*,*) 'pnetelmw = ', pnetelmw - write(*,*) 'pnetelin = ', pnetelin - end subroutine - - subroutine constraint_err_030() - !! Error in: Equation for injection power upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - use current_drive_variables, only: p_hcd_injected_total_mw, p_hcd_injected_max - use constraint_variables, only: fpinj - implicit none - write(*,*) 'fpinj = ', fpinj - write(*,*) 'p_hcd_injected_max = ', p_hcd_injected_max - write(*,*) 'p_hcd_injected_total_mw = ', p_hcd_injected_total_mw - end subroutine - - subroutine constraint_err_066() - !! Error in: Limit on rate of change of energy in poloidal field - !! author: P B Lloyd, CCFE, Culham Science Centre - use constraint_variables, only: fpoloidalpower - use pf_power_variables, only: maxpoloidalpower, peakpoloidalpower - implicit none - write(*,*) 'fpoloidalpower = ', fpoloidalpower - write(*,*) 'maxpoloidalpower = ', maxpoloidalpower - write(*,*) 'peakpoloidalpower = ', peakpoloidalpower - end subroutine constraint_err_066 - - !--- - - subroutine constraint_eqn_001(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! author: J Morris - !! category: equality constraint - !! - !! Relationship between beta, temperature (keV) and density - !! - !! \begin{equation} - !! c_i = 1 - \frac{1}{\beta}\left( \beta_{ft} + \beta_{NBI} + 2 \times 10^3 \mu_0 e - !! \left( \frac{n_e T_e + n_i T_i}{B_{tot}^2} \right) \right) - !! \end{equation} - !! - !! - \( \beta \) -- total plasma beta - !! - \( \beta_{ft} \) -- fast alpha beta component - !! - \( \beta_{NBI} \) -- neutral beam beta component - !! - \( n_e \) -- electron density [m\(^3\)] - !! - \( n_i \) -- total ion density [m\(^3\)] - !! - \( T_e \) -- density weighted average electron temperature [keV] - !! - \( T_i \) -- density weighted average ion temperature [keV] - !! - \( B_{tot} \) -- total toroidal + poloidal field [T] - - use physics_variables, only: beta_fast_alpha, beta_beam, dene, ten, nd_ions_total, tin, btot, beta - use constants, only: electron_charge,rmu0 - - implicit none - - ! type(constraint_args_type), intent(out) :: args - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - !! constraint derived type - - tmp_cc = 1.0D0 - (beta_fast_alpha + beta_beam + & - 2.0D3*rmu0*electron_charge * (dene*ten + nd_ions_total*tin)/btot**2 )/beta - tmp_con = beta * (1.0D0 - tmp_cc) - tmp_err = beta * tmp_cc - tmp_symbol = '=' - tmp_units = '' - - end subroutine constraint_eqn_001 - - subroutine constraint_eqn_002(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! author: J. Morris - !! category: equality constraint - !! - !! Global plasma power balance equation - !! - !! \begin{equation} c_i = - !! \end{equation} - !! - !! i_rad_loss : input integer : switch for radiation loss term usage in power balance (see User Guide):
    - !!
  • = 0 total power lost is scaling power plus radiation (needed for ipedestal=2,3) - !!
  • = 1 total power lost is scaling power plus core radiation only - !!
  • = 2 total power lost is scaling power only, with no additional - !! allowance for radiation. This is not recommended for power plant models.
- !! i_plasma_ignited : input integer : switch for ignition assumption:
    - !!
  • = 0 do not assume plasma ignition; - !!
  • = 1 assume ignited (but include auxiliary power in costs)
- !! pden_electron_transport_loss_mw : input real : electron transport power per volume (MW/m3) - !! pden_ion_transport_loss_mw : input real : ion transport power per volume (MW/m3) - !! pden_plasma_rad_mw : input real : total radiation power per volume (MW/m3) - !! pden_plasma_core_rad_mw : input real : total core radiation power per volume (MW/m3) - !! f_alpha_plasma : input real : fraction of alpha power deposited in plasma - !! alpha_power_density_total : input real : alpha power per volume (MW/m3) - !! charged_power_density : input real : non-alpha charged particle fusion power per volume (MW/m3) - !! pden_plasma_ohmic_mw : input real : ohmic heating power per volume (MW/m3) - !! p_hcd_injected_total_mw : input real : total auxiliary injected power (MW) - !! vol_plasma : input real : plasma volume (m3) - - use physics_variables, only: i_rad_loss, i_plasma_ignited, pden_electron_transport_loss_mw, pden_ion_transport_loss_mw, pden_plasma_rad_mw, & - pden_plasma_core_rad_mw, f_alpha_plasma, alpha_power_density_total, charged_power_density, & - pden_plasma_ohmic_mw, vol_plasma - use current_drive_variables, only: p_hcd_injected_total_mw - - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - !! constraint derived type - - ! pscaling : Local real : total transport power per volume (MW/m3) - real(dp) :: pscaling - real(dp) :: pnumerator, pdenom - pscaling = pden_electron_transport_loss_mw + pden_ion_transport_loss_mw - ! Total power lost is scaling power plus radiation: - if (i_rad_loss == 0) then - pnumerator = pscaling + pden_plasma_rad_mw - else if (i_rad_loss == 1) then - pnumerator = pscaling + pden_plasma_core_rad_mw - else - pnumerator = pscaling - end if - - ! if plasma not ignited include injected power - if (i_plasma_ignited == 0) then - pdenom = f_alpha_plasma*alpha_power_density_total + charged_power_density + pden_plasma_ohmic_mw + p_hcd_injected_total_mw/vol_plasma - else - ! if plasma ignited - pdenom = f_alpha_plasma*alpha_power_density_total + charged_power_density + pden_plasma_ohmic_mw - end if - - tmp_cc = 1.0D0 - pnumerator / pdenom - tmp_con = pdenom * (1.0D0 - tmp_cc) - tmp_err = pdenom * tmp_cc - tmp_symbol = '=' - tmp_units = 'MW/m3' - - end subroutine constraint_eqn_002 - - subroutine constraint_eqn_003(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Global power balance equation for ions - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Global power balance equation for ions - !! This is a consistency equation (NBI) - !! #=# physics - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! i_plasma_ignited : input integer : switch for ignition assumption:
    - !!
  • = 0 do not assume plasma ignition; - !!
  • = 1 assume ignited (but include auxiliary power in costs)
- !! pden_ion_transport_loss_mw : input real : ion transport power per volume (MW/m3) - !! piepv : input real : ion/electron equilibration power per volume (MW/m3) - !! f_alpha_plasma : input real : fraction of alpha power deposited in plasma - !! alpha_power_ions_density : input real : alpha power per volume to ions (MW/m3) - !! p_hcd_injected_ions_mw : input real : auxiliary injected power to ions (MW) - !! vol_plasma : input real : plasma volume (m3) - use physics_variables, only: i_plasma_ignited, pden_ion_transport_loss_mw, piepv, f_alpha_plasma, alpha_power_ions_density, vol_plasma - use current_drive_variables, only: p_hcd_injected_ions_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! No assume plasma ignition: - if (i_plasma_ignited == 0) then - tmp_cc = 1.0D0 - (pden_ion_transport_loss_mw + piepv) / (f_alpha_plasma*alpha_power_ions_density + p_hcd_injected_ions_mw/vol_plasma) - tmp_con = (f_alpha_plasma*alpha_power_ions_density + p_hcd_injected_ions_mw/vol_plasma) * (1.0D0 - tmp_cc) - tmp_err = (f_alpha_plasma*alpha_power_ions_density + p_hcd_injected_ions_mw/vol_plasma) * tmp_cc - tmp_symbol = '=' - tmp_units = 'MW/m3' - ! Plasma ignited: - else - tmp_cc = 1.0D0 - (pden_ion_transport_loss_mw+piepv) / (f_alpha_plasma*alpha_power_ions_density) - tmp_con = (f_alpha_plasma*alpha_power_ions_density) * (1.0D0 - tmp_cc) - tmp_err = (f_alpha_plasma*alpha_power_ions_density) * tmp_cc - tmp_symbol = '=' - tmp_units = 'MW/m3' - end if - - end subroutine constraint_eqn_003 - - subroutine constraint_eqn_004(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Global power balance equation for electrons - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Global power balance equation for electrons - !! This is a consistency equation - !! N.B. This constraint is currently NOT RECOMMENDED for use. - !! #=# physics - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! i_rad_loss : input integer : switch for radiation loss term usage in power balance (see User Guide):
    - !!
  • = 0 total power lost is scaling power plus radiation (needed for ipedestal=2,3) - !!
  • = 1 total power lost is scaling power plus core radiation only - !!
  • = 2 total power lost is scaling power only, with no additional - !! allowance for radiation. This is not recommended for power plant models.
- !! i_plasma_ignited : input integer : switch for ignition assumption:
    - !!
  • = 0 do not assume plasma ignition; - !!
  • = 1 assume ignited (but include auxiliary power in costs)
- !! pden_electron_transport_loss_mw : input real : electron transport power per volume (MW/m3) - !! pden_plasma_rad_mw : input real : total radiation power per volume (MW/m3) - !! pden_plasma_core_rad_mw : input real : total core radiation power per volume (MW/m3) - !! f_alpha_plasma : input real : fraction of alpha power deposited in plasma - !! alpha_power_electron_density : input real : alpha power per volume to electrons (MW/m3) - !! piepv : input real : ion/electron equilibration power per volume (MW/m3) - !! p_hcd_injected_electrons_mw : input real : auxiliary injected power to electrons (MW) - !! vol_plasma : input real : plasma volume (m3) - use physics_variables, only: i_rad_loss, i_plasma_ignited, pden_electron_transport_loss_mw, pden_plasma_core_rad_mw, f_alpha_plasma, & - alpha_power_electron_density, piepv, vol_plasma, pden_plasma_rad_mw - use current_drive_variables, only: p_hcd_injected_electrons_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! pscaling : Local real : total transport power per volume (MW/m3) - real(dp) :: pscaling - real(dp) :: pnumerator, pdenom - pscaling = pden_electron_transport_loss_mw - ! Total power lost is scaling power plus radiation: - if (i_rad_loss == 0) then - pnumerator = pscaling + pden_plasma_rad_mw - else if (i_rad_loss == 1) then - pnumerator = pscaling + pden_plasma_core_rad_mw - else - pnumerator = pscaling - end if - - ! if plasma not ignited include injected power - if (i_plasma_ignited == 0) then - pdenom = f_alpha_plasma*alpha_power_electron_density + piepv + p_hcd_injected_electrons_mw/vol_plasma - else - ! if plasma ignited - pdenom = f_alpha_plasma*alpha_power_electron_density + piepv - end if - - tmp_cc = 1.0D0 - pnumerator / pdenom - tmp_con = pdenom * (1.0D0 - tmp_cc) - tmp_err = pdenom * tmp_cc - tmp_symbol = '=' - tmp_units = 'MW/m3' - - end subroutine constraint_eqn_004 - - subroutine constraint_eqn_005(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for density upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for density upper limit - !! #=# physics - !! #=#=# fdene, dnelimt - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! i_density_limit : input integer : switch for density limit to enforce (constraint equation 5):
    - !!
  • = 1 old ASDEX; - !!
  • = 2 Borrass model for ITER (I); - !!
  • = 3 Borrass model for ITER (II); - !!
  • = 4 JET edge radiation; - !!
  • = 5 JET simplified; - !!
  • = 6 Hugill-Murakami Mq limit; - !!
  • = 7 Greenwald limit
- !! fdene : input real : f-value for density limit - !! dene : input real : electron density (/m3) - !! dnelimt : input real : density limit (/m3) - !! dnla : input real : line averaged electron density (m-3) - use physics_variables, only: i_density_limit, dnelimt, dnla, dene - use constraint_variables, only: fdene - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! Apply Greenwald limit to line-averaged density - if (i_density_limit == 7) then - tmp_cc = dnla/dnelimt - 1.0D0 * fdene - tmp_con = fdene * dnelimt - tmp_err = fdene * dnelimt - dnla - tmp_symbol = '<' - tmp_units = '/m3' - else - tmp_cc = dene/dnelimt - 1.0D0 * fdene - tmp_con = dnelimt * (1.0D0 - tmp_cc) - tmp_err = dene * tmp_cc - tmp_symbol = '<' - tmp_units = '/m3' - end if - - end subroutine constraint_eqn_005 - - subroutine constraint_eqn_006(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for epsilon beta-poloidal upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for epsilon beta-poloidal upper limit - !! #=# physics - !! #=#=# fbeta_poloidal_eps, beta_poloidal_eps_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fbeta_poloidal_eps : input real : f-value for epsilon beta-poloidal - !! beta_poloidal_eps_max : input real : maximum (eps*beta_poloidal) - !! eps : input real : inverse aspect ratio - !! beta_poloidal : input real : poloidal beta - use physics_variables, only: beta_poloidal_eps_max, eps, beta_poloidal - use constraint_variables, only: fbeta_poloidal_eps - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = (eps*beta_poloidal)/beta_poloidal_eps_max - 1.0D0 * fbeta_poloidal_eps - tmp_con = beta_poloidal_eps_max * (1.0D0 - tmp_cc) - tmp_err = (eps*beta_poloidal) * tmp_cc - tmp_symbol = '<' - tmp_units = '' - - end subroutine constraint_eqn_006 - - subroutine constraint_eqn_007(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for hot beam ion density - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for hot beam ion density - !! This is a consistency equation (NBI) - !! #=# physics - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! i_plasma_ignited : input integer : switch for ignition assumption:
    - !!
  • = 0 do not assume plasma ignition; - !!
  • = 1 assume ignited (but include auxiliary power in costs)
- !! Obviously, i_plasma_ignited must be zero if current drive is required. - !! If i_plasma_ignited=1, any auxiliary power is assumed to be used only - !! during plasma start-up, and is excluded from all steady-state - !! power balance calculations. - !! beam_density_out : input real : hot beam ion density from calculation (/m3) - !! nd_beam_ions : input real : hot beam ion density, variable (/m3) - use physics_variables, only: i_plasma_ignited, beam_density_out, nd_beam_ions - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! Do not assume plasma ignition: - if (i_plasma_ignited == 0) then - tmp_cc = 1.0D0 - beam_density_out/nd_beam_ions - tmp_con = nd_beam_ions * (1.0D0 - tmp_cc) - tmp_err = nd_beam_ions * tmp_cc - tmp_symbol = '=' - tmp_units = '/m3' - else - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - call report_error(1) - end if - - end subroutine constraint_eqn_007 - - subroutine constraint_eqn_008(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for neutron wall load upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for neutron wall load upper limit - !! #=# physics - !! #=#=# fwalld, walalw - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fwalld : input real : f-value for maximum wall load - !! walalw : input real : allowable wall-load (MW/m2) - !! pflux_fw_neutron_mw : input real : average neutron wall load (MW/m2) - use constraint_variables, only: fwalld, walalw - use physics_variables, only: pflux_fw_neutron_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = pflux_fw_neutron_mw/walalw - 1.0D0 * fwalld - tmp_con = fwalld * walalw - tmp_err = fwalld * walalw - pflux_fw_neutron_mw - tmp_symbol = '<' - tmp_units = 'MW/m2' - - end subroutine constraint_eqn_008 - - subroutine constraint_eqn_009(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for fusion power upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for fusion power upper limit - !! #=# physics - !! #=#=# ffuspow, powfmax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ffuspow : input real : f-value for maximum fusion power - !! powfmax : input real : maximum fusion power (MW) - !! fusion_power : input real : fusion power (MW) - use constraint_variables, only: ffuspow, powfmax - use physics_variables, only: fusion_power - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = fusion_power/powfmax - 1.0D0 * ffuspow - tmp_con = powfmax * (1.0D0 - tmp_cc) - tmp_err = fusion_power * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW' - - end subroutine constraint_eqn_009 - - subroutine constraint_eqn_010(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! Equation for field at TF coil - !! This is a consistency equation - !! (do not use for stellarators) - !! #=# tfcoil - !! #=#=# consistency - !! rmajor | plasma major radius (m) - !! bt | toroidal field on axis (T) - !! r_b_tf_inboard_peak | radius of maximum toroidal field (m) - !! b_tf_inboard_peak | peak field at toroidal field coil (T) - - !! This constraint is depreciated - - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - !! Constraints output - - ! This constraint is depreciated - call report_error(236) - - tmp_con = 1.0D0 - tmp_err = 0.0D0 - tmp_symbol = '=' - tmp_units = '' - - end subroutine constraint_eqn_010 - - subroutine constraint_eqn_011(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for radial build - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for radial build - !! (This is a consistency equation.) - !! #=# build - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! rbld : input real : sum of thicknesses to the major radius (m) - !! rmajor : input real : plasma major radius (m) - use build_variables, only: rbld - use physics_variables, only: rmajor - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - rbld/rmajor - tmp_con = rmajor * (1.0D0 - tmp_cc) - tmp_err = rmajor * tmp_cc - tmp_symbol = '=' - tmp_units = 'm' - - end subroutine constraint_eqn_011 - - subroutine constraint_eqn_012(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for volt-second capability lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for volt-second capability lower limit - !! #=# pfcoil - !! #=#=# fvs, vs_plasma_total_required - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! vs_plasma_total_required : input real : total V-s needed (Wb) - !! vs_plasma_total_required (lower limit) is positive; vs_cs_pf_total_pulse (available) is negative - !! fvs : input real : f-value for flux-swing (V-s) requirement (STEADY STATE) - !! vs_cs_pf_total_pulse : input real : total flux swing for pulse (Wb) - use physics_variables, only: vs_plasma_total_required - use constraint_variables, only: fvs - use pfcoil_variables, only: vs_cs_pf_total_pulse - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - !! vs_cs_pf_total_pulse is negative, requires sign change - tmp_cc = 1.0D0 - fvs * (-vs_cs_pf_total_pulse)/vs_plasma_total_required - tmp_con = vs_plasma_total_required * (1.0D0 - tmp_cc) - tmp_err = vs_plasma_total_required * tmp_cc - tmp_symbol = '>' - tmp_units = 'V.sec' - - end subroutine constraint_eqn_012 - - subroutine constraint_eqn_013(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for burn time lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for burn time lower limit - !! #=# times - !! #=#=# ft_burn, t_burn_min - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ft_burn : input real : f-value for minimum burn time - !! t_burn : input real : burn time (s) (calculated if i_pulsed_plant=1) - !! t_burn_min : input real : minimum burn time (s) - use constraint_variables, only: ft_burn,t_burn_min - use times_variables, only: t_burn - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - ft_burn * t_burn/t_burn_min - tmp_con = t_burn_min / ft_burn - tmp_err = t_burn_min / ft_burn - t_burn - tmp_symbol = '>' - tmp_units = 'sec' - - end subroutine constraint_eqn_013 - - subroutine constraint_eqn_014(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation to fix number of NBI decay lengths to plasma centre - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation to fix number of NBI decay lengths to plasma centre - !! This is a consistency equation - !! #=# current_drive - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! n_beam_decay_lengths_core : input real : neutral beam e-decay lengths to plasma centre - !! tbeamin : input real : permitted neutral beam e-decay lengths to plasma centre - use current_drive_variables, only: n_beam_decay_lengths_core, tbeamin - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - n_beam_decay_lengths_core/tbeamin - tmp_con = tbeamin * (1.0D0 - tmp_cc) - tmp_err = tbeamin * tmp_cc - tmp_symbol = '=' - tmp_units = '' - - end subroutine constraint_eqn_014 - - subroutine constraint_eqn_015(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for L-H power threshold limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for L-H power threshold limit - !! #=# physics - !! #=#=# fl_h_threshold, p_l_h_threshold_mw - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fl_h_threshold : input real : f-value for L-H power threshold - !! p_l_h_threshold_mw : input real : L-H mode power threshold (MW) - !! p_plasma_separatrix_mw : input real : power to conducted to the divertor region (MW) - use constraint_variables, only: fl_h_threshold - use physics_variables, only: p_l_h_threshold_mw, p_plasma_separatrix_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - fl_h_threshold * p_plasma_separatrix_mw / p_l_h_threshold_mw - tmp_con = p_l_h_threshold_mw - tmp_err = p_l_h_threshold_mw - p_plasma_separatrix_mw / fl_h_threshold - if (fl_h_threshold > 1.0D0) then - tmp_symbol = '>' - else - tmp_symbol = '<' - end if - tmp_units = 'MW' - - end subroutine constraint_eqn_015 - - subroutine constraint_eqn_016(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for net electric power lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for net electric power lower limit - !! #=# heat_transport - !! #=#=# fpnetel, pnetelin - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fpnetel : input real : f-value for net electric power - !! pnetelmw : input real : net electric power (MW) - !! pnetelin : input real : required net electric power (MW) - use constraint_variables, only: fpnetel, pnetelin - use heat_transport_variables, only: pnetelmw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - fpnetel * pnetelmw / pnetelin - tmp_con = pnetelin - tmp_err = pnetelmw - pnetelin / fpnetel - tmp_symbol = '>' - tmp_units = 'MW' - - end subroutine constraint_eqn_016 - - subroutine constraint_eqn_017(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for radiation power upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for radiation power upper limit - !! #=# physics - !! #=#=# fradpwr, pradmaxpv - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! f_alpha_plasma : input real : fraction of alpha power deposited in plasma - !! p_hcd_injected_total_mw : input real : total auxiliary injected power (MW) - !! vol_plasma : input real : plasma volume (m3) - !! alpha_power_density_total : input real : alpha power per volume (MW/m3) - !! charged_power_density : input real : non-alpha charged particle fusion power per volume (MW/m3) - !! pden_plasma_ohmic_mw : input real : ohmic heating power per volume (MW/m3) - !! fradpwr : input real : f-value for core radiation power limit - !! pden_plasma_rad_mw : input real : total radiation power per volume (MW/m3) - use physics_variables, only: f_alpha_plasma, vol_plasma, alpha_power_density_total, charged_power_density, pden_plasma_ohmic_mw, pden_plasma_rad_mw - use current_drive_variables, only: p_hcd_injected_total_mw - use constraint_variables, only: fradpwr - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - real(dp) :: pradmaxpv - !! Maximum possible power/vol_plasma that can be radiated (local) - - pradmaxpv = p_hcd_injected_total_mw/vol_plasma + alpha_power_density_total*f_alpha_plasma + charged_power_density + pden_plasma_ohmic_mw - tmp_cc = pden_plasma_rad_mw/pradmaxpv - 1.0D0 * fradpwr - tmp_con = pradmaxpv * (1.0D0 - tmp_cc) - tmp_err = pden_plasma_rad_mw * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW/m3' - - end subroutine constraint_eqn_017 - - subroutine constraint_eqn_018(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for divertor heat load upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for divertor heat load upper limit - !! #=# divertor - !! #=#=# fpflux_div_heat_load_mw, pflux_div_heat_load_max_mw - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fpflux_div_heat_load_mw : input real : f-value for divertor heat load - !! pflux_div_heat_load_max_mw : input real : heat load limit (MW/m2) - !! pflux_div_heat_load_mw : input real : divertor heat load (MW/m2) - use constraint_variables, only: fpflux_div_heat_load_mw - use divertor_variables, only: pflux_div_heat_load_max_mw, pflux_div_heat_load_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = pflux_div_heat_load_mw/pflux_div_heat_load_max_mw - 1.0D0 * fpflux_div_heat_load_mw - tmp_con = pflux_div_heat_load_max_mw * (1.0D0 - tmp_cc) - tmp_err = pflux_div_heat_load_mw * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW/m2' - - end subroutine constraint_eqn_018 - - subroutine constraint_eqn_019(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for MVA (power) upper limit: resistive TF coil set - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for MVA upper limit - !! #=# tfcoil - !! #=#=# fmva, mvalim - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! tfcpmw : input real : peak resistive TF coil inboard leg power (total) (MW) - !! tflegmw : input real : TF coil outboard leg resistive power (total) (MW) - !! fmva : input real : f-value for maximum MVA - !! mvalim : input real : MVA limit for resistive TF coil set (total) (MW) - use tfcoil_variables, only: tfcpmw, tflegmw - use constraint_variables, only: fmva, mvalim - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - ! totmva : local real : total MVA in TF coil (MW) - real(dp) :: totmva - - totmva = tfcpmw + tflegmw - tmp_cc = totmva/mvalim - 1.0D0 * fmva - tmp_con = mvalim * (1.0D0 - tmp_cc) - tmp_err = totmva * tmp_cc - tmp_symbol = '<' - tmp_units = 'MVA' - - end subroutine constraint_eqn_019 - - subroutine constraint_eqn_020(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for neutral beam tangency radius upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for neutral beam tangency radius upper limit - !! #=# current_drive - !! #=#=# fportsz, rtanmax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fportsz : input real : f-value for neutral beam tangency radius limit - !! rtanmax : input real : maximum tangency radius for centreline of beam (m) - !! rtanbeam : input real : neutral beam centreline tangency radius (m) - use constraint_variables, only: fportsz - use current_drive_variables, only: rtanmax, rtanbeam - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = rtanbeam/rtanmax - 1.0D0 * fportsz - tmp_con = rtanmax * (1.0D0 - tmp_cc) - tmp_err = rtanbeam * tmp_cc - tmp_symbol = '<' - tmp_units = 'm' - - end subroutine constraint_eqn_020 - - subroutine constraint_eqn_021(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for minor radius lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for minor radius lower limit - !! #=# physics - !! #=#=# frminor, aplasmin - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! frminor : input real : f-value for minor radius limit - !! rminor : input real : plasma minor radius (m) - !! aplasmin : input real : minimum minor radius (m) - use constraint_variables, only: frminor - use physics_variables, only: rminor - use build_variables, only: aplasmin - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - frminor * rminor/aplasmin - tmp_con = aplasmin * (1.0D0 - tmp_cc) - tmp_err = aplasmin * tmp_cc - tmp_symbol = '>' - tmp_units = '' - - end subroutine constraint_eqn_021 - - subroutine constraint_eqn_022(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for divertor collision/connection length ratio upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for divertor collision/connection length ratio upper limit - !! #=# divertor - !! #=#=# fdivcol, rlenmax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fdivcol : input real : f-value for divertor collisionality - !! rlenmax : input real : maximum value for length ratio (rlclolcn) - !! rlclolcn : input real : ratio of collision length / connection length - - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - !! Constraints output - - ! This constraint is depreciated - call report_error(289) - - tmp_con = 1.0D0 - tmp_err = 0.0D0 - tmp_symbol = '=' - tmp_units = '' - - end subroutine constraint_eqn_022 - - subroutine constraint_eqn_023(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for conducting shell radius / rminor upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for conducting shell radius / rminor upper limit - !! #=# physics - !! #=#=#fr_conducting_wall, f_r_conducting_wall - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! rminor : input real : plasma minor radius (m) - !! dr_fw_plasma_gap_outboard : input real : gap between plasma and first wall, outboard side (m) - !! dr_fw_outboard : input real : outboard first wall thickness, initial estimate (m) - !! dr_blkt_outboard : input real : outboard blanket thickness (m) - !!fr_conducting_wall : input real : f-value for conducting wall radius / rminor limit - !! f_r_conducting_wall : input real : maximum ratio of conducting wall distance to plasma minor radius for vertical stability - use physics_variables, only: rminor, f_r_conducting_wall - use build_variables, only: dr_fw_plasma_gap_outboard, dr_fw_outboard, dr_blkt_outboard - use constraint_variables, only:fr_conducting_wall - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - ! rcw : local real : conducting shell radius (m) - real(dp) :: rcw - - rcw = rminor + dr_fw_plasma_gap_outboard + dr_fw_outboard + dr_blkt_outboard - tmp_cc = rcw / (f_r_conducting_wall * rminor) - 1.0D0 * fr_conducting_wall - tmp_con = f_r_conducting_wall*rminor * (1.0D0 - tmp_cc) - tmp_err = rcw * tmp_cc - tmp_symbol = '<' - tmp_units = 'm' - - end subroutine constraint_eqn_023 - - subroutine constraint_eqn_024(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for beta upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for beta upper limit - !! #=# physics - !! #=#=# fbeta_max, beta_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! i_beta_component : input integer : switch for beta limit scaling (constraint equation 24):
    - !!
  • = 0 apply limit to total beta; - !!
  • = 1 apply limit to thermal beta; - !!
  • = 2 apply limit to thermal + neutral beam beta - !!
  • = 3 apply limit to toroidal beta
- !! istell : input integer : switch for stellarator option (set via device.dat):
    - !!
  • = 0 use tokamak model; - !!
  • = 1 use stellarator model
- !! fbeta_max : input real : f-value for beta limit - !! beta_max : input real : allowable beta - !! beta : input real : total plasma beta (calculated if ipedestal =3) - !! beta_fast_alpha : input real : fast alpha beta component - !! beta_beam : input real : neutral beam beta component - !! bt : input real : toroidal field - !! btot : input real : total field - use physics_variables, only: i_beta_component, beta_max, beta, beta_beam, beta_fast_alpha, bt, btot - use stellarator_variables, only: istell - use constraint_variables, only: fbeta_max - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! Include all beta components: relevant for both tokamaks and stellarators - if ((i_beta_component == 0).or.(istell /= 0)) then - tmp_cc = beta/beta_max - 1.0D0 * fbeta_max - tmp_con = beta_max - tmp_err = beta_max - beta / fbeta_max - tmp_symbol = '<' - tmp_units = '' - ! Here, the beta limit applies to only the thermal component, not the fast alpha or neutral beam parts - else if (i_beta_component == 1) then - tmp_cc = (beta-beta_fast_alpha-beta_beam)/beta_max - 1.0D0 * fbeta_max - tmp_con = beta_max - tmp_err = beta_max - (beta-beta_fast_alpha-beta_beam) / fbeta_max - tmp_symbol = '<' - tmp_units = '' - ! Beta limit applies to thermal + neutral beam: components of the total beta, i.e. excludes alphas - else if (i_beta_component == 2) then - tmp_cc = (beta-beta_fast_alpha)/beta_max - 1.0D0 * fbeta_max - tmp_con = beta_max * (1.0D0 - tmp_cc) - tmp_err = (beta-beta_fast_alpha) * tmp_cc - tmp_symbol = '<' - tmp_units = '' - ! Beta limit applies to toroidal beta - else if (i_beta_component == 3) then - tmp_cc = (beta*(btot/bt)**2)/beta_max - 1.0D0 * fbeta_max - tmp_con = beta_max - tmp_err = beta_max - (beta*(btot/bt)**2) / fbeta_max - tmp_symbol = '<' - tmp_units = '' - end if - - end subroutine constraint_eqn_024 - - subroutine constraint_eqn_025(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for peak toroidal field upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for peak toroidal field upper limit - !! #=# tfcoil - !! #=#=# fpeakb, bmxlim - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fpeakb : input real : f-value for maximum toroidal field - !! bmxlim : input real : maximum peak toroidal field (T) - !! b_tf_inboard_peak : input real : mean peak field at TF coil (T) - use constraint_variables, only: fpeakb, bmxlim - use tfcoil_variables, only: b_tf_inboard_peak - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = b_tf_inboard_peak/bmxlim - 1.0D0 * fpeakb - tmp_con = bmxlim * (1.0D0 - tmp_cc) - tmp_err = b_tf_inboard_peak * tmp_cc - tmp_symbol = '<' - tmp_units = 'T' - - end subroutine constraint_eqn_025 - - subroutine constraint_eqn_026(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for Central Solenoid current density upper limit at EOF - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for Central Solenoid current density upper limit at EOF - !! #=# pfcoil - !! #=#=# fjohc, j_cs_critical_flat_top_end - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fjohc : input real : f-value for central solenoid current at end-of-flattop - !! j_cs_critical_flat_top_end : input real : allowable central solenoid current density at end of flat-top (A/m2) - !! j_cs_flat_top_end : input real : central solenoid overall current density at end of flat-top (A/m2) - use constraint_variables, only: fjohc - use pfcoil_variables, only: j_cs_critical_flat_top_end, j_cs_flat_top_end - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = j_cs_flat_top_end/j_cs_critical_flat_top_end - 1.0D0 * fjohc - tmp_con = j_cs_critical_flat_top_end - tmp_err = j_cs_critical_flat_top_end - j_cs_flat_top_end / fjohc - tmp_symbol = '<' - tmp_units = 'A/m2' - - end subroutine constraint_eqn_026 - - subroutine constraint_eqn_027(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for Central Solenoid current density upper limit at BOP - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for Central Solenoid current density upper limit at BOP - !! #=# pfcoil - !! #=#=# fjohc0, j_cs_critical_pulse_start - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fjohc0 : input real : f-value for central solenoid current at beginning of pulse - !! j_cs_critical_pulse_start : input real : allowable central solenoid current density at beginning of pulse (A/m2) - !! j_cs_pulse_start : input real : central solenoid overall current density at beginning of pulse (A/m2) - use constraint_variables, only: fjohc0 - use pfcoil_variables, only: j_cs_critical_pulse_start, j_cs_pulse_start - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = j_cs_pulse_start/j_cs_critical_pulse_start - 1.0D0 * fjohc0 - tmp_con = j_cs_critical_pulse_start - tmp_err = j_cs_critical_pulse_start - j_cs_pulse_start / fjohc0 - tmp_symbol = '<' - tmp_units = 'A/m2' - - end subroutine constraint_eqn_027 - - subroutine constraint_eqn_028(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for fusion gain (big Q) lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for fusion gain (big Q) lower limit - !! #=# physics - !! #=#=# fqval, bigqmin - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fqval : input real : pf-value for Q - !! bigq : input real : Fusion gain; P_fusion / (P_injection + P_ohmic) - !! bigqmin : input real : minimum fusion gain Q - !! i_plasma_ignited : input integer : switch for ignition assumption:
    - !!
  • = 0 do not assume plasma ignition; - !!
  • = 1 assume ignited (but include auxiliary power in costs)
- !! Obviously, i_plasma_ignited must be zero if current drive is required. - !! If i_plasma_ignited=1, any auxiliary power is assumed to be used only - !! during plasma start-up, and is excluded from all steady-state - !! power balance calculations. - use constraint_variables, only: fqval, bigqmin - use current_drive_variables, only: bigq - use physics_variables, only: i_plasma_ignited - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! if plasma is not ignited ... - if (i_plasma_ignited == 0) then - tmp_cc = 1.0D0 - fqval * bigq/bigqmin - tmp_con = bigqmin * (1.0D0 - tmp_cc) - tmp_err = bigqmin * tmp_cc - tmp_symbol = '>' - tmp_units = '' - ! if plasma is ignited report error - else - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - call report_error(4) - end if - - end subroutine constraint_eqn_028 - - subroutine constraint_eqn_029(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for inboard major radius: This is a consistency equation - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for inboard major radius: This is a consistency equation - !! #=# build - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! rmajor : input real : plasma major radius (m) (iteration variable 3) - !! rminor : input real : plasma minor radius (m) - !! rinboard : input real : plasma inboard radius (m) - use physics_variables, only: rmajor, rminor - use build_variables, only: rinboard - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - (rmajor - rminor) / rinboard - tmp_con = rinboard * (1.0D0 - tmp_cc) - tmp_err = rinboard * tmp_cc - tmp_symbol = '=' - tmp_units = 'm' - - end subroutine constraint_eqn_029 - - subroutine constraint_eqn_030(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for injection power upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for injection power upper limit - !! #=# current_drive - !! #=#=# fpinj, p_hcd_injected_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! p_hcd_injected_total_mw : input real : total auxiliary injected power (MW) - !! fpinj : input real : f-value for injection power - !! p_hcd_injected_max : input real : Maximum allowable value for injected power (MW) - use current_drive_variables, only: p_hcd_injected_total_mw, p_hcd_injected_max - use constraint_variables, only: fpinj - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = p_hcd_injected_total_mw/p_hcd_injected_max - 1.0D0 * fpinj - tmp_con = p_hcd_injected_max - tmp_err = p_hcd_injected_max - p_hcd_injected_total_mw / fpinj - tmp_symbol = '<' - tmp_units = 'MW' - - end subroutine constraint_eqn_030 - - subroutine constraint_eqn_031(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil case stress upper limit (SCTF) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for TF coil case stress upper limit (SCTF) - !! #=# tfcoil - !! #=#=# fstrcase, sig_tf_case_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fstrcase : input real : f-value for TF coil case stress - !! sig_tf_case_max : input real : Allowable maximum shear stress in TF coil case (Tresca criterion) (Pa) - !! sig_tf_case : input real : Constrained stress in TF coil case (Pa) - use constraint_variables, only: fstrcase - use tfcoil_variables, only: sig_tf_case_max, sig_tf_case - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = sig_tf_case/sig_tf_case_max - 1.0D0 * fstrcase - tmp_con = sig_tf_case_max - tmp_err = sig_tf_case_max - sig_tf_case / fstrcase - tmp_symbol = '<' - tmp_units = 'Pa' - - end subroutine constraint_eqn_031 - - subroutine constraint_eqn_032(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil conduit stress upper limit (SCTF) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for TF coil conduit stress upper limit (SCTF) - !! #=# tfcoil - !! #=#=# fstrcond, sig_tf_wp_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fstrcond : input real : f-value for TF coil conduit stress - !! sig_tf_wp_max : input real : Allowable maximum shear stress in TF coil conduit (Tresca criterion) (Pa) - !! sig_tf_wp : input real : Constrained stress in TF conductor conduit (Pa) - use constraint_variables, only: fstrcond - use tfcoil_variables, only: sig_tf_wp_max, sig_tf_wp - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = sig_tf_wp/sig_tf_wp_max - 1.0D0 * fstrcond - tmp_con = sig_tf_wp_max - tmp_err = sig_tf_wp_max - sig_tf_wp / fstrcond - tmp_symbol = '<' - tmp_units = 'Pa' - - end subroutine constraint_eqn_032 - - subroutine constraint_eqn_033(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil operating/critical J upper limit (SCTF) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for TF coil operating/critical J upper limit (SCTF) - !! #=# tfcoil - !! #=#=# fiooic, jwdgcrt - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fiooic : input real : f-value for TF coil operating current / critical - !! jwdgcrt : input real : critical current density for winding pack (A/m2) - !! j_tf_wp : input real : winding pack current density (A/m2) - use constraint_variables, only: fiooic - use tfcoil_variables, only: jwdgcrt, j_tf_wp - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - if (fiooic > 0.7D0) call report_error(285) - tmp_cc = j_tf_wp/jwdgcrt - 1.0D0 * fiooic - tmp_con = jwdgcrt * (1.0D0 - tmp_cc) - tmp_err = j_tf_wp * tmp_cc - tmp_symbol = '<' - tmp_units = 'A/m2' - - end subroutine constraint_eqn_033 - - subroutine constraint_eqn_034(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil dump voltage upper limit (SCTF) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for TF coil dump voltage upper limit (SCTF) - !! #=# tfcoil - !! #=#=# fvdump, vdalw - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fvdump : input real : f-value for dump voltage - !! vdalw : input real : max voltage across TF coil during quench (kV) - !! vtfskv : input real : voltage across a TF coil during quench (kV) - use constraint_variables, only: fvdump - use tfcoil_variables, only: vdalw, vtfskv - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = vtfskv/vdalw - 1.0D0 * fvdump - tmp_con = vdalw - tmp_err = vdalw - vtfskv - tmp_symbol = '<' - tmp_units = 'V' - - end subroutine constraint_eqn_034 - - subroutine constraint_eqn_035(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil J_wp/J_prot upper limit (SCTF) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for TF coil J_wp/J_prot upper limit (SCTF) - !! #=# tfcoil - !! #=#=# fjprot, jwdgpro - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fjprot : input real : f-value for TF coil winding pack current density - !! jwdgpro : input real : allowable TF coil winding pack current density, for dump temperature rise protection (A/m2) - !! j_tf_wp : input real : winding pack current density (A/m2) - use constraint_variables, only: fjprot - use tfcoil_variables, only: jwdgpro, j_tf_wp - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = j_tf_wp/jwdgpro - 1.0D0 * fjprot - tmp_con = jwdgpro - tmp_err = j_tf_wp - jwdgpro - tmp_symbol = '<' - tmp_units = 'A/m2' - - end subroutine constraint_eqn_035 - - subroutine constraint_eqn_036(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil s/c temperature margin lower limit (SCTF) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for TF coil s/c temperature margin lower limit (SCTF) - !! #=# tfcoil - !! #=#=# ftmargtf, tmargmin_tf - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ftmargtf : input real : f-value for TF coil temperature margin - !! tmargtf : input real : TF coil temperature margin (K) - !! tmargmin_tf : input real : minimum allowable temperature margin : TF coils (K) - use constraint_variables, only: ftmargtf - use tfcoil_variables, only: tmargtf, tmargmin_tf - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - ftmargtf * tmargtf/tmargmin_tf - tmp_con = tmargmin_tf - tmp_err = tmargmin_tf - tmargtf - tmp_symbol = '>' - tmp_units = 'K' - - end subroutine constraint_eqn_036 - - subroutine constraint_eqn_037(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for current drive gamma upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for current drive gamma upper limit - !! #=# current_drive - !! #=#=# fgamcd, gammax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fgamcd : input real : f-value for current drive gamma - !! gammax : input real : maximum current drive gamma - !! eta_cd_norm_hcd_primary : input real : normalised current drive efficiency (1.0e20 A/W-m2) - use constraint_variables, only: fgamcd, gammax - use current_drive_variables, only: eta_cd_norm_hcd_primary - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = eta_cd_norm_hcd_primary/gammax - 1.0D0 * fgamcd - tmp_con = gammax * (1.0D0 - tmp_cc) - tmp_err = eta_cd_norm_hcd_primary * tmp_cc - tmp_symbol = '<' - tmp_units = '1E20 A/Wm2' - - end subroutine constraint_eqn_037 - - subroutine constraint_eqn_038(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Obsolete - !! author: P B Lloyd, CCFE, Culham Science Centre - !! Obsolete - !! #=# empty - !! #=#=# empty - implicit none - ! Dummy formal arguments, for compliance with interface - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - - end subroutine constraint_eqn_038 - - subroutine constraint_eqn_039(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for first wall temperature upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for first wall temperature upper limit - !! #=# fwbs - !! #=#=# ftpeak, temp_fw_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ftpeak : input real : f-value for first wall peak temperature - !! temp_fw_max : input real : maximum temperature of first wall material (K) (i_thermal_electric_conversion>1) - !! temp_fw_peak : input real : peak first wall temperature (K) - use constraint_variables, only: ftpeak - use fwbs_variables, only: temp_fw_max, temp_fw_peak - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! If the temperature peak == 0 then report an error - if (temp_fw_peak < 1.0D0) call report_error(5) - tmp_cc = temp_fw_peak/temp_fw_max - 1.0D0 * ftpeak - tmp_con = temp_fw_max * (1.0D0 - tmp_cc) - tmp_err = temp_fw_peak * tmp_cc - tmp_symbol = '<' - tmp_units = 'K' - - end subroutine constraint_eqn_039 - - subroutine constraint_eqn_040(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for auxiliary power lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for auxiliary power lower limit - !! #=# current_drive - !! #=#=# fauxmn, auxmin - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fauxmn : input real : f-value for minimum auxiliary power - !! p_hcd_injected_total_mw : input real : total auxiliary injected power (MW) - !! auxmin : input real : minimum auxiliary power (MW) - use constraint_variables, only: fauxmn, auxmin - use current_drive_variables, only: p_hcd_injected_total_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - fauxmn * p_hcd_injected_total_mw/auxmin - tmp_con = auxmin * (1.0D0 - tmp_cc) - tmp_err = auxmin * tmp_cc - tmp_symbol = '>' - tmp_units = 'MW' - - end subroutine constraint_eqn_040 - - subroutine constraint_eqn_041(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for plasma current ramp-up time lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for plasma current ramp-up time lower limit - !! #=# times - !! #=#=# ft_current_ramp_up, t_current_ramp_up_min - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ft_current_ramp_up : input real : f-value for plasma current ramp-up time - !! t_current_ramp_up : input real : plasma current ramp-up time for current initiation (s) - !! t_current_ramp_up_min : input real : minimum plasma current ramp-up time (s) - use constraint_variables, only: ft_current_ramp_up, t_current_ramp_up_min - use times_variables, only: t_current_ramp_up - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - ft_current_ramp_up * t_current_ramp_up/t_current_ramp_up_min - tmp_con = t_current_ramp_up_min * (1.0D0 - tmp_cc) - tmp_err = t_current_ramp_up_min * tmp_cc - tmp_symbol = '>' - tmp_units = 'sec' - - end subroutine constraint_eqn_041 - - subroutine constraint_eqn_042(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for cycle time lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for cycle time lower limit - !! #=# times - !! #=#=# ftcycl, tcycmn - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ftcycl : input real : f-value for cycle time - !! t_cycle : input real : full cycle time (s) - !! tcycmn : input real : minimum cycle time (s) - use constraint_variables, only: ftcycl, tcycmn - use times_variables, only: t_cycle - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! if the minimum cycle time == 0 report an error - if (tcycmn < 1.0D0) call report_error(6) - tmp_cc = 1.0D0 - ftcycl * t_cycle/tcycmn - tmp_con = tcycmn * (1.0D0 - tmp_cc) - tmp_err = tcycmn * tmp_cc - tmp_symbol = '>' - tmp_units = 'sec' - - end subroutine constraint_eqn_042 - - subroutine constraint_eqn_043(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for average centrepost temperature: This is a consistency equation (TART) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for average centrepost temperature: This is a consistency equation (TART) - !! #=# tfcoil - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! temp_cp_average : input real : average temp of TF coil inboard leg conductor (C)e - !! tcpav2 : input real : centrepost average temperature (C) (for consistency) - !! itart : input integer : switch for spherical tokamak (ST) models:
    - !!
  • = 0 use conventional aspect ratio models; - !!
  • = 1 use spherical tokamak models
- use tfcoil_variables, only: temp_cp_average, tcpav2 - use physics_variables, only: itart - use tfcoil_variables, only: i_tf_sup - - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! if the machine isn't a ST then report error - if (itart == 0) call report_error(7) - - ! For some reasons these lines are needed to make VMCON CONVERGE .... - if ( i_tf_sup == 0 ) then ! Copper case - temp_cp_average = temp_cp_average - 273.15D0 - tcpav2 = tcpav2 - 273.15D0 - end if - - tmp_cc = 1.0D0 - temp_cp_average/tcpav2 - tmp_con = tcpav2 * (1.0D0 - tmp_cc) - tmp_err = tcpav2 * tmp_cc - tmp_symbol = '=' - tmp_units = 'deg C' - - ! For some reasons these lines are needed to make VMCON CONVERGE .... - if ( i_tf_sup == 0 ) then ! Copper case - temp_cp_average = temp_cp_average + 273.15D0 - tcpav2 = tcpav2 + 273.15D0 - end if - - - - end subroutine constraint_eqn_043 - - subroutine constraint_eqn_044(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for centrepost temperature upper limit (TART) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for centrepost temperature upper limit (TART) - !! #=# tfcoil - !! #=#=# fptemp, ptempalw - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fptemp : input real : f-value for peak centrepost temperature - !! ptempalw : input real : maximum peak centrepost temperature (K) - !! tcpmax : input real : peak centrepost temperature (K) - !! itart : input integer : switch for spherical tokamak (ST) models:
    - !!
  • = 0 use conventional aspect ratio models; - !!
  • = 1 use spherical tokamak models
- use constraint_variables, only: fptemp - use tfcoil_variables, only: ptempalw, tcpmax - use physics_variables, only: itart - use tfcoil_variables, only: i_tf_sup - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! if the machine isn't a ST then report error - if (itart == 0) call report_error(8) - - ! For some reasons these lines are needed to make VMCON CONVERGE .... - if ( i_tf_sup == 0 ) then ! Copper case - ptempalw = ptempalw - 273.15D0 - tcpmax = tcpmax - 273.15D0 - end if - - tmp_cc = tcpmax/ptempalw - 1.0D0 * fptemp - tmp_con = ptempalw * (1.0D0 - tmp_cc) - tmp_err = tcpmax * tmp_cc - tmp_symbol = '<' - tmp_units = 'deg C' - - ! For some reasons these lines are needed to make VMCON CONVERGE .... - if ( i_tf_sup == 0 ) then ! Copper case - ptempalw = ptempalw + 273.15D0 - tcpmax = tcpmax + 273.15D0 - end if - - end subroutine constraint_eqn_044 - - subroutine constraint_eqn_045(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for edge safety factor lower limit (TART) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for edge safety factor lower limit (TART) - !! #=# tfcoil - !! #=#=# fq, q95_min - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fq : input real : f-value for edge safety factor - !! q95 : safety factor 'near' plasma edge - !! (unless i_plasma_current = 2 (ST current scaling), in which case q = mean edge safety factor qbar) - !! q95_min : input real : lower limit for edge safety factor - !! itart : input integer : switch for spherical tokamak (ST) models:
    - !!
  • = 0 use conventional aspect ratio models; - !!
  • = 1 use spherical tokamak models
- use constraint_variables, only: fq - use physics_variables, only: q95, q95_min, itart - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! if the machine isn't a ST then report error - if (itart == 0) call report_error(9) - tmp_cc = 1.0D0 - fq * q95/q95_min - tmp_con = q95_min * (1.0D0 - tmp_cc) - tmp_err = q95_min * tmp_cc - tmp_symbol = '<' - tmp_units = '' - - end subroutine constraint_eqn_045 - - subroutine constraint_eqn_046(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for Ip/Irod upper limit (TART) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for Ip/Irod upper limit (TART) - !! #=# tfcoil - !! #=#=# fipir, cratmx - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! eps : input real : inverse aspect ratio - !! fipir : input real : f-value for Ip/Irod upper limit - !! c_tf_total : input real : total (summed) current in TF coils (A) - !! plasma_current : input real : plasma current (A) - !! itart : input integer : switch for spherical tokamak (ST) models:
    - !!
  • = 0 use conventional aspect ratio models; - !!
  • = 1 use spherical tokamak models
- use physics_variables, only: eps, plasma_current, itart - use constraint_variables, only: fipir - use tfcoil_variables, only: c_tf_total - implicit none - ! cratmx : local real : maximum ratio of plasma current to centrepost current - real(dp) :: cratmx - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! if the machine isn't a ST then report error - if (itart == 0) call report_error(10) - cratmx = 1.0D0 + 4.91D0*(eps-0.62D0) - tmp_cc = (plasma_current / c_tf_total) / cratmx - 1.0D0 * fipir - tmp_con = cratmx * (1.0D0 - tmp_cc) - tmp_err = plasma_current/c_tf_total * tmp_cc - tmp_symbol = '<' - tmp_units = '' - - end subroutine constraint_eqn_046 - - subroutine constraint_eqn_047(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Issue #508 Remove RFP option: Relevant only to reversed field pinch devices - !! author: P B Lloyd, CCFE, Culham Science Centre - !! Issue #508 Remove RFP option: Relevant only to reversed field pinch devices - !! Equation for TF coil toroidal thickness upper limit - !! #=# empty - !! #=#=# empty - implicit none - ! Dummy formal arguments, just to comply with the subroutine interface - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - - end subroutine constraint_eqn_047 - - subroutine constraint_eqn_048(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for poloidal beta upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for poloidal beta upper limit - !! #=# physics - !! #=#=# fbeta_poloidal, beta_poloidal_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fbeta_poloidal : input real : rf-value for poloidal beta - !! beta_poloidal_max : input real : maximum poloidal beta - !! beta_poloidal : input real : poloidal beta - use constraint_variables, only: fbeta_poloidal, beta_poloidal_max - use physics_variables, only: beta_poloidal - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = beta_poloidal/beta_poloidal_max - 1.0D0 * fbeta_poloidal - tmp_con = beta_poloidal_max * (1.0D0 - tmp_cc) - tmp_err = beta_poloidal * tmp_cc - tmp_symbol = '<' - tmp_units = '' - - end subroutine constraint_eqn_048 - - subroutine constraint_eqn_049(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Issue #508 Remove IFE option: Equation for repetition rate upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! Issue #508 Remove IFE option: Equation for repetition rate upper limit - !! #=# empty - !! #=#=# empty - ! Dummy formal arguments, just to comply with the subroutine interface - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - - end subroutine constraint_eqn_049 - - subroutine constraint_eqn_050(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! IFE option: Equation for repetition rate upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! author: S I Muldrew, CCFE, Culham Science Centre - !! IFE option: Equation for repetition rate upper limit - !! #=# IFE - !! #=#=# frrmax, rrmax - use ife_variables, only: frrmax, ife, rrmax, reprat - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - if (ife /= 1) then - call report_error(12) - end if - - tmp_cc = reprat/rrmax - 1.0D0 * frrmax - tmp_con = rrmax * (1.0D0 - tmp_cc) - tmp_err = reprat * tmp_cc - tmp_symbol = '<' - tmp_units = 'Hz' - - end subroutine constraint_eqn_050 - - subroutine constraint_eqn_051(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation to enforce startup flux = available startup flux - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation to enforce startup flux = available startup flux - !! #=# pfcoil - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! vs_plasma_res_ramp : input real : resistive losses in startup V-s (Wb) - !! vs_plasma_ind_ramp : input real : internal and external plasma inductance V-s (Wb)) - !! vs_cs_pf_total_ramp : input real : total flux swing for startup (Wb) - use physics_variables, only: vs_plasma_res_ramp, vs_plasma_ind_ramp - use pfcoil_variables, only: vs_cs_pf_total_ramp, fvs_cs_pf_total_ramp - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - fvs_cs_pf_total_ramp * abs((vs_plasma_res_ramp+vs_plasma_ind_ramp) / vs_cs_pf_total_ramp) - tmp_con = vs_cs_pf_total_ramp * (1.0D0 - tmp_cc) - tmp_err = vs_cs_pf_total_ramp * tmp_cc - tmp_symbol = '=' - tmp_units = 'V.s' - - end subroutine constraint_eqn_051 - - subroutine constraint_eqn_052(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for tritium breeding ratio lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for tritium breeding ratio lower limit - !! #=# fwbs - !! #=#=# ftbr, tbrmin - !! ? TODO should this only be for certain blanket models ? - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ftbr : input real : f-value for minimum tritium breeding ratio - !! tbr : input real : tritium breeding ratio (i_blanket_type=2,3 (KIT HCPB/HCLL)) - !! tbrmin : input real : minimum tritium breeding ratio (If i_blanket_type=1, tbrmin=minimum 5-year time-averaged tritium breeding ratio) - use constraint_variables, only: ftbr, tbrmin - use fwbs_variables, only: tbr - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - ftbr * tbr/tbrmin - tmp_con = tbrmin * (1.0D0 - tmp_cc) - tmp_err = tbrmin * tmp_cc - tmp_symbol = '>' - tmp_units = '' - - end subroutine constraint_eqn_052 - - subroutine constraint_eqn_053(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for fast neutron fluence on TF coil upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for fast neutron fluence on TF coil upper limit - !! #=# fwbs - !! #=#=# fflutf, nflutfmax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fflutf : input real : f-value for maximum TF coil nuclear heating - !! nflutfmax : input real : max fast neutron fluence on TF coil (n/m2) - !! nflutf : input real : peak fast neutron fluence on TF coil superconductor (n/m2) - use constraint_variables, only: fflutf, nflutfmax - use fwbs_variables, only: nflutf - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = nflutf/nflutfmax - 1.0D0 * fflutf - tmp_con = nflutfmax * (1.0D0 - tmp_cc) - tmp_err = nflutf * tmp_cc - tmp_symbol = '<' - tmp_units = 'neutron/m2' - - end subroutine constraint_eqn_053 - - subroutine constraint_eqn_054(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for peak TF coil nuclear heating upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for peak TF coil nuclear heating upper limit - !! #=# fwbs - !! #=#=# fptfnuc, ptfnucmax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fptfnuc : input real : f-value for maximum TF coil nuclear heating - !! ptfnucmax : input real : maximum nuclear heating in TF coil (MW/m3) - !! ptfnucpm3 : input real : nuclear heating in the TF coil (MW/m3) (blktmodel>0) - use constraint_variables, only: fptfnuc, ptfnucmax - use fwbs_variables, only: ptfnucpm3 - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = ptfnucpm3/ptfnucmax - 1.0D0 * fptfnuc - tmp_con = ptfnucmax * (1.0D0 - tmp_cc) - tmp_err = ptfnucpm3 * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW/m3' - - end subroutine constraint_eqn_054 - - subroutine constraint_eqn_055(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! vvhemax is no longer calculated in PROCESS and this constraint is disabled - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - call report_error(173) - end subroutine constraint_eqn_055 - - subroutine constraint_eqn_056(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for power through separatrix / major radius upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for power through separatrix / major radius upper limit - !! #=# current_drive - !! #=#=# fnbshinef, f_p_beam_shine_through_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fpsepr : input real : f-value for maximum Psep/R limit - !! pseprmax : input real : maximum ratio of power crossing the separatrix to plasma major radius (Psep/R) (MW/m) - !! p_plasma_separatrix_mw : input real : power to be conducted to the divertor region (MW) - !! rmajor : input real : plasma major radius (m) - use constraint_variables, only: fpsepr, pseprmax - use physics_variables, only: p_plasma_separatrix_mw, rmajor - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = (p_plasma_separatrix_mw/rmajor)/pseprmax - 1.0D0 * fpsepr - tmp_con = pseprmax * (1.0D0 - tmp_cc) - tmp_err = (p_plasma_separatrix_mw/rmajor) * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW/m' - - end subroutine constraint_eqn_056 - - subroutine constraint_eqn_057(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Obsolete - !! author: P B Lloyd, CCFE, Culham Science Centre - !! Obsolete - !! #=# empty - !! #=#=# empty - ! Dummy formal arguments, just to comply with the subroutine interface - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - - end subroutine constraint_eqn_057 - - subroutine constraint_eqn_058(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Obsolete - !! author: P B Lloyd, CCFE, Culham Science Centre - !! Obsolete - !! #=# empty - !! #=#=# empty - ! Dummy formal arguments, just to comply with the subroutine interface - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 0 - tmp_con = 0 - tmp_err = 0 - tmp_symbol = '' - tmp_units = '' - - end subroutine constraint_eqn_058 - - subroutine constraint_eqn_059(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for neutral beam shine-through fraction upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for neutral beam shine-through fraction upper limit - !! #=# current_drive - !! #=#=# fnbshinef, f_p_beam_shine_through_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fnbshinef : input real : f-value for maximum neutral beam shine-through fraction - !! f_p_beam_shine_through_max : input real : maximum neutral beam shine-through fraction - !! f_p_beam_shine_through : input real : neutral beam shine-through fraction - use constraint_variables, only: fnbshinef, f_p_beam_shine_through_max - use current_drive_variables, only: f_p_beam_shine_through - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - tmp_cc = f_p_beam_shine_through/f_p_beam_shine_through_max - 1.0D0 * fnbshinef - tmp_con = f_p_beam_shine_through_max * (1.0D0 - tmp_cc) - tmp_err = f_p_beam_shine_through * tmp_cc - tmp_symbol = '<' - tmp_units = '' - end subroutine constraint_eqn_059 - - subroutine constraint_eqn_060(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for Central Solenoid s/c temperature margin lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for Central Solenoid s/c temperature margin lower limit - !! #=# tfcoil - !! #=#=# ftmargoh, tmargmin_cs - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ftmargoh : input real : f-value for central solenoid temperature margin - !! temp_cs_margin : input real : Central solenoid temperature margin (K) - !! tmargmin_cs : input real : Minimum allowable temperature margin : CS (K) - use constraint_variables, only: ftmargoh - use pfcoil_variables, only: temp_cs_margin - use tfcoil_variables, only: tmargmin_cs - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - ftmargoh * temp_cs_margin/tmargmin_cs - tmp_con = tmargmin_cs - tmp_err = tmargmin_cs - temp_cs_margin - tmp_symbol = '>' - tmp_units = 'K' - - end subroutine constraint_eqn_060 - - subroutine constraint_eqn_061(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for availability lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for availability limit - !! #=# cost - !! #=#=# favail, avail_min - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! favail : input real : F-value for minimum availability - !! cfactr : input real : Total plant availability fraction - !! avail_min : input real : Minimum availability - use cost_variables, only: favail, cfactr, avail_min - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - favail * cfactr / avail_min - tmp_con = avail_min * (1.0D0 - tmp_cc) - tmp_err = cfactr * tmp_cc - tmp_symbol = '>' - tmp_units = '' - - end subroutine constraint_eqn_061 - - subroutine constraint_eqn_062(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement times - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement times - !! #=# physics - !! #=#=# falpha_energy_confinement, f_alpha_energy_confinement_min - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! falpha_energy_confinement : input real : f-value for lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement - !! t_alpha_confinement : input real : alpha particle confinement time (s) - !! t_energy_confinement : input real : global thermal energy confinement time (sec) - !! f_alpha_energy_confinement_min : input real : Lower limit on f_alpha_energy_confinement the ratio of alpha particle to energy confinement times - use constraint_variables, only: falpha_energy_confinement, f_alpha_energy_confinement_min - use physics_variables, only: t_alpha_confinement, t_energy_confinement - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - falpha_energy_confinement * (t_alpha_confinement / t_energy_confinement) / f_alpha_energy_confinement_min - tmp_con = f_alpha_energy_confinement_min - tmp_err = (t_alpha_confinement / t_energy_confinement) * tmp_cc - tmp_symbol = '>' - tmp_units = '' - - end subroutine constraint_eqn_062 - - subroutine constraint_eqn_063(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit on niterpump (vacuum_model = simple) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Upper limit on niterpump (vacuum_model = simple) - !! #=# vacuum - !! #=#=# fniterpump, tfno - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fniterpump : input real : f-value for constraint that number of pumps < tfno - !! tfno : input real : number of TF coils (default = 50 for stellarators) - !! niterpump : input real : number of high vacuum pumps (real number), each with the throughput - use constraint_variables, only: fniterpump - use tfcoil_variables, only: n_tf_coils - use vacuum_variables, only: niterpump - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = niterpump/n_tf_coils - 1.0D0 * fniterpump - tmp_con = n_tf_coils - tmp_err = n_tf_coils * tmp_cc - tmp_symbol = '<' - tmp_units = '' - - end subroutine constraint_eqn_063 - - subroutine constraint_eqn_064(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit on Zeff - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Upper limit on Zeff - !! #=# physics - !! #=#=# fzeffmax, zeffmax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fzeffmax : input real : f-value for maximum zeff - !! zeffmax : input real : maximum value for Zeff - !! zeff : input real : plasma effective charge - use constraint_variables, only: fzeffmax, zeffmax - use physics_variables, only: zeff - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = zeff/zeffmax - 1.0D0 * fzeffmax - tmp_con = zeffmax - tmp_err = zeffmax * tmp_cc - tmp_symbol = '<' - tmp_units = '' - - end subroutine constraint_eqn_064 - - subroutine constraint_eqn_065(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit on stress of the vacuum vessel that occurs when the TF coil quenches. - !! author: Timothy Nunn, UKAEA - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fmaxvvstress : input real : f-value for constraint on maximum VV stress - !! max_vv_stress : input real : Maximum permitted stress of the VV (Pa) - !! vv_stress_quench : input real : Stress of the VV (Pa) - use constraint_variables, only: fmaxvvstress - use tfcoil_variables, only: max_vv_stress - use sctfcoil_module, only: vv_stress_quench - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = vv_stress_quench / max_vv_stress - 1.0d0 * fmaxvvstress - tmp_con = max_vv_stress - tmp_err = max_vv_stress * tmp_cc - tmp_symbol = '<' - tmp_units = 'Pa' - - end subroutine constraint_eqn_065 - - subroutine constraint_eqn_066(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit on rate of change of energy in poloidal field - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Limit on rate of change of energy in poloidal field - !! #=# pfcoil - !! #=#=# fpoloidalpower, maxpoloidalpower - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fpoloidalpower : input real : f-value for constraint on rate of change of energy in poloidal field - !! maxpoloidalpower : input real : Maximum permitted absolute rate of change of stored energy in poloidal field (MW) - !! peakpoloidalpower : input real : Peak absolute rate of change of stored energy in poloidal field (MW) (11/01/16) - use constraint_variables, only: fpoloidalpower - use pf_power_variables, only: maxpoloidalpower, peakpoloidalpower - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = peakpoloidalpower / maxpoloidalpower - 1.0d0 * fpoloidalpower - tmp_con = maxpoloidalpower - tmp_err = maxpoloidalpower * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW' - - end subroutine constraint_eqn_066 - - subroutine constraint_eqn_067(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Simple upper limit on radiation wall load - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Simple upper limit on radiation wall load - !! #=# physics - !! #=#=# fradwall, pflux_fw_rad_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fradwall : input real : f-value for upper limit on radiation wall load - !! pflux_fw_rad_max : input real : Maximum permitted radiation wall load (MW/m^2) - !! pflux_fw_rad_max_mw : input real : Peak radiation wall load (MW/m^2) - use constraint_variables, only: fradwall, pflux_fw_rad_max, pflux_fw_rad_max_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = pflux_fw_rad_max_mw / pflux_fw_rad_max - 1.0d0 * fradwall - tmp_con = pflux_fw_rad_max - tmp_err = pflux_fw_rad_max * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW/m^2' - - end subroutine constraint_eqn_067 - - subroutine constraint_eqn_068(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit on Psep scaling (PsepB/qAR) - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! New Psep scaling (PsepB/qAR) - !! Issue #464 - !! #=# physics - !! #=#=# fpsepbqar, psepbqarmax - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fpsepbqar : input real : f-value for upper limit on psepbqar, maximum Psep*Bt/qAR limit - !! psepbqarmax : input real : maximum permitted value of ratio of Psep*Bt/qAR (MWT/m) - !! p_plasma_separatrix_mw : input real : Power to conducted to the divertor region (MW) - !! bt : input real : toroidal field on axis (T) (iteration variable 2) - !! q95 : input real : safety factor q at 95% flux surface - !! aspect : input real : aspect ratio (iteration variable 1) - !! rmajor : input real : plasma major radius (m) (iteration variable 3) - !! i_q95_fixed : input int : Switch that allows for fixing q95 only in this constraint. - !! q95_fixed : input real : fixed safety factor q at 95% flux surface - use constraint_variables, only: fpsepbqar, psepbqarmax, i_q95_fixed, q95_fixed - use physics_variables, only: p_plasma_separatrix_mw, bt, q95, aspect, rmajor - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - if (i_q95_fixed == 1) then - tmp_cc = ((p_plasma_separatrix_mw*bt)/(q95_fixed*aspect*rmajor)) / psepbqarmax - 1.0d0 * fpsepbqar - tmp_err = (p_plasma_separatrix_mw*bt)/(q95_fixed*aspect*rmajor) - psepbqarmax - else - tmp_cc = ((p_plasma_separatrix_mw*bt)/(q95*aspect*rmajor)) / psepbqarmax - 1.0d0 * fpsepbqar - tmp_err = (p_plasma_separatrix_mw*bt)/(q95*aspect*rmajor) - psepbqarmax - end if - tmp_con = psepbqarmax - tmp_symbol = '<' - tmp_units = 'MWT/m' - - end subroutine constraint_eqn_068 - - subroutine constraint_eqn_069(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Ensure separatrix power is less than value from Kallenbach divertor - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Ensure separatrix power is less than value from Kallenbach divertor - !! #=# divertor_kallenbach - !! #=#=# consistency, psep_kallenbach - !! fpsep has been removed from the equation. - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! psep_kallenbach : input real : Power conducted through the separatrix, as calculated by the divertor model [W] - !! p_plasma_separatrix_mw : input real : power to conducted to the divertor region (MW) - ! use div_kal_vars, only: psep_kallenbach - use physics_variables, only: p_plasma_separatrix_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! From Kallenbach model, should be reserved if the model is going to be added back - - end subroutine constraint_eqn_069 - - subroutine constraint_eqn_070(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Separatrix density consistency - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Separatrix density consistency - !! #=# divertor_kallenbach - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! teomp : input real : Separatrix temperature calculated by the Kallenbach divertor model [eV] - !! tesep : input real : Electron temperature at separatrix [keV] - ! use div_kal_vars, only: teomp - use physics_variables, only: tesep - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! From Kallenbach model, should be reserved if the model is going to be added back - - end subroutine constraint_eqn_070 - - subroutine constraint_eqn_071(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! TODO Remove - !! Separatrix density consistency - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Separatrix density consistency - !! #=# divertor_kallenbach - !! #=#=# consistency - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! neomp : input real : Mean SOL density at OMP calculated by the Kallenbach divertor model [m-3] - !! nesep : input real : electron density at separatrix [m-3] (ipedestal=1,2, calculated if 3) - !! neratio : input real : Ratio of mean SOL density at OMP to separatrix density at OMP (iteration variable 121) - ! use div_kal_vars, only: neomp, neratio - use physics_variables, only: nesep - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! From Kallenbach model, should be reserved if the model is going to be added back - - end subroutine constraint_eqn_071 - - subroutine constraint_eqn_072(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit on central Solenoid Tresca yield stress - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Central Solenoid Tresca yield criterion - !! #=# pfcoil - !! #=#=# foh_stress, alstroh - !! In the case if the bucked and wedged option ( i_tf_bucking >= 2 ) the constrained - !! stress is the largest the largest stress of the - !! - CS stress at maximum current (conservative as the TF inward pressure is not taken - !! into account) - !! - CS stress at flux swing (no current in CS) from the TF inward pressure - !! This allow to cover the 2 worst stress scenario in the bucked and wedged design - !! Otherwise (free standing TF), the stress limits are only set by the CS stress at max current - !! Reverse the sign so it works as an inequality constraint (tmp_cc > 0) - !! This will have no effect if it is used as an equality constraint because it will be squared. - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! foh_stress : input real : f-value for Tresca yield criterion in Central Solenoid - !! alstroh : input real : allowable hoop stress in Central Solenoid structural material (Pa) - !! s_shear_cs_peak : input real : Maximum shear stress coils/central solenoid (Pa) - !! sig_tf_cs_bucked : input real : Maximum shear stress in CS case at flux swing (no current in CS) - !! can be significant for the bucked and weged design - !! i_tf_bucking : input integer : switch for TF structure design - use constraint_variables, only: foh_stress - use pfcoil_variables, only: alstroh, s_shear_cs_peak - use tfcoil_variables, only: sig_tf_cs_bucked, i_tf_bucking - use build_variables, only: i_tf_inside_cs - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! bucked and wedged desing (see subroutine comment) - if ( i_tf_bucking >= 2 .and. i_tf_inside_cs == 0 ) then - tmp_cc = max(s_shear_cs_peak, sig_tf_cs_bucked) / alstroh - 1.0d0 * foh_stress - tmp_err = alstroh - max(s_shear_cs_peak, sig_tf_cs_bucked) - ! Free standing CS - else - tmp_cc = s_shear_cs_peak / alstroh - 1.0d0 * foh_stress - tmp_err = alstroh - s_shear_cs_peak - end if - - tmp_con = alstroh - tmp_symbol = '<' - tmp_units = 'Pa' - - end subroutine constraint_eqn_072 - - subroutine constraint_eqn_073(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Lower limit to ensure separatrix power is greater than the L-H power + auxiliary power - !! Related to constraint 15 - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Ensure separatrix power is greater than the L-H power + auxiliary power - !! #=# physics - !! #=#=# fplhsep, p_plasma_separatrix_mw - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fplhsep : input real : F-value for Psep >= Plh + Paux : for consistency of two values of separatrix power - !! p_l_h_threshold_mw : input real : L-H mode power threshold (MW) - !! p_plasma_separatrix_mw : input real : power to be conducted to the divertor region (MW) - !! p_hcd_injected_total_mw : inout real : total auxiliary injected power (MW) - use physics_variables, only: fplhsep, p_l_h_threshold_mw, p_plasma_separatrix_mw - use current_drive_variables, only: p_hcd_injected_total_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0d0 - fplhsep * p_plasma_separatrix_mw / (p_l_h_threshold_mw+p_hcd_injected_total_mw) - tmp_con = p_plasma_separatrix_mw - tmp_err = p_plasma_separatrix_mw * tmp_cc - tmp_symbol = '>' - tmp_units = 'MW' - - end subroutine constraint_eqn_073 - - subroutine constraint_eqn_074(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit to ensure TF coil quench temperature < tmax_croco - !! ONLY used for croco HTS coil - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Ensure TF coil quench temperature < tmax_croco ONLY used for croco HTS coil - !! #=# physics - !! #=#=# fcqt, tmax_croco - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fcqt : input real : f-value: TF coil quench temparature remains below tmax_croco - !! croco_quench_temperature : input real : CroCo strand: Actual temp reached during a quench (K) - !! tmax_croco : input real : CroCo strand: maximum permitted temp during a quench (K) - use constraint_variables, only: fcqt - use tfcoil_variables, only: croco_quench_temperature, tmax_croco - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = croco_quench_temperature / tmax_croco - 1.0d0 * fcqt - tmp_con = croco_quench_temperature - tmp_err = croco_quench_temperature * tmp_cc - tmp_symbol = '<' - tmp_units = 'K' - - end subroutine constraint_eqn_074 - - subroutine constraint_eqn_075(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit to ensure that TF coil current / copper area < Maximum value - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Ensure that TF coil current / copper area < Maximum value - !! ONLY used for croco HTS coil - !! #=# physics - !! #=#=# f_coppera_m2, copperA_m2_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! copperA_m2 : input real : TF coil current / copper area (A/m2) - !! copperA_m2_max : input real : Maximum TF coil current / copper area (A/m2) - !! f_coppera_m2 : input real : f-value for TF coil current / copper area < copperA_m2_max - use rebco_variables, only: copperA_m2, copperA_m2_max, f_coppera_m2 - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = copperA_m2 / copperA_m2_max - 1.0d0 * f_coppera_m2 - tmp_con = copperA_m2 - tmp_err = copperA_m2 * tmp_cc - tmp_symbol = '<' - tmp_units = 'A/m2' - - end subroutine constraint_eqn_075 - - subroutine constraint_eqn_076(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit for Eich critical separatrix density model: Added for issue 558 - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Eich critical separatrix density model - !! Added for issue 558 with ref to http://iopscience.iop.org/article/10.1088/1741-4326/aaa340/pdf - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! alpha_crit : output real : critical ballooning parameter value - !! nesep_crit : output real : critical electron density at separatrix [m-3] - !! kappa : input real : plasma separatrix elongation (calculated if i_plasma_geometry = 1-5, 7 or 9) - !! triang : input real : plasma separatrix triangularity (calculated if i_plasma_geometry = 1, 3-5 or 7) - !! aspect : input real : aspect ratio (iteration variable 1) - !! p_plasma_separatrix_mw : input real : power to conducted to the divertor region (MW) - !! dlimit(7) : input real array : density limit (/m3) as calculated using various models - !! fnesep : input real : f-value for Eich critical separatrix density - use physics_variables, only: alpha_crit, nesep_crit, kappa, triang, & - aspect, p_plasma_separatrix_mw, dlimit, nesep - use constraint_variables, only: fnesep - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - alpha_crit = (kappa ** 1.2D0) * (1.0D0 + 1.5D0 * triang) - nesep_crit = 5.9D0 * alpha_crit * (aspect ** (-2.0D0/7.0D0)) * & - (((1.0D0 + (kappa ** 2.0D0)) / 2.0D0) ** (-6.0D0/7.0D0)) & - * ((p_plasma_separatrix_mw* 1.0D6) ** (-11.0D0/70.0D0)) * dlimit(7) - tmp_cc = nesep / nesep_crit - 1.0D0 * fnesep - tmp_con = nesep - tmp_err = nesep * tmp_cc - tmp_symbol = '<' - tmp_units = 'm-3' - - end subroutine constraint_eqn_076 - - subroutine constraint_eqn_077(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for maximum TF current per turn upper limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; residual error in physical units; output string; units string - !! Equation for maximum TF current per turn upper limit - !! #=# tfcoil - !! #=#=# fcpttf, cpttf, cpttf_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fcpttf : input : f-value for TF coil current per turn - !! cpttf_max : input : allowable TF coil current per turn [A/turn] - !! cpttf : input : TF coil current per turn [A/turn] - use constraint_variables, only: fcpttf - use tfcoil_variables, only: cpttf_max, cpttf - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = cpttf / cpttf_max - 1.0D0 * fcpttf - tmp_con = cpttf_max - tmp_err = cpttf_max * tmp_cc - tmp_symbol = '<' - tmp_units = 'A/turn' - - end subroutine constraint_eqn_077 - - subroutine constraint_eqn_078(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for Reinke criterion, divertor impurity fraction lower limit - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; residual error in physical units; output string; units string - !! Equation for Reinke criterion, divertor impurity fraction lower limit - !! #=# divertor - !! #=#=# freinke, fzactual, fzmin - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present; - !! and con will be printed out only if present. Thesw conditions were missing. - !! freinke : input : f-value for Reinke criterion (itv 147) - !! fzmin : input : minimum impurity fraction from Reinke model - !! fzactual : input : actual impurity fraction - use constraint_variables, only: freinke - use reinke_variables, only: fzactual, fzmin - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! write(*,*) 'freinke, fzact, fzmin = ', freinke, ', ', fzactual, ', ', fzmin - ! 1.0, 0.0, value - tmp_cc = 1.0D0 - freinke * fzactual/fzmin - !The following two pre-existing lines are not understood: - !KE note - cc is always 1, code never enters IF statement... - tmp_con = fzmin * (1.0D0 - tmp_cc) - tmp_err = fzmin * tmp_cc - tmp_symbol = '>' - tmp_units = '' - ! write(*,*) 'cc, con = ', tmp_cc, ', ', tmp_con - ! write(*,*) 'freinke, fzactual, fzmin = ', freinke, ', ', fzactual, ', ', fzmin - - end subroutine constraint_eqn_078 - - subroutine constraint_eqn_079(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for maximum CS field - !! author: P B Lloyd, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; residual error in physical units; output string; units string - !! Equation for maximum CS field - !! #=# pfcoil - !! #=#=# fb_cs_limit_max, b_cs_peak_flat_top_end, b_cs_peak_pulse_start, b_cs_limit_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fb_cs_limit_max : input : F-value for CS mmax field (cons. 79, itvar 149) - !! b_cs_limit_max : input : Central solenoid max field limit [T] - !! b_cs_peak_pulse_start : input : maximum field in central solenoid at beginning of pulse (T) - !! b_cs_peak_flat_top_end : input real : maximum field in central solenoid at end of flat-top (EoF) (T) - !! (Note: original code has "b_cs_peak_flat_top_end/b_cs_peak_pulse_start | peak CS field [T]".) - use pfcoil_variables, only: fb_cs_limit_max, b_cs_limit_max, b_cs_peak_pulse_start, b_cs_peak_flat_top_end - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = max(b_cs_peak_flat_top_end, b_cs_peak_pulse_start) / b_cs_limit_max - 1.0D0 * fb_cs_limit_max - tmp_con = b_cs_limit_max - tmp_err = max(b_cs_peak_flat_top_end, b_cs_peak_pulse_start) * tmp_cc - tmp_symbol = '<' - tmp_units = 'A/turn' - - end subroutine constraint_eqn_079 - - subroutine constraint_eqn_080(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for p_plasma_separatrix_mw lower limit - !! author: J Morris, Culham Science Centre - !! args : output structure : residual error; constraint value; residual error in physical units; - !! output string; units string - !! Lower limit p_plasma_separatrix_mw - !! #=# physics - !! #=#=# fp_plasma_separatrix_min_mw, p_plasma_separatrix_mw - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fp_plasma_separatrix_min_mw : input : F-value for lower limit on p_plasma_separatrix_mw (cons. 80, itvar 153) - !! p_plasma_separatrix_min_mw : input : Minimum power crossing separatrix p_plasma_separatrix_mw [MW] - !! p_plasma_separatrix_mw : input : Power crossing separatrix [MW] - use physics_variables, only: fp_plasma_separatrix_min_mw, p_plasma_separatrix_mw - use constraint_variables, only : p_plasma_separatrix_min_mw - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - tmp_cc = 1.0D0 - fp_plasma_separatrix_min_mw * p_plasma_separatrix_mw / p_plasma_separatrix_min_mw - tmp_con = p_plasma_separatrix_min_mw - tmp_err = p_plasma_separatrix_mw * tmp_cc - tmp_symbol = '>' - tmp_units = 'MW' - - end subroutine constraint_eqn_080 - - subroutine constraint_eqn_081(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Lower limit to ensure central density is larger that the pedestal one - !! author: S Kahn, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Lower limit ne0 > neped - !! !#=# physics - !! !#=#=# ne0, neped - !! Logic change during pre-factoring: err, symbol, units will be - !! assigned only if present. - !! fne0 : input : F-value for constraint on ne0 > neped - !! ne0 : input : Central electron density [m-3] - !! neped : input : Electron density at pedestal [m-3] - use physics_variables, only: ne0, fne0, neped - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - tmp_cc = 1.0D0 - fne0 * ne0/neped - tmp_con = fne0 - tmp_err = fne0 * tmp_cc - tmp_symbol = '>' - tmp_units = '/m3' - - end subroutine constraint_eqn_081 - - subroutine constraint_eqn_082(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for toroidal consistency of stellarator build - !! author: J Lion, IPP Greifswald - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! toroidalgap > dx_tf_inboard_out_toroidal - !! #=# tfcoil - !! #=#=# dx_tf_inboard_out_toroidal, ftoroidalgap - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! ftoroidalgap : input real : f-value for constraint toroidalgap > dx_tf_inboard_out_toroidal - !! toroidalgap : input real : minimal gap between two stellarator coils - !! dx_tf_inboard_out_toroidal : input real : total toroidal width of a tf coil - use tfcoil_variables, only: dx_tf_inboard_out_toroidal,ftoroidalgap,toroidalgap - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - ftoroidalgap * toroidalgap/dx_tf_inboard_out_toroidal - tmp_con = toroidalgap - tmp_err = toroidalgap - dx_tf_inboard_out_toroidal/ftoroidalgap - tmp_symbol = '<' - tmp_units = 'm' - - end subroutine constraint_eqn_082 - - subroutine constraint_eqn_083(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for radial consistency of stellarator build - !! author: J Lion, IPP Greifswald - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! available_radial_space > required_radial_space - !! #=# build - !! #=#=# required_radial_space, f_avspace - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! f_avspace : input real : f-value for constraint available_radial_space > required_radial_space - !! available_radial_space : input real : avaible space in radial direction as given by each s.-configuration - !! required_radial_space : input real : required space in radial direction - use build_variables, only: available_radial_space, required_radial_space, f_avspace - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = 1.0D0 - f_avspace * available_radial_space/required_radial_space - tmp_con = available_radial_space * (1.0D0 - tmp_cc) - tmp_err = required_radial_space * tmp_cc - tmp_symbol = '<' - tmp_units = 'm' - end subroutine constraint_eqn_083 - - subroutine constraint_eqn_084(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for the lower limit of beta - !! author: J Lion, IPP Greifswald - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! (beta-beta_fast_alpha) > beta_min - !! #=# physics - !! #=#=# beta_fast_alpha, beta, fbeta_min - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fbeta_min : input real : f-value for constraint beta-beta_fast_alpha > beta_min - !! beta_min : input real : Lower limit for beta - !! beta : input real : plasma beta - - use physics_variables, only: beta_min, beta - use constraint_variables, only: fbeta_min - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - - tmp_cc = 1.0D0 - fbeta_min * (beta)/beta_min - tmp_con = beta_min * (1.0D0 - tmp_cc) - tmp_err = (beta) * tmp_cc - tmp_symbol = '>' - tmp_units = '' - - - end subroutine constraint_eqn_084 - - subroutine constraint_eqn_085(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equality constraint for the centerpost (CP) lifetime - !! Author : S Kahn - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Depending on the chosen option : i_cp_lifetime - !! - 0 : The CP full power year lifelime is set by the user (cplife_input) - !! - 1 : The CP lifelime is equal to the divertor one - !! - 2 : The CP lifetime is equal to the breeding blankets one - !! - 3 : The CP lifetime is equal to the plant one - !! #=# availability - !! #=#=# consistency - !! Logic change during pre-factoring: err, symbol, units will be assigned - !! only if present. - !! cplife : input real : calculated CP full power year lifetime (years) - !! life_blkt_fpy : input real : calculated first wall/blanket power year lifetime (years) - !! divlife : input real : calculated divertor power year lifetime (years) - !! i_cp_lifetime : input integer : switch chosing which plant element the CP - !! the CP lifetime must equate - use cost_variables, only : cplife, divlife, cplife_input, & - tlife, i_cp_lifetime - use fwbs_variables, only : life_blkt_fpy - - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - !! Constraints output - - - ! The CP lifetime is equal to the the divertor one - if ( i_cp_lifetime == 0 ) then - tmp_cc = 1.0D0 - cplife/cplife_input - - else if ( i_cp_lifetime == 1 ) then - tmp_cc = 1.0D0 - cplife/divlife - - ! The CP lifetime is equal to the tritium breeding blankets / FW one - else if ( i_cp_lifetime == 2 ) then - tmp_cc = 1.0D0 - cplife/life_blkt_fpy - - ! The CP lifetime is equal to the - else if ( i_cp_lifetime == 3 ) then - tmp_cc = 1.0D0 - cplife/tlife - end if - - tmp_con = divlife * (1.0D0 - tmp_cc) - tmp_err = divlife * tmp_cc - tmp_symbol = '=' - tmp_units = 'years' - - end subroutine constraint_eqn_085 - - subroutine constraint_eqn_086(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit on the turn edge length in the TF winding pack - !! Author : S Kahn - !! args : output structure : residual error; constraint value; - !! residual error in physical units; - !! t_turn_tf : input real : TF coil turn edge length including turn insulation [m] - !! f_t_turn_tf : input real : f-value for TF turn edge length constraint - !! t_turn_tf_max : input real : TF turn edge length including turn insulation upper limit [m] - use tfcoil_variables, only : t_turn_tf, f_t_turn_tf, t_turn_tf_max - - implicit none - - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - !! Constraints output - tmp_cc = t_turn_tf / t_turn_tf_max - 1.0D0 * f_t_turn_tf - tmp_con = t_turn_tf_max * (1.0D0 - tmp_cc) - tmp_err = t_turn_tf_max * tmp_cc - tmp_symbol = '<' - tmp_units = 'm' - - end subroutine constraint_eqn_086 - - - subroutine constraint_eqn_087(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil cryogenic power upper limit - !! author: S. Kahn, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! crypmw : input real : cryogenic plant power (MW) - !! f_crypmw : input real : f-value for maximum cryogenic plant power - !! crypmw_max : input real : Maximum cryogenic plant power (MW) - - use heat_transport_variables, only: crypmw, crypmw_max, f_crypmw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = crypmw / crypmw_max - 1.0D0 * f_crypmw - tmp_con = crypmw_max * (1.0D0 - tmp_cc) - tmp_err = crypmw * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW' - end subroutine constraint_eqn_087 - - subroutine constraint_eqn_088(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for TF coil vertical strain upper limit (absolute value) - !! author: CPS Swanson, PPPL, USA - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! Equation for TF coil vertical strain upper limit (absolute value) - !! #=# tfcoil - !! #=#=# fstr_wp, str_wp_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! fstr_wp : input real : f-value for TF coil strain - !! str_wp_max : input real : Allowable maximum TF coil vertical strain - !! str_wp : input real : Constrained TF coil vertical strain - use constraint_variables, only: fstr_wp - use tfcoil_variables, only: str_wp_max, str_wp - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = abs(str_wp) / str_wp_max - 1.0D0 * fstr_wp - tmp_con = str_wp_max - tmp_err = str_wp_max - abs(str_wp) / fstr_wp - tmp_symbol = '<' - tmp_units = '' - end subroutine constraint_eqn_088 - - subroutine constraint_eqn_089(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Upper limit to ensure that the Central Solenoid [OH] coil current / copper area < Maximum value - !! author: G Turkington, CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! #=# physics - !! #=#=# f_copperaoh_m2, copperaoh_m2_max - !! and hence also optional here. - !! Logic change during pre-factoring: err, symbol, units will be assigned only if present. - !! copperaoh_m2 : input real : CS coil current at EOF / copper area [A/m2] - !! copperaoh_m2_max : input real : maximum coil current / copper area [A/m2] - !! f_copperaoh_m2 : input real : f-value for CS coil current / copper area - use rebco_variables, only: copperaoh_m2, copperaoh_m2_max, f_copperaoh_m2 - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - tmp_cc = copperaoh_m2 / copperaoh_m2_max - 1.0d0 * f_copperaoh_m2 - tmp_con = copperaoh_m2 - tmp_err = copperaoh_m2 * tmp_cc - tmp_symbol = '<' - tmp_units = 'A/m2' - - end subroutine constraint_eqn_089 - - subroutine constraint_eqn_090(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Lower limit for CS coil stress load cycles - !! author: A. Pearce, G Turkington CCFE, Culham Science Centre - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! fncycle : input real : f-value for constraint n_cycle > n_cycle_min - !! n_cycle : input real : Allowable number of cycles for CS - !! n_cycle_min : input real : Minimum required cycles for CS - use CS_fatigue_variables, only: n_cycle, n_cycle_min, bkt_life_csf - use constraint_variables, only: fncycle - use cost_variables, only: ibkt_life, bktcycles - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - !! Switch to relay the calculated fw/blanket lifetime cycles as the minimum required CS stress cycles. - !! bkt_life_cycle = 1 turns on the relay. Otherwise the models run independently. - if (ibkt_life == 1 .and. bkt_life_csf == 1 ) then - n_cycle_min = bktcycles - end if - - tmp_cc = 1.0D0 - fncycle * n_cycle / n_cycle_min - tmp_con = n_cycle_min * (1.0D0 - tmp_cc) - tmp_err = n_cycle * tmp_cc - tmp_symbol = '>' - tmp_units = '' - - end subroutine constraint_eqn_090 - - subroutine constraint_eqn_091(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Lower limit to ensure ECRH te is greater than required te for ignition - !! at lower values for n and B. Or if the design point is ECRH heatable (if i_plasma_ignited==0) - !! stellarators only (but in principle usable also for tokamaks). - !! author: J Lion, IPP Greifswald - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! powerht_local > powerscaling - !! #=# physics - !! #=#=# fecrh_ignition, powerht_local, powerscaling - !! fecrh_ignition : input real : f-value for constraint powerht_local > powerscaling - !! max_gyrotron_frequency : input real : Max. av. gyrotron frequency - !! te0_ecrh_achievable : input real : Max. achievable electron temperature at ignition point - use constraint_variables, only: fecrh_ignition - use stellarator_variables, only: max_gyrotron_frequency, te0_ecrh_achievable, powerscaling_constraint, powerht_constraint - use physics_variables, only: i_plasma_ignited - use current_drive_variables, only: p_hcd_primary_extra_heat_mw - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - ! Achievable ECRH te needs to be larger than needed te for igntion - if(i_plasma_ignited==0) then - tmp_cc = 1.0D0 - fecrh_ignition* (powerht_constraint+p_hcd_primary_extra_heat_mw)/powerscaling_constraint - else - tmp_cc = 1.0D0 - fecrh_ignition* powerht_constraint/powerscaling_constraint - endif - - tmp_con = powerscaling_constraint * (1.0D0 - tmp_cc) - tmp_err = powerht_constraint * tmp_cc - tmp_symbol = '<' - tmp_units = 'MW' - end subroutine constraint_eqn_091 - - subroutine constraint_eqn_092(tmp_cc, tmp_con, tmp_err, tmp_symbol, tmp_units) - !! Equation for checking is D/T ratio is consistent, and sums to 1. - !! author: G Turkington, UKAEA - !! args : output structure : residual error; constraint value; - !! residual error in physical units; output string; units string - !! f_deuterium : input : fraction of deuterium ions - !! f_tritium : input : fraction of tritium ions - !! f_helium3 : input : fraction of helium-3 ions - use physics_variables, only: f_deuterium, f_tritium, f_helium3 - implicit none - real(dp), intent(out) :: tmp_cc - real(dp), intent(out) :: tmp_con - real(dp), intent(out) :: tmp_err - character(len=1), intent(out) :: tmp_symbol - character(len=10), intent(out) :: tmp_units - - - ! Iterate over f_tritium and calculate f_deuterium - f_deuterium = 1.0D0 - (f_tritium + f_helium3) - tmp_cc = 1.0D0 - (f_deuterium + f_tritium + f_helium3) - tmp_con = 1.0D0 - tmp_err = tmp_con * tmp_cc - tmp_symbol = '=' - tmp_units = 'fraction' - - end subroutine constraint_eqn_092 - - -end module constraints diff --git a/source/fortran/constraint_variables.f90 b/source/fortran/constraint_variables.f90 index 6320d9d4d8..54b4183d54 100644 --- a/source/fortran/constraint_variables.f90 +++ b/source/fortran/constraint_variables.f90 @@ -310,99 +310,4 @@ module constraint_variables real(dp) :: fcqt !! TF coil quench temparature remains below tmax_croco !! (`constraint equation 74`, `iteration variable 141`) - - contains - - subroutine init_constraint_variables - !! Initialise module variables - implicit none - - auxmin = 0.1D0 - beta_poloidal_max = 0.19D0 - bigqmin = 10.0D0 - bmxlim = 12.0D0 - fauxmn = 1.0D0 - fbeta_poloidal_eps = 1.0D0 - fbeta_poloidal = 1.0D0 - fbeta_max = 1.0D0 - fbeta_min = 1.0D0 - fcpttf = 1.0D0 - fr_conducting_wall = 1.0D0 - fdene = 1.0D0 - fdtmp = 1.0D0 - fflutf = 1.0D0 - ffuspow = 1.0D0 - fgamcd = 1.0D0 - fpflux_div_heat_load_mw = 1.0D0 - fiooic = 0.5D0 - fipir = 1.0D0 - q95_fixed = 3.0D0 - fjohc = 1.0D0 - fjohc0 = 1.0D0 - fjprot = 1.0D0 - fl_h_threshold = 1.0D0 - fmva = 1.0D0 - fnbshinef = 1.0D0 - fncycle = 1.0D0 - fnesep = 1.0D0 - foh_stress = 1.0D0 - fpeakb = 1.0D0 - fpinj = 1.0D0 - fpnetel = 1.0D0 - fportsz = 1.0D0 - fpsepbqar = 1.0D0 - fpsepr = 1.0D0 - fptemp = 1.0D0 - fptfnuc = 1.0D0 - fq = 1.0D0 - fqval = 1.0D0 - fradpwr = 0.99D0 - fradwall = 1.0D0 - freinke = 1.0D0 - frminor = 1.0D0 - fstrcase = 1.0D0 - fstrcond = 1.0D0 - fstr_wp = 1.0D0 - fmaxvvstress = 1.0D0 - ftbr = 1.0D0 - ft_burn = 1.0D0 - ftcycl = 1.0D0 - ftmargoh = 1.0D0 - ftmargtf = 1.0D0 - ft_current_ramp_up = 1.0D0 - ftpeak = 1.0D0 - fvdump = 1.0D0 - fvs = 1.0D0 - fvvhe = 1.0D0 - fwalld = 1.0D0 - fzeffmax = 1.0D0 - gammax = 2.0D0 - i_q95_fixed = 0 - pflux_fw_rad_max = 1.0D0 - mvalim = 40.0D0 - f_p_beam_shine_through_max = 1.0D-3 - nflutfmax = 1.0D23 - p_plasma_separatrix_min_mw = 150.0D0 - f_fw_rad_max = 3.33D0 - pflux_fw_rad_max_mw = 0.0D0 - pnetelin = 1.0D3 - powfmax = 1.5D3 - psepbqarmax = 9.5D0 - pseprmax = 25.0D0 - ptfnucmax = 1.0D-3 - tbrmin = 1.1D0 - t_burn_min = 1.0D0 - tcycmn = 0.0D0 - t_current_ramp_up_min = 1.0D0 - vvhealw = 1.0D0 - walalw = 1.0D0 - f_alpha_energy_confinement_min = 5.0D0 - falpha_energy_confinement = 1.0D0 - fniterpump = 1.0D0 - zeffmax = 3.6D0 - fpoloidalpower = 1.0D0 - fpsep = 1.0D0 - fcqt = 1.0D0 - fecrh_ignition = 1.0D0 - end subroutine init_constraint_variables end module constraint_variables diff --git a/source/fortran/error_handling.f90 b/source/fortran/error_handling.f90 deleted file mode 100644 index a7388ff143..0000000000 --- a/source/fortran/error_handling.f90 +++ /dev/null @@ -1,354 +0,0 @@ -! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -#ifndef INSTALLDIR -#error INSTALLDIR not defined! -#endif - -module error_handling - - !! Error handling module for PROCESS - !! author: P J Knight, CCFE, Culham Science Centre - !! N/A - !! This module provides a centralised method for dealing with - !! errors generated by PROCESS. - !!

All possible informational/error messages are initialised - !! via a call to - !! initialise_error_list. Thereafter, any routine - !! that needs to flag a message should call - !! report_error with the relevant error identifier as - !! the argument. Up to eight integer and eight floating-point diagnostic - !! values may be saved by the user in arrays idiags and - !! fdiags, respectively, for debugging purposes. - !!

The list of messages reported during the course of a run - !! may be displayed by calling routine - !! show_errors. - !!

The error_status variable returns the highest severity - !! level that has been encountered; if a severe error is flagged - !! (level 3) the program is terminated immediately. - !! None - ! - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -#ifndef dp - use, intrinsic :: iso_fortran_env, only: dp=>real64 -#endif - implicit none - - private - public :: errors_on, error_status, idiags, fdiags - public :: initialise_error_list, report_error, show_errors, & - init_error_handling - - ! Switch to turn error handling on - ! Error reporting is turned off, until either a severe error is found, or - ! during an output step. Warnings during intermediate iteration steps - ! may be premature and might clear themselves at the final converged point. - - logical :: errors_on - - ! Levels of severity - - integer, parameter :: ERROR_OKAY = 0 - integer, parameter :: ERROR_INFO = 1 - integer, parameter :: ERROR_WARN = 2 - integer, parameter :: ERROR_SEVERE = 3 - - integer :: error_id - !! error_id : identifier for final message encountered - - ! Overall status - - integer :: error_status - !! error_status : overall status flag for a run; on exit:

    - !!
  • 0 all okay - !!
  • 1 informational messages have been encountered - !!
  • 2 warning (non-fatal) messages have been encountered - !!
  • 3 severe (fatal) errors have occurred
- - integer, parameter :: INT_DEFAULT = -999999 - real(dp), parameter :: FLT_DEFAULT = real(INT_DEFAULT, kind(1.0D0)) - - ! Arrays for diagnostic output - - integer, dimension(8) :: idiags - real(dp), dimension(8) :: fdiags - - ! Individual error item - ! int and float arrays may be useful to provide diagnostic information - - type :: error - integer :: level ! severity level - character(len=200) :: message ! information string - integer, dimension(8) :: idiags = INT_DEFAULT - real(dp), dimension(8) :: fdiags = FLT_DEFAULT - end type error - - ! Individual element in an error list - - type :: error_list_item - integer :: id ! identifier - type (error) :: data ! error details - type (error_list_item), pointer :: ptr ! linked list pointer - end type error_list_item - - ! Pointers to head and tail of the error list - - type (error_list_item), pointer :: error_head - type (error_list_item), pointer :: error_tail - - ! List of messages - - type(error), allocatable, dimension(:) :: error_type - -contains - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - subroutine init_error_handling - !! Initialise the error_handling module variables - implicit none - - errors_on = .false. - error_id = 0 - error_status = ERROR_OKAY - idiags = INT_DEFAULT - fdiags = FLT_DEFAULT - error_head => null() - error_tail => null() - end subroutine init_error_handling - - subroutine initialise_error_list - - !! Initialises the informational/error message list - !! author: P J Knight, CCFE, Culham Science Centre - !! None - !! This routine sets all the possible informational/error messages - !! that may be used during the course of a run. Thus, it needs - !! to be called during the initialisation phase. - !!

The error messages are read in from a JSON-format file. - !! None - ! - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - use fson_library, only: fson_parse, fson_value, fson_get, fson_destroy - implicit none - - ! Arguments - - ! Local variables - - integer :: n_errortypes - character(len=180) :: filename - type(fson_value), pointer :: errorfile - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - ! Parse the json file - character(len=200) :: process_dir - CALL get_environment_variable("PYTHON_PROCESS_ROOT", process_dir) - if (process_dir == "") then - filename = INSTALLDIR//'/process//utilities/errorlist.json' - else - filename = trim(process_dir)//'/utilities/errorlist.json' - end if - errorfile => fson_parse(trim(filename)) - - ! Allocate memory for error_type array contents - - call fson_get(errorfile, "n_errortypes", n_errortypes) - - ! Guard against re-allocation - if (allocated(error_type)) deallocate(error_type) - allocate(error_type(n_errortypes)) - - error_type(:)%level = ERROR_OKAY - error_type(:)%message = ' ' - - ! Extract information arrays from the file - - call fson_get(errorfile, "errors", "level", error_type%level) - call fson_get(errorfile, "errors", "message", error_type%message) - - ! Clean up - - call fson_destroy(errorfile) - - end subroutine initialise_error_list - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - subroutine report_error(error_id) - - !! Adds the latest error message to the list already specified - !! author: P J Knight, CCFE, Culham Science Centre - !! error_id : input integer : identifier (error_type element number) - !! for the relevant error - !! This routine should be called if a informational, warning - !! or error message needs to be flagged. - !! It uses a linked list (see references) to provide - !! an audit trail for any such messages during the program - !! execution. - !!

Up to eight integer and eight floating-point diagnostic - !! values may be saved by the user in arrays idiags and - !! fdiags, respectively, for debugging; these arrays must - !! be assigned with up to eight values each prior to calling this routine. - !!

The error_status variable returns the highest severity - !! level that has been encountered; if a severe error is flagged - !! (level 3) the program is terminated immediately. - !! Introduction to Fortran 90/95, Stephen J, Chapman, pp.467-472, - !! McGraw-Hill, ISBN 0-07-115896-0 - ! - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - implicit none - - ! Arguments - - integer, intent(in) :: error_id - - ! Local variables - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - ! Turn on error handling if a severe error has been encountered - - if (error_type(error_id)%level == ERROR_SEVERE) errors_on = .true. - - ! Error handling is only turned on during an output step, not during - ! intermediate iteration steps - - if (.not.errors_on) then - idiags = INT_DEFAULT - fdiags = FLT_DEFAULT - return - end if - - if (.not.associated(error_head)) then - allocate(error_head) - error_tail => error_head - else - -! SJP Issue #867 -! Remove consecutive identical error messages - - if (error_tail%id == error_id) return - - allocate(error_tail%ptr) - error_tail => error_tail%ptr - end if - - error_tail%id = error_id - error_tail%data%level = error_type(error_id)%level - error_tail%data%message = error_type(error_id)%message - error_tail%data%idiags = idiags ; idiags = INT_DEFAULT - error_tail%data%fdiags = fdiags ; fdiags = FLT_DEFAULT - - nullify (error_tail%ptr) - - ! Update the overall error status (highest severity level encountered) - ! and stop the program if a severe error has occurred - - error_status = max(error_status, error_type(error_id)%level) - - if (error_status == ERROR_SEVERE) then - call show_errors - write(*,*) 'PROCESS stopping.' - stop 1 - end if - - end subroutine report_error - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - subroutine show_errors - - !! Reports all informational/error messages encountered - !! author: P J Knight, CCFE, Culham Science Centre - !! None - !! This routine provides a summary audit trail of all the errors - !! encountered during the program's execution. - !! Introduction to Fortran 90/95, Stephen J, Chapman, pp.467-472, - !! McGraw-Hill, ISBN 0-07-115896-0 - ! - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - use constants, only: iotty, nout - use process_output_fortran, only: oblnkl, oheadr, ocmmnt, ovarin, ostars - implicit none - - ! Arguments - - ! Local variables - - type (error_list_item), pointer :: ptr - integer :: i - character(len=50) :: status_message - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - call oheadr(iotty,'Errors and Warnings') - call oheadr(nout,'Errors and Warnings') - call ocmmnt(nout,'(See top of file for solver errors and warnings.)') - - select case (error_status) - case (0) - status_message = 'No messages' - case (1) - status_message = 'Information messages only' - case (2) - status_message = 'Warning messages' - case (3) - status_message = 'Errors' - case default - status_message = 'Incorrect value of error_status' - end select - - call ocmmnt(nout,'PROCESS status flag: '//status_message) - write(*,*) 'PROCESS status flag: '//status_message - call oblnkl(iotty) - call ovarin(nout,'PROCESS error status flag','(error_status)',error_status) - - ptr => error_head - - if (.not.associated(ptr)) then - call ovarin(nout,'Final error/warning identifier','(error_id)',error_id) - return - end if - - write(*,*) 'ID LEVEL MESSAGE' - - output: do - if (.not.associated(ptr)) exit output - - error_id = ptr%id - write(nout,'(i3,t7,i3,t13,a80)') ptr%id,ptr%data%level,ptr%data%message - write(*, '(i3,t7,i3,t13,a80)') ptr%id,ptr%data%level,ptr%data%message - - if (any(ptr%data%idiags /= INT_DEFAULT)) then - write(*,*) 'Integer diagnostic values for this error:' - do i = 1,8 - if (ptr%data%idiags(i) /= INT_DEFAULT) then - write(*,'(i4,a,i14)') i,') ',ptr%data%idiags(i) - write(nout,'(i4,a,i14)') i,') ',ptr%data%idiags(i) - endif - end do - end if - if (any(ptr%data%fdiags /= FLT_DEFAULT)) then - write(*,*) 'Floating point diagnostic values for this error:' - do i = 1,8 - if (ptr%data%fdiags(i) /= FLT_DEFAULT) then - write(*,'(i4,a,1pe14.5)') i,') ',ptr%data%fdiags(i) - write(nout,'(i4,a,1pe14.5)') i,') ',ptr%data%fdiags(i) - endif - end do - end if - write(*,*) ' ' - - ptr => ptr%ptr - end do output - call ostars(iotty, 110) - call oblnkl(iotty) - - call ovarin(nout,'Final error identifier','(error_id)',error_id) - - end subroutine show_errors - -end module error_handling diff --git a/source/fortran/fson_library.f90 b/source/fortran/fson_library.f90 deleted file mode 100644 index f1010b224a..0000000000 --- a/source/fortran/fson_library.f90 +++ /dev/null @@ -1,1794 +0,0 @@ -! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -!+PJK Need to convert to autodoc style + usual PROCESS standard layout - -!----------------------------------------------------------------------------------------- -! -! FSON library -! -! Extracted from https://github.com/josephalevin/fson on 9th July 2014 -! -! with selected updates taken from the version forked as -! -! https://github.com/jmozmoz/fson/commit/b210a9011bb804957546e2a4b6eade578e7035ef -! -! plus some improvements to help with array handling and double precision, by -! P J Knight, 17th July 2014 -! -! Comprises the following original FSON files: -! string.f95, value_m.f95, fson_path_m.f95, fson.f95 -! -!----------------------------------------------------------------------------------------- -! -! Copyright (c) 2012 Joseph A. Levin -! -! Permission is hereby granted, free of charge, to any person obtaining a copy of this -! software and associated documentation files (the "Software"), to deal in the Software -! without restriction, including without limitation the rights to use, copy, modify, merge, -! publish, distribute, sublicense, and/or sell copies of the Software, and to permit -! persons to whom the Software is furnished to do so, subject to the following conditions: -! -! The above copyright notice and this permission notice shall be included in all copies or -! substantial portions of the Software. -! -! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -! INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -! PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -! LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -! OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -! DEALINGS IN THE SOFTWARE. -! -!----------------------------------------------------------------------------------------- - -module fson_string_m - - private - - public :: fson_string, fson_string_create, fson_string_destroy, fson_string_length, & - fson_string_append, fson_string_clear - public :: fson_string_equals, fson_string_copy - - integer, parameter :: BLOCK_SIZE = 32 - - type fson_string - character (len = BLOCK_SIZE) :: chars - integer :: index = 0 - type(fson_string), pointer :: next => null() - end type fson_string - - interface fson_string_append - module procedure append_chars, append_string - end interface - - interface fson_string_copy - module procedure copy_chars - end interface - - interface fson_string_equals - module procedure equals_string - end interface - - interface fson_string_length - module procedure string_length - end interface - -contains - - ! - ! FSON STRING CREATE - ! - function fson_string_create(chars) result(new) - character(len=*), optional :: chars - type(fson_string), pointer :: new - - allocate(new) - - ! append chars if available - if (present(chars)) then - call append_chars(new, chars) - end if - - end function fson_string_create - - ! - ! FSON STRING DESTROY - ! - recursive subroutine fson_string_destroy(this) - type(fson_string), pointer :: this - - if (associated(this % next)) then - call fson_string_destroy(this % next) - end if - - nullify (this % next) - nullify (this) - end subroutine fson_string_destroy - - ! - ! ALLOCATE BLOCK - ! - subroutine allocate_block(this) - type(fson_string), pointer :: this - type(fson_string), pointer :: new - - if (.not.associated(this % next)) then - allocate(new) - this % next => new - end if - - end subroutine allocate_block - - ! - ! APPEND_STRING - ! - subroutine append_string(str1, str2) - type(fson_string), pointer :: str1, str2 - integer length, i - - length = string_length(str2) - - do i = 1, length - call append_char(str1, get_char_at(str2, i)) - end do - - end subroutine append_string - - ! - ! APPEND_CHARS - ! - subroutine append_chars(str, c) - type(fson_string), pointer :: str - character (len = *), intent(in) :: c - integer length, i - - length = len(c) - - do i = 1, length - call append_char(str, c(i:i)) - end do - - end subroutine append_chars - - ! - ! APPEND_CHAR - ! - recursive subroutine append_char(str, c) - type(fson_string), pointer :: str - character, intent(in) :: c - - if (str % index >= BLOCK_SIZE) then - !set down the chain - call allocate_block(str) - call append_char(str % next, c) - - else - ! set local - str % index = str % index + 1 - str % chars(str % index:str % index) = c - end if - - end subroutine append_char - - ! - ! COPY CHARS - ! - subroutine copy_chars(this, to) - type(fson_string), pointer :: this - character(len = *), intent(inout) :: to - integer :: length - - length = min(string_length(this), len(to)) - - do i = 1, length - to(i:i) = get_char_at(this, i) - end do - - ! pad with nothing - do i = length + 1, len(to) - to(i:i) = "" - end do - - end subroutine copy_chars - - ! - ! CLEAR - ! - recursive subroutine string_clear(this) - type(fson_string), pointer :: this - - if (associated(this % next)) then - call string_clear(this % next) - deallocate(this % next) - nullify (this % next) - end if - - this % index = 0 - - end subroutine string_clear - - ! - ! SIZE - ! - recursive integer function string_length(str) result(count) - type(fson_string), pointer :: str - - count = str % index - - if (str % index == BLOCK_SIZE .AND. associated(str % next)) then - count = count + string_length(str % next) - end if - - end function string_length - - ! - ! GET CHAR AT - ! - recursive character function get_char_at(this, i) result(c) - type(fson_string), pointer :: this - integer, intent(in) :: i - - if (i <= this % index) then - c = this % chars(i:i) - else - c = get_char_at(this % next, i - this % index) - end if - - end function get_char_at - - ! - ! EQUALS STRING - ! - logical function equals_string(this, other) result(equals) - type(fson_string), pointer :: this, other - integer :: i - equals = .false. - - if (fson_string_length(this) /= fson_string_length(other)) then - equals = .false. - return - else if (fson_string_length(this) == 0) then - equals = .true. - return - end if - - do i=1, fson_string_length(this) - if (get_char_at(this, i) /= get_char_at(other, i)) then - equals = .false. - return - end if - end do - - equals = .true. - - end function equals_string - -end module fson_string_m - -! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -module fson_value_m - use fson_string_m, only: fson_string - implicit none - - private - - public :: fson_value, fson_value_create, fson_value_destroy, fson_value_add, fson_value_get, & - fson_value_count, fson_value_print - - ! constants for the value types - integer, public, parameter :: TYPE_UNKNOWN = -1 - integer, public, parameter :: TYPE_NULL = 0 - integer, public, parameter :: TYPE_OBJECT = 1 - integer, public, parameter :: TYPE_ARRAY = 2 - integer, public, parameter :: TYPE_STRING = 3 - integer, public, parameter :: TYPE_INTEGER = 4 - integer, public, parameter :: TYPE_REAL = 5 - integer, public, parameter :: TYPE_LOGICAL = 6 - - ! - ! FSON VALUE - ! - type fson_value - type(fson_string), pointer :: name => null() - integer :: value_type = TYPE_UNKNOWN - logical :: value_logical - integer :: value_integer - real :: value_real - !+PJK - real(kind(1.0D0)) :: value_double - !-PJK - type(fson_string), pointer :: value_string => null() - type(fson_value), pointer :: next => null() - type(fson_value), pointer :: parent => null() - type(fson_value), pointer :: children => null() - end type fson_value - - ! - ! FSON VALUE GET - ! - ! Use either a 1 based index or member name to get the value. - interface fson_value_get - module procedure get_by_index - module procedure get_by_name_chars - module procedure get_by_name_string - end interface - -contains - - ! - ! FSON VALUE CREATE - ! - function fson_value_create() result(new) - type(fson_value), pointer :: new - - allocate(new) - - end function fson_value_create - - ! - ! FSON VALUE DESTROY - ! - recursive subroutine fson_value_destroy(this) - use fson_string_m, only: fson_string_destroy - implicit none - - type(fson_value), pointer :: this - - if (associated(this % children)) then - call fson_value_destroy(this % children) - nullify(this % children) - end if - - if (associated(this % next)) then - call fson_value_destroy(this % next) - nullify (this % next) - end if - - if (associated(this % name)) then - call fson_string_destroy(this % name) - nullify (this % name) - end if - - if (associated(this % value_string)) then - call fson_string_destroy(this % value_string) - nullify (this % value_string) - end if - - nullify(this) - - end subroutine fson_value_destroy - - ! - ! FSON VALUE ADD - ! - ! Adds the member to the linked list - subroutine fson_value_add(this, member) - type(fson_value), pointer :: this, member, p - - ! associate the parent - member % parent => this - - ! add to linked list - if (associated(this % children)) then - ! get to the tail of the linked list - p => this % children - do while (associated(p % next)) - p => p % next - end do - - p % next => member - else - this % children => member - end if - - end subroutine fson_value_add - - ! - ! FSON_VALUE_COUNT - ! - integer function fson_value_count(this) result(count) - type(fson_value), pointer :: this, p - - count = 0 - - p => this % children - - do while (associated(p)) - count = count + 1 - p => p % next - end do - - end function fson_value_count - - ! - ! GET BY INDEX - ! - function get_by_index(this, index) result(p) - type(fson_value), pointer :: this, p - integer, intent(in) :: index - integer :: i - - p => this % children - - do i = 1, index - 1 - p => p % next - end do - - end function get_by_index - - ! - ! GET BY NAME CHARS - ! - function get_by_name_chars(this, name) result(p) - use fson_string_m, only: fson_string, fson_string_create - implicit none - - type(fson_value), pointer :: this, p - character(len=*), intent(in) :: name - - type(fson_string), pointer :: string - - ! convert the char array into a string - string => fson_string_create(name) - - p => get_by_name_string(this, string) - - end function get_by_name_chars - - ! - ! GET BY NAME STRING - ! - function get_by_name_string(this, name) result(p) - use fson_string_m, only: fson_string, fson_string_equals - implicit none - - type(fson_value), pointer :: this, p - type(fson_string), pointer :: name - integer :: i - - if (this % value_type /= TYPE_OBJECT) then - nullify(p) - return - end if - - do i=1, fson_value_count(this) - p => fson_value_get(this, i) - if (fson_string_equals(p%name, name)) then - return - end if - end do - - ! didn't find anything - nullify(p) - - end function get_by_name_string - - ! - ! FSON VALUE PRINT - ! - recursive subroutine fson_value_print(this, indent) - use fson_string_m, only: fson_string_copy - implicit none - - type(fson_value), pointer :: this, element - integer, optional, intent(in) :: indent - character(len=1024) :: tmp_chars - integer :: tab, i, count, spaces - - !+PJK - if (.not.associated(this)) return - !-PJK - - if (present(indent)) then - tab = indent - else - tab = 0 - end if - - spaces = tab * 2 - - select case (this % value_type) - case(TYPE_OBJECT) - print *, repeat(" ", spaces), "{" - count = fson_value_count(this) - do i = 1, count - ! get the element - element => fson_value_get(this, i) - ! get the name - call fson_string_copy(element % name, tmp_chars) - ! print the name - print *, repeat(" ", spaces), '"', trim(tmp_chars), '":' - ! recursive print of the element - call fson_value_print(element, tab + 1) - ! print the separator if required - if (i < count) then - print *, repeat(" ", spaces), "," - end if - end do - - print *, repeat(" ", spaces), "}" - case (TYPE_ARRAY) - print *, repeat(" ", spaces), "[" - count = fson_value_count(this) - do i = 1, count - ! get the element - element => fson_value_get(this, i) - ! recursive print of the element - call fson_value_print(element, tab + 1) - ! print the separator if required - if (i < count) then - print *, "," - end if - end do - print *, repeat(" ", spaces), "]" - case (TYPE_NULL) - print *, repeat(" ", spaces), "null" - case (TYPE_STRING) - call fson_string_copy(this % value_string, tmp_chars) - print *, repeat(" ", spaces), '"', trim(tmp_chars), '"' - case (TYPE_LOGICAL) - if (this % value_logical) then - print *, repeat(" ", spaces), "true" - else - print *, repeat(" ", spaces), "false" - end if - case (TYPE_INTEGER) - print *, repeat(" ", spaces), this % value_integer - case (TYPE_REAL) - print *, repeat(" ", spaces), this % value_real ! N.B. doubles will be shown as single precision - end select - end subroutine fson_value_print - -end module fson_value_m - -! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -! Modifications by P J Knight to handle arrays more conveniently than by providing -! an array_callback argument to get_array, and to handle double precision values better - -module fson_path_m - - private - - public :: fson_path_get, array_callback - - interface fson_path_get - module procedure get_by_path - module procedure get_integer - module procedure get_real - module procedure get_double - module procedure get_logical - module procedure get_chars - module procedure get_array - !+PJK - module procedure get_int_array - module procedure get_real_array - module procedure get_double_array - module procedure get_string_array - module procedure get_int_array_in_struct - module procedure get_real_array_in_struct - module procedure get_double_array_in_struct - module procedure get_string_array_in_struct - !-PJK - end interface - -contains - - ! - ! GET BY PATH - ! - ! $ = root - ! @ = this - ! . = child object member - ! [] = child array element - ! - recursive subroutine get_by_path(this, path, p) - use fson_value_m, only: fson_value, fson_value_get - implicit none - - type(fson_value), pointer :: this, p - character(len=*), intent(inout) :: path - integer :: i, length, child_i - character :: c - logical :: array - - ! default to assuming relative to this - p => this - - child_i = 1 - - array = .false. - - length = len_trim(path) - - do i=1, length - c = path(i:i) - select case (c) - case ("$") - ! root - do while (associated (p % parent)) - p => p % parent - end do - child_i = i + 1 - case ("@") - ! this - p => this - child_i = i + 1 - case (".", "[") - ! get child member from p - if (child_i < i) then - p => fson_value_get(p, path(child_i:i-1)) - else - child_i = i + 1 - cycle - end if - - if (.not.associated(p)) then - return - end if - - child_i = i + 1 - - ! check if this is an array - ! if so set the array flag - if (c == "[") then - ! start looking for the array element index - array = .true. - end if - case ("]") - if (.not.array) then - print *, "ERROR: Unexpected ], not missing preceding [" - call exit(1) - end if - array = .false. - child_i = parse_integer(path(child_i:i-1)) - p => fson_value_get(p, child_i) - - child_i = i + 1 - end select - end do - - ! grab the last child if present in the path - if (child_i <= length) then - p => fson_value_get(p, path(child_i:i-1)) - if (.not.associated(p)) then - return - else - end if - end if - - end subroutine get_by_path - - ! - ! PARSE INTEGER - ! - integer function parse_integer(chars) result(integral) - character(len=*) :: chars - character :: c - integer :: tmp, i - - integral = 0 - do i=1, len_trim(chars) - c = chars(i:i) - select case(c) - case ("0":"9") - ! digit - read (c, '(i1)') tmp - ! shift - if (i > 1) then - integral = integral * 10 - end if - ! add - integral = integral + tmp - - case default - return - end select - end do - - end function parse_integer - - ! - ! GET INTEGER - ! - subroutine get_integer(this, path, value) - use fson_value_m, only: type_integer, type_real, type_logical, fson_value - implicit none - - type(fson_value), pointer :: this, p - character(len=*), optional :: path - integer :: value - - nullify(p) - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_INTEGER) then - value = p % value_integer - else if (p % value_type == TYPE_REAL) then - value = p % value_real - else if (p % value_type == TYPE_LOGICAL) then - if (p % value_logical) then - value = 1 - else - value = 0 - end if - else - print *, "Unable to resolve value to integer: ", path - call exit(1) - end if - - end subroutine get_integer - - ! - ! GET REAL - ! - subroutine get_real(this, path, value) - use fson_value_m, only: type_integer, type_real, type_logical, fson_value - implicit none - - type(fson_value), pointer :: this, p - character(len=*), optional :: path - real :: value - - nullify(p) - - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_INTEGER) then - value = p % value_integer - else if (p % value_type == TYPE_REAL) then - value = p % value_real - else if (p % value_type == TYPE_LOGICAL) then - if (p % value_logical) then - value = 1 - else - value = 0 - end if - else - print *, "Unable to resolve value to real: ", path - call exit(1) - end if - - end subroutine get_real - - ! - ! GET DOUBLE - ! - subroutine get_double(this, path, value) - use fson_value_m, only: type_integer, type_real, type_logical, fson_value - implicit none - - type(fson_value), pointer :: this, p - character(len=*), optional :: path - real(kind(1.0D0)) :: value - - nullify(p) - - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_INTEGER) then - value = p % value_integer - else if (p % value_type == TYPE_REAL) then - value = p % value_double ! PJK from value_real - else if (p % value_type == TYPE_LOGICAL) then - if (p % value_logical) then - value = 1 - else - value = 0 - end if - else - print *, "Unable to resolve value to double: ", path - call exit(1) - end if - - end subroutine get_double - - ! - ! GET LOGICAL - ! - subroutine get_logical(this, path, value) - use fson_value_m, only: type_integer, type_logical, fson_value - implicit none - - type(fson_value), pointer :: this, p - character(len=*), optional :: path - logical :: value - - nullify(p) - - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_INTEGER) then - value = (p % value_integer > 0) - else if (p % value_type == TYPE_LOGICAL) then - value = p % value_logical - else - print *, "Unable to resolve value to logical: ", path - call exit(1) - end if - - end subroutine get_logical - - ! - ! GET CHARS - ! - subroutine get_chars(this, path, value) - use fson_value_m, only: type_string, fson_value - use fson_string_m, only: fson_string_copy - implicit none - - type(fson_value), pointer :: this, p - character(len=*), optional :: path - character(len=*) :: value - - nullify(p) - - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_STRING) then - call fson_string_copy(p % value_string, value) - else - print *, "Unable to resolve value to characters: ", path - call exit(1) - end if - - end subroutine get_chars - - ! - ! GET ARRAY (original version using array_callback) - ! - subroutine get_array(this, path, array_callback) - use fson_value_m, only: type_array, fson_value_get, fson_value_count, & - fson_value - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*), optional :: path - integer :: index, count - - ! ELEMENT CALLBACK (PJK: Added example comments) - interface - subroutine array_callback(element, index, count) - use fson_value_m, only: fson_value - implicit none - - ! In the actual routine add a second 'use' line as follows: - !use shared_data ! contains declarations for the array(s) to be populated - - type(fson_value), pointer :: element - integer :: index, count - - ! Example usage in the actual routine: - ! call fson_get(element, "element_name", array_name(index)) - - end subroutine array_callback - end interface - - nullify(p) - - ! resolve the path to the value - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - call array_callback(element, index, count) - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_array - - !+PJK - ! - ! GET INT ARRAY - ! - subroutine get_int_array(this, path, array) - use fson_value_m, only: type_array, fson_value_get, fson_value_count, & - fson_value - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*), optional :: path - integer :: index, count - integer, dimension(:) :: array - - nullify(p) - - ! resolve the path to the value - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - array(index) = element%value_integer - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_int_array - - ! - ! GET REAL ARRAY - ! - subroutine get_real_array(this, path, array) - use fson_value_m, only: type_array, fson_value_get, fson_value_count, & - fson_value - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*), optional :: path - integer :: index, count - real, dimension(:) :: array - - nullify(p) - - ! resolve the path to the value - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - array(index) = element%value_real - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_real_array - - ! - ! GET DOUBLE ARRAY - ! - subroutine get_double_array(this, path, array) - use fson_value_m, only: type_array, fson_value_get, fson_value_count, & - fson_value - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*), optional :: path - integer :: index, count - real(kind(1.0D0)), dimension(:) :: array - - nullify(p) - - ! resolve the path to the value - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - array(index) = element%value_double - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_double_array - - ! - ! GET STRING ARRAY - ! - subroutine get_string_array(this, path, array) - use fson_value_m, only: fson_value_count, fson_value_get, fson_value, & - TYPE_ARRAY - use fson_string_m, only: fson_string_copy - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*), optional :: path - integer :: index, count - character(len=*), dimension(:) :: array - - nullify(p) - - ! resolve the path to the value - if (present(path)) then - call get_by_path(this=this, path=path, p=p) - else - p => this - end if - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - call fson_string_copy(element%value_string, array(index)) - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_string_array - - ! - ! GET INT ARRAY IN STRUCTURE - ! - subroutine get_int_array_in_struct(this, path, subpath, array) - use fson_value_m, only: fson_value_count, fson_value_get, fson_value, & - TYPE_ARRAY - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*) :: path, subpath - integer, dimension(:), intent(out) :: array - integer :: index, count - - nullify(p) - - ! resolve the path to the value - call get_by_path(this=this, path=path, p=p) - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - call get_integer(element, subpath, array(index)) - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_int_array_in_struct - - ! - ! GET REAL ARRAY IN STRUCTURE - ! - subroutine get_real_array_in_struct(this, path, subpath, array) - use fson_value_m, only: fson_value_count, fson_value_get, fson_value, & - TYPE_ARRAY - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*) :: path, subpath - real, dimension(:), intent(out) :: array - integer :: index, count - - nullify(p) - - ! resolve the path to the value - call get_by_path(this=this, path=path, p=p) - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - call get_real(element, subpath, array(index)) - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_real_array_in_struct - - ! - ! GET DOUBLE ARRAY IN STRUCTURE - ! - subroutine get_double_array_in_struct(this, path, subpath, array) - use fson_value_m, only: fson_value_count, fson_value_get, fson_value, & - TYPE_ARRAY - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*) :: path, subpath - real(kind(1.0D0)), dimension(:), intent(out) :: array - integer :: index, count - - nullify(p) - - ! resolve the path to the value - call get_by_path(this=this, path=path, p=p) - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - call get_double(element, subpath, array(index)) - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_double_array_in_struct - - ! - ! GET STRING ARRAY IN STRUCTURE - ! - subroutine get_string_array_in_struct(this, path, subpath, array) - use fson_value_m, only: fson_value_count, fson_value_get, fson_value, & - TYPE_ARRAY - implicit none - - type(fson_value), pointer :: this, p, element - character(len=*) :: path, subpath - character(len=*), dimension(:), intent(out) :: array - integer :: index, count - - nullify(p) - - ! resolve the path to the value - call get_by_path(this=this, path=path, p=p) - - if (.not.associated(p)) then - print *, "Unable to resolve path: ", path - call exit(1) - end if - - if (p % value_type == TYPE_ARRAY) then - count = fson_value_count(p) - do index = 1, count - element => fson_value_get(p, index) - call get_chars(element, subpath, array(index)) - end do - else - print *, "Resolved value is not an array. ", path - call exit(1) - end if - - end subroutine get_string_array_in_struct - !-PJK - -end module fson_path_m - -! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -module fson_library - - !! JSON file reading module - !! author: P J Knight, CCFE, Culham Science Centre - !! N/A - !! This module uses a local copy of the freely-available FSON library, - !! written by Joseph A. Levin and distributed via github, - !! to enable PROCESS to read in information from JSON-formatted files. - !! None - ! - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - use fson_value_m, only: fson_print => fson_value_print, & - fson_destroy => fson_value_destroy, fson_value - use fson_path_m, only: fson_get => fson_path_get - - implicit none - - private - - public :: fson_parse, fson_value, fson_get, fson_print, fson_destroy, & - init_fson_library - - ! FILE IOSTAT CODES - integer, parameter :: end_of_file = -1 - integer, parameter :: end_of_record = -2 - - ! PARSING STATES - integer, parameter :: STATE_LOOKING_FOR_VALUE = 1 - integer, parameter :: STATE_IN_OBJECT = 2 - integer, parameter :: STATE_IN_PAIR_NAME = 3 - integer, parameter :: STATE_IN_PAIR_VALUE = 4 - - ! POP/PUSH CHARACTER - integer :: pushed_index - character (len = 10) :: pushed_char - -contains - - subroutine init_fson_library - !! Initialise fson library module variables - implicit none - - pushed_index = 0 - end subroutine init_fson_library - - ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - ! - ! FSON PARSE - ! - function fson_parse(file, unit) result(p) - use fson_value_m, only: fson_value_create - implicit none - - type(fson_value), pointer :: p - integer, optional, intent(inout) :: unit - character(len = *), intent(in) :: file - logical :: unit_available - integer :: u - ! init the pointer to null - nullify(p) - - ! select the file unit to use - if (present(unit)) then - u = unit - else - ! find the first available unit - unit_available = .false. - u = 20 - - do while (.not.unit_available) - inquire(unit=u, exist=unit_available) - u = u + 1 - end do - end if - - ! open the file - open (unit=u, file=file, status="old", action="read", & - form="formatted", position="rewind") - - ! create the value and associate the pointer - p => fson_value_create() - - ! parse as a value - call parse_value(unit=u, value=p) - - ! close the file - if ( .not. present(unit)) then - close (u) - end if - - end function fson_parse - - ! - ! PARSE_VALUE - ! - recursive subroutine parse_value(unit, value) - use fson_value_m, only: TYPE_ARRAY, TYPE_LOGICAL, TYPE_NULL, TYPE_OBJECT, & - TYPE_STRING - implicit none - - integer, intent(inout) :: unit - type(fson_value), pointer :: value - logical :: eof - character :: c - - ! for some unknown reason the next pointer is getting messed with the pop - type(fson_value), pointer :: hack - - ! start the hack - hack => value % next - - ! pop the next non whitespace character off the file - c = pop_char(unit, eof=eof, skip_ws=.true.) - - ! finish the hack; set the next pointer to whatever it was before the pop - value % next => hack - - if (eof) then - return - else - select case (c) - case ("{") - ! start object - value % value_type = TYPE_OBJECT - call parse_object(unit, value) - case ("[") - ! start array - value % value_type = TYPE_ARRAY - call parse_array(unit, value) - case ("]") - ! end an empty array - nullify(value) - case ('"') - ! string - value % value_type = TYPE_STRING - value % value_string => parse_string(unit) - case ("t") - ! true - value % value_type = TYPE_LOGICAL - call parse_for_chars(unit, "rue") - value % value_logical = .true. - case ("f") - ! false - value % value_type = TYPE_LOGICAL - value % value_logical = .false. - call parse_for_chars(unit, "alse") - case ("n") - value % value_type = TYPE_NULL - call parse_for_chars(unit, "ull") - case("-", "0": "9") - call push_char(c) - call parse_number(unit, value) - case default - print *, "ERROR: Unexpected character while parsing value. '", c, "' ASCII=", iachar(c) - call exit (1) - end select - end if - - end subroutine parse_value - - ! - ! PARSE OBJECT - ! - recursive subroutine parse_object(unit, parent) - use fson_value_m, only: fson_value_create, fson_value_add - implicit none - - integer, intent(inout) :: unit - type(fson_value), pointer :: parent, pair - - logical :: eof - character :: c - - ! pair name - c = pop_char(unit, eof=eof, skip_ws=.true.) - if (eof) then - print *, "ERROR: Unexpected end of file while parsing start of object." - call exit (1) - else if ("}" == c) then - ! end of an empty object - return - else if ('"' == c) then - pair => fson_value_create() - pair % name => parse_string(unit) - else - print *, "ERROR: Expecting string: '", c, "'" - call exit (1) - end if - - ! pair value - c = pop_char(unit, eof=eof, skip_ws=.true.) - if (eof) then - print *, "ERROR: Unexpected end of file while parsing object member. 1" - call exit (1) - else if (":" == c) then - ! parse the value - call parse_value(unit, pair) - call fson_value_add(parent, pair) - else - print *, "ERROR: Expecting : and then a value. ", c - call exit (1) - end if - - ! another possible pair - c = pop_char(unit, eof=eof, skip_ws=.true.) - if (eof) then - return - else if ("," == c) then - ! read the next member - call parse_object(unit=unit, parent=parent) - else if ("}" == c) then - return - else - print *, "ERROR: Expecting end of object.", c - call exit (1) - end if - - end subroutine parse_object - - ! - ! PARSE ARRAY - ! - recursive subroutine parse_array(unit, array) - use fson_value_m, only: fson_value_create, fson_value_add - implicit none - - integer, intent(inout) :: unit - type(fson_value), pointer :: array, element - - logical :: eof - character :: c - - ! try to parse an element value - element => fson_value_create() - call parse_value(unit, element) - - ! parse value will disassociate an empty array value - if (associated(element)) then - call fson_value_add(array, element) - end if - - ! popped the next character - c = pop_char(unit, eof=eof, skip_ws=.true.) - - if (eof) then - return - else if ("," == c) then - ! parse the next element - call parse_array(unit, array) - else if ("]" == c) then - ! end of array - return - end if - - end subroutine parse_array - - ! - ! PARSE STRING - ! - function parse_string(unit) result(string) - use fson_string_m, only: fson_string, fson_string_create, fson_string_append - implicit none - - integer, intent(inout) :: unit - type(fson_string), pointer :: string - - logical :: eof - character :: c, last - - string => fson_string_create() - - do - c = pop_char(unit, eof=eof, skip_ws=.false.) - if (eof) then - print *, "Expecting end of string" - call exit(1)! - else if ('"' == c .and. last /= '\') then !' - exit - else - last = c - call fson_string_append(string, c) - end if - end do - end function parse_string - - ! - ! PARSE FOR CHARACTERS - ! - subroutine parse_for_chars(unit, chars) - integer, intent(in) :: unit - character(len = *), intent(in) :: chars - integer :: i, length - logical :: eof - character :: c - - length = len_trim(chars) - - do i = 1, length - c = pop_char(unit, eof=eof, skip_ws=.true.) - if (eof) then - print *, "ERROR: Unexpected end of file while parsing array." - call exit (1) - else if (c /= chars(i:i)) then - print *, "ERROR: Unexpected character.'", c,"'", chars(i:i) - call exit (1) - end if - end do - - end subroutine parse_for_chars - - ! - ! PARSE NUMBER - ! - subroutine parse_number(unit, value) - use fson_value_m, only: TYPE_INTEGER, TYPE_REAL - implicit none - - integer, intent(inout) :: unit - type(fson_value), pointer :: value - logical :: eof, negative, decimal, scientific - character :: c - integer :: integral, exp, digit_count - real(kind(1.0D0)) :: frac - - ! first character is either - or a digit - c = pop_char(unit, eof=eof, skip_ws=.true.) - if (eof) then - print *, "ERROR: Unexpected end of file while parsing number." - call exit (1) - else if ("-" == c) then - negative = .true. - else - negative = .false. - call push_char(c) - end if - - ! parse the integral - integral = parse_integer(unit) - - decimal = .false. - scientific = .false. - - do - ! first character is either - or a digit - c = pop_char(unit, eof=eof, skip_ws=.true.) - if (eof) then - print *, "ERROR: Unexpected end of file while parsing number." - call exit (1) - else - select case (c) - case (".") - ! this is already fractional number - if (decimal) then - ! already found a decimal place - print *, "ERROR: Unexpected second decimal place while parsing number." - call exit(1) - end if - decimal = .true. - frac = parse_integer(unit, digit_count) - frac = frac / (10.0D0 ** digit_count) - case ("e", "E") - ! this is already an exponent number - if (scientific) then - ! already found a e place - print *, "ERROR: Unexpected second exponent while parsing number." - call exit(1) - end if - scientific = .true. - ! this number has an exponent - exp = parse_integer(unit) - - case default - ! this is a integer - if (decimal) then - - ! add the integral - frac = frac + integral - - if (scientific) then - ! apply exponent - frac = frac * (10.0D0 ** exp) - end if - - ! apply negative - if (negative) then - frac = frac * (-1) - end if - - value % value_type = TYPE_REAL - value % value_real = frac - value % value_double = frac - else - if (scientific) then - ! apply exponent - integral = integral * (10.0D0 ** exp) - end if - - ! apply negative - if (negative) then - integral = integral * (-1) - end if - - value % value_type = TYPE_INTEGER - value % value_integer = integral - !+PJK - ! Following two lines are included in case the decimal point of - ! a floating point number has been (accidentally) left out - value % value_real = integral - value % value_double = integral - !-PJK - end if - call push_char(c) - exit - end select - end if - end do - - end subroutine parse_number - - ! - ! PARSE INTEGER - ! - integer(kind=8) function parse_integer(unit, digit_count) result(integral) - integer, intent(in) :: unit - integer, optional, intent(inout) :: digit_count - logical :: eof, found_sign, found_digit - character :: c - integer :: tmp, icount, isign - integer, parameter :: max_integer_length = 18 - - icount = 0 - integral = 0 - isign = 1 - found_sign = .false. - found_digit = .false. - do - c = pop_char(unit, eof=eof, skip_ws=.true.) - if (eof) then - print *, "ERROR: Unexpected end of file while parsing digit." - call exit (1) - else - select case(c) - case ("+") - if (found_sign.or.found_digit) then - print *, "ERROR: Miss formatted number." - call exit(1) - end if - found_sign = .true. - case ("-") - if (found_sign.or.found_digit) then - print *, "ERROR: Miss formatted number." - call exit(1) - end if - found_sign = .true. - isign = -1 - case ("0":"9") - found_sign = .true. - if (icount > max_integer_length) then - print *, "ERROR: Too many digits for an integer." - call exit(1) - end if - ! digit - read (c, '(i1)') tmp - ! shift - if (icount > 0) then - integral = integral * 10 - end if - ! add - integral = integral + tmp - - ! increase the icount - icount = icount + 1 - case default - if (present(digit_count)) then - digit_count = icount - end if - call push_char(c) - integral = isign * integral - return - end select - end if - end do - - end function parse_integer - - ! - ! POP CHAR - ! - recursive character function pop_char(unit, eof, skip_ws) result(popped) - integer, intent(in) :: unit - logical, intent(out) :: eof - logical, intent(in), optional :: skip_ws - - integer :: ios - character :: c - logical :: ignore - - !+PJK - ios = 0 - !-PJK - eof = .false. - if (.not.present(skip_ws)) then - ignore = .false. - else - ignore = skip_ws - end if - - do - if (pushed_index > 0) then - ! there is a character pushed back on, most likely from the number parsing - c = pushed_char(pushed_index:pushed_index) - pushed_index = pushed_index - 1 - else - read (unit=unit, fmt="(a)", advance="no", iostat=ios) c - end if - if (ios == end_of_record) then - cycle - else if (ios == end_of_file) then - eof = .true. - exit - else if (iachar(c) <= 31) then ! PJK from 32 - ! non printing ascii characters - cycle - else if (ignore .and. c == " ") then - cycle - else - popped = c - exit - end if - end do - - end function pop_char - - ! - ! PUSH CHAR - ! - subroutine push_char(c) - character, intent(inout) :: c - pushed_index = pushed_index + 1 - pushed_char(pushed_index:pushed_index) = c - - end subroutine push_char - -end module fson_library diff --git a/source/fortran/init_module.f90 b/source/fortran/init_module.f90 index 59b2d69edb..c177a9fe7b 100644 --- a/source/fortran/init_module.f90 +++ b/source/fortran/init_module.f90 @@ -10,18 +10,6 @@ module init_module contains - subroutine init_fortran_modules - !! Temporary routine to call initialisation routines for Fortran modules - !! that are not wrapped by f2py and thus cannot be called from Python. - - use fson_library, only: init_fson_library - - implicit none - - call init_fson_library - - end subroutine init_fortran_modules - subroutine open_files use global_variables, only: verbose, fileprefix, output_prefix use constants, only: nout, mfile diff --git a/tests/conftest.py b/tests/conftest.py index 361df550f3..1a5fe21e91 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ from _pytest.fixtures import SubRequest from system_check import system_compatible -from process.fortran import error_handling as eh +from process.warning_handler import WarningManager def pytest_addoption(parser): @@ -128,8 +128,7 @@ def initialise_error_module(): Initialise the error module initially otherwise segmentation faults can occur when tested subroutines raise errors. """ - eh.init_error_handling() - eh.initialise_error_list() + WarningManager.reinitialise() @pytest.fixture @@ -143,7 +142,7 @@ def reinitialise_error_module(): # TODO Perhaps this should be autoused by all tests? Specify use explicitly # for now for known error-raisers yield - eh.init_error_handling() + WarningManager.reinitialise() @pytest.fixture(autouse=True) diff --git a/tests/integration/test_pfcoil_int.py b/tests/integration/test_pfcoil_int.py index fe023c6e97..4ac16e0634 100644 --- a/tests/integration/test_pfcoil_int.py +++ b/tests/integration/test_pfcoil_int.py @@ -16,7 +16,6 @@ from process.cs_fatigue import CsFatigue from process.fortran import build_variables as bv from process.fortran import constants -from process.fortran import error_handling as eh from process.fortran import fwbs_variables as fwbsv from process.fortran import pfcoil_module as pf from process.fortran import pfcoil_variables as pfv @@ -54,7 +53,6 @@ def test_pfcoil(monkeypatch, pfcoil): monkeypatch.setattr(bv, "dr_tf_inboard", 1.4) monkeypatch.setattr(bv, "r_tf_outboard_mid", 1.66e1) monkeypatch.setattr(bv, "dr_bore", 2.15) - monkeypatch.setattr(eh, "idiags", np.full(8, -999999)) monkeypatch.setattr(fwbsv, "denstl", 7.8e3) monkeypatch.setattr(pfv, "rpf1", 0.0) monkeypatch.setattr(pfv, "m_pf_coil_structure_total", 0.0) @@ -190,7 +188,6 @@ def test_ohcalc(monkeypatch, reinitialise_error_module, pfcoil): monkeypatch.setattr(bv, "hmax", 8.864) monkeypatch.setattr(bv, "dr_cs", 6.510e-1) monkeypatch.setattr(fwbsv, "denstl", 7.8e3) - monkeypatch.setattr(eh, "idiags", np.full(8, 0)) monkeypatch.setattr(pfv, "n_cs_pf_coils", 5) monkeypatch.setattr(pfv, "b_cs_peak_flat_top_end", 1.4e1) monkeypatch.setattr(pfv, "i_cs_stress", 0) @@ -262,7 +259,6 @@ def test_ohcalc(monkeypatch, reinitialise_error_module, pfcoil): monkeypatch.setattr(tfv, "poisson_steel", 3.0e-1) # Mocks for superconpf() - monkeypatch.setattr(eh, "fdiags", np.full(8, -9.99999e5)) monkeypatch.setattr(tfv, "tmargmin_cs", 1.5) monkeypatch.setattr(tfv, "temp_margin", 0.0) monkeypatch.setattr(tfv, "b_crit_upper_nbti", 1.486e1) @@ -2197,20 +2193,6 @@ def test_peakb(monkeypatch: pytest.MonkeyPatch, pfcoil: PFCoil): monkeypatch.setattr(bv, "iohcl", 1) monkeypatch.setattr(bv, "hmax", 9.0730900215620327) monkeypatch.setattr(bv, "dr_cs", 0.55242000000000002) - monkeypatch.setattr( - eh, - "idiags", - np.array([ - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - ]), - ) monkeypatch.setattr( pfv, "r_pf_coil_inner", @@ -2564,8 +2546,6 @@ def test_superconpf(monkeypatch: pytest.MonkeyPatch, pfcoil: PFCoil): """ # TODO This test would benefit from parameterisation for different SC # materials (isumat) - monkeypatch.setattr(eh, "fdiags", np.zeros(8)) - monkeypatch.setattr(eh, "idiags", np.zeros(8)) monkeypatch.setattr(tfv, "tmargmin_cs", 0.0) monkeypatch.setattr(tfv, "temp_margin", 0.0) monkeypatch.setattr(tfv, "b_crit_upper_nbti", 0.0) @@ -2746,34 +2726,6 @@ def test_induct(pfcoil: PFCoil, monkeypatch: pytest.MonkeyPatch): """ monkeypatch.setattr(bv, "iohcl", 1) monkeypatch.setattr(bv, "dr_cs", 0.55242000000000002) - monkeypatch.setattr( - eh, - "fdiags", - np.array([ - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - ]), - ) - monkeypatch.setattr( - eh, - "idiags", - np.array([ - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - -999999, - ]), - ) monkeypatch.setattr(pfv, "n_cs_pf_coils", 7) monkeypatch.setattr( pfv, diff --git a/tests/integration/test_vmcon.py b/tests/integration/test_vmcon.py index c5613e008c..abe636239f 100644 --- a/tests/integration/test_vmcon.py +++ b/tests/integration/test_vmcon.py @@ -13,16 +13,12 @@ import pytest from process.evaluators import Evaluators -from process.fortran import error_handling from process.init import init_all_module_vars from process.solver import get_solver # Debug-level terminal output logging logger = logging.getLogger(__name__) -# Provide helpful errors in the event of a failed Vmcon test -error_handling.initialise_error_list() - @pytest.fixture(autouse=True) def reinit(): diff --git a/tests/unit/test_costs_1990.py b/tests/unit/test_costs_1990.py index 0ff4454224..41d2bb2337 100644 --- a/tests/unit/test_costs_1990.py +++ b/tests/unit/test_costs_1990.py @@ -26,7 +26,6 @@ times_variables, vacuum_variables, ) -from process.fortran import error_handling as eh from process.fortran import fwbs_variables as fv from process.fortran import heat_transport_variables as htv @@ -41,24 +40,6 @@ def costs(): return Costs() -@pytest.fixture -def initialise_error_module(monkeypatch): - """pytest fixture to initialise error module - - Any routine which can raise an error should initialise - the error module otherwise segmentation faults can occur. - - This fixture also resets the `fdiags` array to 0's. - - :param monkeypatch: Mock fixture - :type monkeypatch: object - """ - eh.init_error_handling() - eh.initialise_error_list() - # monkeypatch.setattr(eh, 'fdiags', np.zeros(8)) - # monkeypatch.setattr(eh, 'errors_on', False) - - def acc2261_param(**kwargs): """Make parameters for a single acc2261() test. @@ -5395,7 +5376,7 @@ class Acc2253Param(NamedTuple): ), ), ) -def test_acc2253_urt(acc2253param, monkeypatch, costs, initialise_error_module): +def test_acc2253_urt(acc2253param, monkeypatch, costs): """ Automatically generated Regression Unit Test for acc2253.