{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# Compute Engine Client - Solve App Example (EC4 Composite Column Design)",
   "id": "7d3419dc6f11953b"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## 1. Load client and interface classes, pandas and matplotlib for plotting, and dotenv and os to load environment variables",
   "id": "6807d542408c9512"
  },
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "from dotenv import load_dotenv\n",
    "import os\n",
    "import pandas as pd\n",
    "import plotly.graph_objects as go\n",
    "\n",
    "from compute_sdk.clients.engine.compute_client_engine import ComputeClientEngine\n",
    "from compute_sdk.clients.engine.data_objects import ComputeAppRequest"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## 2. Load the API key from a .env file. \n",
    "\n",
    "NOTE: This key can also be loaded directly, but it's not recommended for security reasons."
   ],
   "id": "b82916310786945e"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "load_dotenv()\n",
    "\n",
    "API_KEY = os.getenv(\"API_KEY\")"
   ],
   "id": "31281fc45c275233",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## 3. Create Compute Engine client\n",
    "This creates the client that you'll use to interact with the Compute Engine. In this example, we'll use it to solve a Compute application. \n",
    "\n",
    "NOTE: You must have a running process of the Compute Engine, otherwise the next line will result in a connection error."
   ],
   "id": "82c66f78de5c56d4"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "engine_client = ComputeClientEngine(api_key=API_KEY)",
   "id": "2f0fa093e626991e",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## 4. Create a response object\n",
    "In order to send a request to solve an app, you first need to define a ComputeAppResponse object. \n",
    "This object includes essential data such as the name of the app, the collection uuid, set of input arguments and whether we want to run the solution or not."
   ],
   "id": "ff22b33081d0556f"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "from dataclasses import dataclass\n",
    "\n",
    "\n",
    "# Define some class to hold the data\n",
    "@dataclass\n",
    "class Forces:\n",
    "    N_Ed: float\n",
    "    N_G_Ed: float\n",
    "    M_y_Ed_top: float\n",
    "    M_y_Ed_bottom: float\n",
    "    M_z_Ed_top: float\n",
    "    M_z_Ed_bottom: float\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class Material:\n",
    "    fck_class: str\n",
    "    fyk_grade: str\n",
    "    grade: str\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class Geometry:\n",
    "    L: float\n",
    "    beta_y: float\n",
    "    beta_z: float\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class CrossSection:\n",
    "    rect_circ: str\n",
    "    h: float\n",
    "    b: float\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class SteelCrossSection:\n",
    "    section_type: str = \"I-Section\"\n",
    "    h_a: int = 400  # Height of box section\n",
    "    b_a: int = 200  # Width of box section\n",
    "    t_f: int = 20  # Flange thickness\n",
    "    t_w: int = 10  # Web thickness\n",
    "    s: float = 8  # Leg length\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class Reinforcement:\n",
    "    c: int\n",
    "    dia_link: int\n",
    "    dia: int\n",
    "    n_bars_y: int\n",
    "    n_bars_z: int\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class LongTermParams:\n",
    "    cement_class: str\n",
    "    Rh: int\n",
    "    t_0: int\n",
    "\n",
    "\n",
    "@dataclass\n",
    "class Settings:\n",
    "    method: str = \"\""
   ],
   "id": "6522ccbf26be780b",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# First we define the request data\n",
    "app_name = \"EC4_Composite_Column_Design_Axial_Moment\"\n",
    "collection_uuid = \"b9518685-d7ca-40e5-86d0-8f39921e7e75\""
   ],
   "id": "9d42e96d7a8dce3f",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# Read excel\n",
    "df = pd.read_excel(\"composite_col_input.xlsx\")\n",
    "n_rows = df.shape[0]\n",
    "df"
   ],
   "id": "5bf7dc866796310f",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# ======== Hard-coded inputs ========\n",
    "# Material\n",
    "fck_class = \"C50/60\"\n",
    "fyk_grade = \"500 B\"\n",
    "grade = \"S355\"\n",
    "material = Material(fck_class=fck_class, fyk_grade=fyk_grade, grade=grade)\n",
    "# Geometry\n",
    "beta_y = 0.85\n",
    "beta_z = 0.85\n",
    "# Reinforcement\n",
    "c = 35\n",
    "dia_link = 12\n",
    "dia = 16\n",
    "# Parameters to calculate long-term effects\n",
    "cement_class = \"N\"\n",
    "Rh = 50\n",
    "t_0 = 28\n",
    "# Settings\n",
    "method = \"EC4 - Full plastic\""
   ],
   "id": "23874b3d727e5b27",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# ======== Inputs ========\n",
    "input_args = []\n",
    "for i in range(n_rows):\n",
    "    curr_row = df.iloc[i]\n",
    "    # Forces\n",
    "    force = Forces(N_Ed=curr_row[\"Fx\"], N_G_Ed=curr_row[\"Fx\"],\n",
    "                   M_y_Ed_top=curr_row[\"My_top\"], M_y_Ed_bottom=curr_row[\"My_bot\"],\n",
    "                   M_z_Ed_top=curr_row[\"Mz_top\"], M_z_Ed_bottom=curr_row[\"Mz_bot\"])\n",
    "    # Materials\n",
    "    material = Material(fck_class=fck_class, fyk_grade=fyk_grade, grade=grade)\n",
    "    # Geometry \n",
    "    geometry = Geometry(L=curr_row[\"Length\"] * 1000, beta_y=beta_y, beta_z=beta_z)\n",
    "    # Cross-Section\n",
    "    cross_section = CrossSection(curr_row[\"Encasement_Type\"], curr_row[\"Encasement_h\"] * 1000,\n",
    "                                 curr_row[\"Encasement_b\"] * 1000)\n",
    "    # Steel cross-section\n",
    "    steel_cross_section = SteelCrossSection(section_type=\"I-Section\",\n",
    "                                            h_a=curr_row[\"I-Section Depth [mm]\"],\n",
    "                                            b_a=curr_row[\"I-Section Width [mm]\"],\n",
    "                                            t_f=curr_row[\"Flange Thickness [mm]\"],\n",
    "                                            t_w=curr_row[\"Web Thickness [mm]\"],\n",
    "                                            s=curr_row[\"Root Radius [mm]\"])\n",
    "\n",
    "    section_type: str = \"I-Section\"\n",
    "    h_a: int = 400  # Height of box section\n",
    "    b_a: int = 200  # Width of box section\n",
    "    t_f: int = 20  # Flange thickness\n",
    "    t_w: int = 10  # Web thickness\n",
    "    s: float = 8  # Leg length\n",
    "    # Reinforcement details\n",
    "    reinforcement = Reinforcement(c=c, dia_link=dia_link, dia=dia,\n",
    "                                  n_bars_y=int(curr_row[\"Number of reinforcement bars parallel to y-axis\"]),\n",
    "                                  n_bars_z=int(curr_row[\"Number of reinforcement bars parallel to z-axis\"]))\n",
    "    # Parameters to calculate long-term effects (creep)\n",
    "    long_term_param = LongTermParams(cement_class=cement_class, Rh=Rh, t_0=t_0)\n",
    "    # Settings\n",
    "    settings = Settings(method=method)\n",
    "    # Append current input args\n",
    "    curr_args = vars(force) | vars(material) | vars(geometry) | vars(cross_section) | vars(steel_cross_section) | vars(\n",
    "        reinforcement) | vars(long_term_param) | vars(settings)\n",
    "    input_args.append(curr_args)\n",
    "\n",
    "input_args"
   ],
   "id": "2f432f3dcac29cff",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "run_solution = True\n",
    "app_request = ComputeAppRequest.from_data(app_name=app_name,\n",
    "                                          input_args=input_args,\n",
    "                                          collection_uuid=collection_uuid,\n",
    "                                          run_solution=run_solution)"
   ],
   "id": "cacb9fdc8dae7d7d",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## 5. Use the Client to solve the app\n",
    "\n",
    "This sends the request to the engine to solve the app. It then returns a response object where we can inspect the payload, and also any error that might have occurred during execution"
   ],
   "id": "51e765ef60c2c842"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "results = engine_client.solve_app(app_request=app_request)",
   "id": "6931e08ec0fd8d84",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## 6. Extract the checks values to a pandas dataframe for easier inspection.",
   "id": "213d0228f36fb142"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# Extract the checks values\n",
    "checks = dict(strength_util_check_1=[],\n",
    "              buckling_util_check_1=[],\n",
    "              buckling_util_check_2=[],\n",
    "              bending_util_check_1=[],\n",
    "              bending_util_check_2=[],\n",
    "              bending_util_check_3=[])\n",
    "check_names = list(checks.keys())\n",
    "for res in results:\n",
    "    for name in check_names:\n",
    "        if res.status != 200 or res.logging:  # means something went wrong for this particular result\n",
    "            continue\n",
    "        checks[name].append(res.results_payload[name][0])\n",
    "capacity_df = pd.DataFrame(checks)\n",
    "check_names_map = dict(strength_util_check_1=\"Axial capacity\",\n",
    "                       buckling_util_check_1=\"Buckling Utilisation Check - about y-y axis\",\n",
    "                       buckling_util_check_2=\"Buckling Utilisation Check - about z-z axis\",\n",
    "                       bending_util_check_1=\"Bending Utilisation Check - about y-y axis\",\n",
    "                       bending_util_check_2=\"Bending Utilisation Check - about z-z axis\",\n",
    "                       bending_util_check_3=\"Biaxial Utilisation Check\")\n",
    "capacity_df = capacity_df.rename(check_names_map, axis=1)\n",
    "capacity_df"
   ],
   "id": "9e5828cab747e895",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# Checks with inputs\n",
    "input_args_df = pd.DataFrame(input_args)\n",
    "final_res = pd.concat([capacity_df, input_args_df], axis=\"columns\")\n",
    "final_res"
   ],
   "id": "7eb06914dbdd2281",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "check_cols = final_res.columns[:6]\n",
    "# Get the remaining columns (to show on hover)\n",
    "other_cols = [col for col in final_res.columns if col not in check_cols]\n",
    "\n",
    "# Convert index to a string for proper x-axis labeling\n",
    "final_res.index = final_res.index.astype(str)\n",
    "\n",
    "custom_data_array = final_res[other_cols].to_numpy()  # Ensure the data shape matches rows\n",
    "\n",
    "# Create a figure\n",
    "fig = go.Figure()\n",
    "\n",
    "# Loop through the check columns and plot each with hover data\n",
    "for col in check_cols:\n",
    "    fig.add_trace(go.Scatter(\n",
    "        x=final_res.index,  # Use row index as x values\n",
    "        y=final_res[col],  # y values for this column\n",
    "        mode=\"lines+markers\",\n",
    "        name=col,\n",
    "        customdata=custom_data_array,  # Attach remaining column values for hover\n",
    "    ))\n",
    "\n",
    "initial_range = [final_res.index[0], final_res.index[min(9, len(final_res) - 1)]]  # Show first 10 rows initially\n",
    "# Customize layout\n",
    "fig.update_layout(\n",
    "    title=\"Checks plot\",\n",
    "    xaxis_title=\"Row Index\",\n",
    "    yaxis_title=\"Check Value [%]\",\n",
    "    height=800,\n",
    "    width=1200,\n",
    "    xaxis=dict(\n",
    "        type='category',  # Treat row index as categorical\n",
    "        rangeslider=dict(visible=True),  # Enable range slider\n",
    "        range=initial_range\n",
    "    ),\n",
    "    yaxis=dict(range=[0, 100]),\n",
    "    hovermode=\"x unified\",  # Ensures hover is triggered across all curves at the same x position\n",
    ")\n",
    "\n",
    "# Show figure\n",
    "fig.show()\n"
   ],
   "id": "12f1e0b8f18ad162",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "",
   "id": "7ead3c3acb4cd31d",
   "outputs": [],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
