{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "88c6c7e6-596f-4b0c-8012-02b92ea50bb4",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import pymbse\n",
    "from pymbse.query.api import PyMBSE\n",
    "from pymbse.microservice.api import init_upload_and_run, download_artefact, get_artefact_names\n",
    "\n",
    "from roxieapi.commons.roxie_constants import PlotLabels\n",
    "from roxieapi.commons.types import Plot2D, Plot3D\n",
    "from roxieapi.input.builder import RoxieInputBuilder\n",
    "from roxieapi.output.plots import RoxiePlotOutputs\n",
    "from roxieapi.cadata.CableDatabase import CableDatabase\n",
    "\n",
    "import scrapbook as sb\n",
    "import pandas as pd\n",
    "\n",
    "from pathlib import Path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c61e260-a20e-4b0a-bf0b-eb4fd0784ffa",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# General settings\n",
    "config_file = './pymbse_example.yml'\n",
    "tmp_folder = Path('./tmp')\n",
    "input_folder = Path('./inputs')\n",
    "output_folder = Path('./outputs')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5e44c012-6e1b-4a6d-b880-f8ee8c9454bc",
   "metadata": {
    "tags": [
     "parameters"
    ]
   },
   "outputs": [],
   "source": [
    "# Parameters\n",
    "zo_start= 250\n",
    "dz = 50\n",
    "add_z0 = 20"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d130ba72-82de-4a78-9d81-f90918833880",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Model specific settings\n",
    "\n",
    "model_name = '3d_connection_side'\n",
    "\n",
    "# Files required by a roxie run. This will collect inputs from artefacts and input files\n",
    "filelist = {\n",
    "    'data' : None,\n",
    "    'cadata' : None,\n",
    "    'bhdata' : None,\n",
    "    'hmo' : None,\n",
    "    'iron': None,\n",
    "}\n",
    "\n",
    "# A list of all input files (for later use)\n",
    "input_files = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6020009e-63f2-487f-8500-e02e86cb6823",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Load values from config\n",
    "config = pymbse.query.config.pymbse_config.load_pymbse_config(config_file)\n",
    "model_config = config.models[model_name]\n",
    "\n",
    "for file in model_config.input_files:\n",
    "    suf = Path(file).suffix[1:]\n",
    "    if suf in filelist:\n",
    "        filelist[suf]=file\n",
    "    if suf != 'data':\n",
    "        input_files.append(file)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d797440-ec56-42e1-b838-bccef7604241",
   "metadata": {},
   "source": [
    "## Dependency execution\n",
    "\n",
    "Next step is to run dependend models. Iterates through all dependend models, executes them, and stores the results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "840b236a-7204-4667-986d-3076c15e5f0a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "dependend_models = {}\n",
    "artefacts = {}\n",
    "for dm in model_config.needs:\n",
    "    dependend_models[dm] = PyMBSE(config_path=config_file,source_model=model_name,target_model=dm).build()\n",
    "    model_artefacts = dependend_models[dm].get_artefacts()\n",
    "    for ma in model_artefacts:\n",
    "        filename = ma\n",
    "        if ma in filelist:\n",
    "            filename = f\"{dm}.{ma}\"\n",
    "            filelist[ma] = filename\n",
    "        with open(tmp_folder / filename, 'w') as f:\n",
    "            f.write(model_artefacts[ma])\n",
    "        input_files.append(str(tmp_folder / filename))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7fe477f0-3774-489e-9ee7-0497f687bb60",
   "metadata": {},
   "source": [
    "## Sanity check - Are all files present\n",
    "\n",
    "A quick check to see if all required input files are defined.\n",
    "\n",
    "The minimum requirements are a datafile + a cadata file.\n",
    "\n",
    "If there is a hmo or iron file linked, there also needs to be a bhdata file present"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da9b43b1-c6d6-476c-bded-38b0ded2f055",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "assert filelist['data'], \"Missing Datafile\"\n",
    "assert filelist['cadata'], \"Missing cadata file\"\n",
    "if filelist['iron'] or filelist['hmo']:\n",
    "    assert filelist['bhdata'], \"Iron mesh given, but no bhdata file\"\n",
    "print(input_files)\n",
    "print(filelist)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03d8f6db-1ccf-47ff-a87d-e9a3a1d5fb74",
   "metadata": {
    "tags": []
   },
   "source": [
    "## Prepare datafile for execution\n",
    "\n",
    "Generate a new datafile, and replace paths to dependend files\n",
    "\n",
    "Here, we could also add modifications on the datafile (adding blocks, removing flags, etc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ce7d14a-4427-403a-86b8-0f39e1288325",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "roxie_input = RoxieInputBuilder.from_datafile(tmp_folder/filelist['data'])\n",
    "cadb = CableDatabase.read_cadata(tmp_folder/filelist['cadata'])\n",
    "roxie_input.cadata_path = Path(filelist['cadata']).name\n",
    "if filelist['bhdata']:\n",
    "    roxie_input.bhdata_path = Path(filelist['bhdata']).name\n",
    "if filelist['hmo'] and roxie_input.flags['LHMO']:\n",
    "    roxie_input.iron_path = Path(filelist['hmo']).name\n",
    "if filelist['iron']:\n",
    "    roxie_input.iron_path = Path(filelist['iron']).name"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa52eb4e-a156-46d6-a911-00b8faa2cf43",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Make connection side magic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fb761735-5c7a-4f48-b704-def94353f474",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def move_ends(roxie_input: RoxieInputBuilder, mapping: dict[int,int]):\n",
    "    existing_ends = list(roxie_input.block3d['ne'])\n",
    "    # check that mapping values don't overlap with existing blocks (raise exception)\n",
    "    for k,v in mapping.items():\n",
    "        if k not in existing_ends:\n",
    "            raise ValueError(f\"End {k} not in existing block3d\")\n",
    "        #if v in existing_ends:\n",
    "        #    raise ValueError(f\"End {v} already exists (mapping {k} to {v})\")\n",
    "    # Move blocks\n",
    "    for k,v in mapping.items():\n",
    "        #block\n",
    "        roxie_input.block.loc[roxie_input.block3d['ne'] == k, 'ne'] = v\n",
    "        \n",
    "        #block3d\n",
    "        roxie_input.block3d.loc[roxie_input.block3d['ne'] == k, 'ne'] = v\n",
    "\n",
    "def move_blocks(roxie_input: RoxieInputBuilder, mapping: dict[int,int]):\n",
    "    \"\"\" Move blocks in the roxie input file from number x to y\n",
    "        :param roxie_input: RoxieInputBuilder object\n",
    "        :param mapping: dictionary with mapping from old block number to new block number\n",
    "    \"\"\"\n",
    "    existing_blocks = list(roxie_input.block['no'])\n",
    "    # check that mapping values don't overlap with existing blocks (raise exception)\n",
    "    for k,v in mapping.items():\n",
    "        if k not in existing_blocks:\n",
    "            raise ValueError(f\"Block {k} not in existing blocks\")\n",
    "        #if v in existing_blocks:\n",
    "        #    raise ValueError(f\"Block {v} already exists (mapping {k} to {v})\")\n",
    "    \n",
    "    # Move blocks\n",
    "    for k,v in mapping.items():\n",
    "        # block\n",
    "        roxie_input.block.loc[roxie_input.block['no'] == k, 'no'] = v\n",
    "        # layer\n",
    "        if not roxie_input.layer.empty:\n",
    "            extracted = [list([v if x == k else x for x in y]) for y in roxie_input.layer['blocks']]\n",
    "            print(extracted)\n",
    "            roxie_input.layer['blocks'] =pd.Series(extracted)\n",
    "        # design Variables\n",
    "        if not roxie_input.design.empty:\n",
    "            filt = roxie_input.design['act'].isin([2,3])\n",
    "            extracted = [list([v if x == k else x for x in y]) for y in roxie_input.design.loc[filt, 'bcs']]\n",
    "            roxie_input.design.loc[filt, 'bcs'] = pd.Series(extracted)\n",
    "        # transformations\n",
    "        if not roxie_input.euler.empty:\n",
    "            filt = roxie_input.euler['act'].isin([2,3])\n",
    "            roxie_input.euler.loc[filt, 'bcs'] = pd.Series([list([v if x == k else x for x in y]) for y in roxie_input.euler.loc[filt, 'bcs']])\n",
    "        # objectives\n",
    "        if not roxie_input.objective.empty:\n",
    "            block_objectives = [\"RADMAX\",\n",
    "                \"EREG\",\n",
    "                \"TORSIO\",\n",
    "                \"GEOSTR\",\n",
    "                \"GEODE\",\n",
    "                \"NORMA\",\n",
    "                \"STRETC\",\n",
    "                \"SQUEEZ\",\n",
    "                \"CURVAT\",\n",
    "                \"PEAK3D\",\n",
    "                \"LOADLI\",\n",
    "                \"PEAK\",\n",
    "                \"BVOLCU\",\n",
    "                \"BVOLSC\",\n",
    "                \"INCLIN\",\n",
    "                \"PHI1\",\n",
    "                \"PHI2\",\n",
    "                \"PHI3\",\n",
    "                \"PHI4\",\n",
    "                \"CCTL\",\n",
    "                \"CCTLC\",\n",
    "                \"CCTD\",\n",
    "                \"XPOS1\",\n",
    "                \"XPOS2\",\n",
    "                \"XPOS3\",\n",
    "                \"XPOS4\",\n",
    "                \"YPOS1\",\n",
    "                \"YPOS2\",\n",
    "                \"YPOS3\",\n",
    "                \"YPOS4\",\n",
    "                \"RPOS1\",\n",
    "                \"CURNTB\",\n",
    "            ]\n",
    "            filt = (roxie_input.objective['string'].isin(block_objectives)) & (roxie_input.objective['s1'] == k)\n",
    "            roxie_input.objective.loc[filt, 's2'] = v\n",
    "        \n",
    "        # peak fields\n",
    "        roxie_input.peak = [v if x == k else x for x in roxie_input.peak]\n",
    "        # graphs\n",
    "        if not roxie_input.graph.empty:\n",
    "            block_graphs = [\n",
    "                \"CURNTB\",\n",
    "                \"PEAK\",\n",
    "                \"PEAK3D\",\n",
    "                \"LOADLI\",\n",
    "                \"BTORS\",\n",
    "                \"BNORMA\",\n",
    "                \"BPEAK\",\n",
    "                \"BGEODE\",\n",
    "                \"DTRF\",\n",
    "                \"BOVERI\",\n",
    "                \"NIB\",\n",
    "            ]\n",
    "            filt = (roxie_input.graph['yvalue'].isin(block_graphs) ) & (roxie_input.graph['s1y'] == k)\n",
    "            roxie_input.graph.loc[filt, 's1y'] = v\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc9cd648",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def make_connectionside(roxie_input: RoxieInputBuilder,layer_nr : int, lead_pos_left = True, add_zo=100,layerjump_reduction=20):\n",
    "    \"\"\" Transform 3D file to create a connection side from a return end\n",
    "        :param roxie_input: RoxieInputBuilder object\n",
    "        :param layer_nr: layer number to create the connection side\n",
    "        :param lead_pos_left: if True, the lead is on the left side, otherwise on the right side\n",
    "    \"\"\"\n",
    "    block_cnt = 1 # Continuous block counter\n",
    "    blocks = [] # New Blocks\n",
    "    blocks3d = [] # End definition\n",
    "    glued_blocks = []\n",
    "\n",
    "    freed_orig = []\n",
    "    freed_mir = []\n",
    "    blocks_orig = []\n",
    "    blocks_mir = []\n",
    "\n",
    "    layer_row = roxie_input.layer.loc[roxie_input.layer['no'] == layer_nr].iloc[0]\n",
    "    if layer_row.empty:\n",
    "        raise ValueError(f\"Layer {layer_nr} not found\")\n",
    "    block_ids = layer_row.blocks\n",
    "    symm = layer_row.symm\n",
    "    nco_count = roxie_input.block.loc[roxie_input.block['no'].isin(block_ids),'nco']\n",
    "    nr_blocks_half = 2*sum(nco_count > 1) + sum(nco_count == 1)\n",
    "    \n",
    "    if lead_pos_left:\n",
    "        turn_n = 0\n",
    "        imag_n = 0\n",
    "        turn_m = 360.0/symm\n",
    "        imag_m = 1\n",
    "    else:\n",
    "        turn_n = 360.0/symm\n",
    "        imag_n = 1\n",
    "        turn_m = 0\n",
    "        imag_m = 0\n",
    "        \n",
    "    \n",
    "    for block_id in block_ids:\n",
    "        block2d = roxie_input.block.loc[roxie_input.block['no']==block_id].copy()\n",
    "        block3d = roxie_input.block3d.loc[roxie_input.block3d['ne']==int(block2d['ne'])].copy()\n",
    "        nc = block2d['nco'].iat[0]\n",
    "\n",
    "        # Block ids for mirrored blocks\n",
    "        block_id_mir_free = nr_blocks_half+block_cnt\n",
    "        block_id_mir = block_id_mir_free + 1\n",
    "\n",
    "        if nc > 1:\n",
    "            block_id_main = block_cnt\n",
    "            block_id_free = block_cnt + 1\n",
    "            block_cnt += 2\n",
    "\n",
    "            block2d = block2d.copy()\n",
    "            block3d = block3d.copy()\n",
    "            block2d['no']=block_id_main\n",
    "            block2d['nco']=nc-1\n",
    "            block2d['imag']=imag_n\n",
    "            block2d['turn']=turn_n\n",
    "            block2d['ne']=block_id_main\n",
    "            block3d['ne']=block_id_main\n",
    "            blocks.append(block2d)\n",
    "            blocks3d.append(block3d)\n",
    "\n",
    "            # Glue next block to this one\n",
    "            glued_blocks.append(block_id_free)\n",
    "            blocks_orig.append(block_id_main)\n",
    "        else:\n",
    "            block_id_free = block_cnt\n",
    "            block_cnt += 1\n",
    "\n",
    "        # Free last conductor as separate block\n",
    "        block2d = block2d.copy()\n",
    "        block3d = block3d.copy()\n",
    "        block2d['no']=block_id_free\n",
    "        block2d['nco']=1\n",
    "        block2d['imag']=imag_n\n",
    "        block2d['turn']=turn_n\n",
    "\n",
    "        block2d['ne']=block_id_free\n",
    "        block3d['ne']=block_id_free\n",
    "        blocks.append(block2d)\n",
    "        blocks3d.append(block3d)\n",
    "        blocks_orig.append(block_id_free)\n",
    "        freed_orig.append(block_id_free)\n",
    "\n",
    "        # Mirrored version, first conductor\n",
    "        block2d = block2d.copy()\n",
    "        block3d = block3d.copy()\n",
    "        block2d['imag']=imag_m\n",
    "        block2d['turn']=turn_m\n",
    "        block2d['no']=block_id_mir_free\n",
    "        block2d['nco']=1\n",
    "        block2d['ne']=block_id_mir_free\n",
    "        block3d['ne']=block_id_mir_free\n",
    "        blocks.append(block2d)\n",
    "        blocks3d.append(block3d)\n",
    "        blocks_mir.append(block_id_mir_free)\n",
    "        freed_mir.append(block_id_mir_free)\n",
    "\n",
    "        # Mirrored, rest\n",
    "        if nc > 1:\n",
    "            glued_blocks.append(block_id_mir)\n",
    "            block2d = block2d.copy()\n",
    "            block3d = block3d.copy()\n",
    "            block2d['no']=block_id_mir\n",
    "            block2d['nco']=nc-1\n",
    "            block2d['ne']=block_id_mir\n",
    "            block3d['ne']=block_id_mir\n",
    "            blocks.append(block2d)\n",
    "            blocks3d.append(block3d)\n",
    "            blocks_mir.append(block_id_mir)\n",
    "    \n",
    "\n",
    "    nr_design = len(roxie_input.design)\n",
    "    design_glued = pd.DataFrame([\n",
    "    [nr_design+1,         0 ,         0 ,     0  ,  5  ,    \"ALPHR\" ,    3, glued_blocks ],\n",
    "    [nr_design+2,         0 ,        0 ,      0  , 5   ,    \"PHIR\" ,    3, glued_blocks ],\n",
    "    ],columns=[\"no\",\"xl\",\"xu\",\"xs\",\"genen\",\"string\",\"act\",\"bcs\"])\n",
    "    nr_design += 2\n",
    "\n",
    "    block_updated = pd.concat(blocks).reset_index(drop=True)\n",
    "    block3d_updated = pd.concat(blocks3d).reset_index(drop=True)\n",
    "\n",
    "\n",
    "    additional_dvs = []\n",
    "\n",
    "    # Connection\n",
    "    b_conn = freed_mir[0]\n",
    "    zo =float(block3d_updated.loc[block3d_updated['ne']==b_conn,'zo'])\n",
    "    block3d_updated.loc[block3d_updated['ne']==b_conn,['type','beta','bo','zo','wi','wo','hwed','horder']] = (90,0,zo+add_zo,0,0,0,0,0)\n",
    "    blocks_mir.remove(b_conn)\n",
    "\n",
    "    # Layer jump\n",
    "    b_layerjump = freed_orig[-1]\n",
    "    zo =float(block3d_updated.loc[block3d_updated['ne']==b_layerjump,'zo'])-layerjump_reduction\n",
    "    block3d_updated.loc[block3d_updated['ne']==b_layerjump,['zo']] = zo\n",
    "    additional_dvs.append([nr_design+1,         zo+add_zo ,         zo+add_zo ,    zo+add_zo  ,  5  ,    \"Z0\" ,    3, [b_layerjump]])\n",
    "    additional_dvs.append([nr_design+2,         0 ,         0 ,     0  ,  5  ,    \"CENTER\" ,    3, [b_layerjump]])\n",
    "    nr_design +=2\n",
    "\n",
    "    block3d_updated.loc[block3d_updated['ne']!=b_conn,'zo']+=add_zo\n",
    "\n",
    "    # Block connections\n",
    "    for b_orig,b_mir in zip(freed_orig[:-1],freed_mir[1:]):\n",
    "        f_orig = block3d_updated['ne']==b_orig\n",
    "        f_mir = block3d_updated['ne']==b_mir\n",
    "        bldata_orig = block_updated.loc[block_updated['no']==b_orig]\n",
    "        geom = cadb.get_cable_definition(bldata_orig.loc[f_orig,'condname'].iat[0])\n",
    "        cable_add = bldata_orig.loc[f_orig,'nco']*(geom.thickness_o+float(block3d_updated.loc[f_orig,'wi']))\n",
    "        ref_vals = {}\n",
    "        dv_names = {'zo' : 'Z0', 'bo' : 'BOVERA', 'beta' : 'BETA'}\n",
    "        zo_ref = float((float(block3d_updated.loc[f_orig,'zo'])+cable_add+float(block3d_updated.loc[f_mir,'zo']))/2)\n",
    "        ref_vals['zo'] = zo_ref\n",
    "        for val_key in ['beta','bo']:\n",
    "            val_mid = (float(block3d_updated.loc[f_orig,val_key])+float(block3d_updated.loc[f_mir,val_key]))/2\n",
    "            ref_vals[val_key]=val_mid\n",
    "\n",
    "        for val_key,val_mid in ref_vals.items():\n",
    "            block3d_updated.loc[f_orig,val_key]= val_mid\n",
    "            block3d_updated.loc[f_mir,val_key]= val_mid\n",
    "            nr_design += 1\n",
    "            additional_dvs.append([nr_design,         val_mid ,         val_mid ,     val_mid  ,  5  ,    dv_names[val_key] ,    3, [b_orig,b_mir]])\n",
    "        # Add CENTER definition\n",
    "        additional_dvs.append([nr_design+1,         0 ,         0 ,     0  ,  5  ,    \"CENTER\" ,    3, [b_orig,b_mir]])\n",
    "        nr_design +=1\n",
    "\n",
    "    additional_dvs_df = pd.DataFrame(additional_dvs,columns=design_glued.columns)\n",
    "    design_updated = pd.concat([roxie_input.design,design_glued, additional_dvs_df]).reset_index(drop=True)\n",
    "    layer_updated =  pd.DataFrame([(1,symm,0,1,blocks_orig),(1,symm,0,1,blocks_mir),(1,symm,0,1,[int(b_conn)])],columns=roxie_input.layer.columns)\n",
    "    roxie_input.design = design_updated.sort_values(by=['no'])\n",
    "    roxie_input.block = block_updated.sort_values(by=['no'])\n",
    "    roxie_input.block3d = block3d_updated.sort_values(by=['ne'])\n",
    "    roxie_input.layer=layer_updated.sort_values(by=['no'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1586869b-6ffd-4221-9537-7bc704c6b976",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import copy\n",
    "# Make Outer layer Connection side\n",
    "input_outer_layer = copy.deepcopy(roxie_input)\n",
    "make_connectionside(input_outer_layer,1,True)\n",
    "\n",
    "# Make Inner layer connection side\n",
    "input_inner_layer = copy.deepcopy(roxie_input)\n",
    "make_connectionside(input_inner_layer,2,False)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb11b025-d641-42cc-9e2c-97c3e760cf8d",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "roxie_input.block"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4aa70ffc-aa13-4068-8b3b-fd3f4501e3e1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "input_outer_layer.block"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e1ec8f7e-5213-4f15-91d9-8877c1ffc39c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "input_inner_layer.block"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79acb3c6-4108-4aec-9d50-339472140175",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Bring both of them together"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f07c8bcc-f69f-4ab7-911b-d367d9c39cac",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "roxie_input_combined = copy.deepcopy(input_inner_layer)\n",
    "offset_outer_layer = input_outer_layer.block['no'].max()\n",
    "move_blocks(roxie_input_combined,{i:i+offset_outer_layer for i in reversed(roxie_input_combined.block['no'])})\n",
    "move_ends(roxie_input_combined,{i:i+offset_outer_layer for i in reversed(roxie_input_combined.block3d['ne'])})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8099c073-225c-43e8-99de-8276fde6dc26",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "roxie_input_combined.layer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "43ad5dab-5920-40b2-80c3-8f574ba99504",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "roxie_input_combined.block = pd.concat([input_outer_layer.block,roxie_input_combined.block], ignore_index=True)\n",
    "roxie_input_combined.block3d = pd.concat([input_outer_layer.block3d,roxie_input_combined.block3d],ignore_index=True)\n",
    "roxie_input_combined.layer   = pd.concat([input_outer_layer.layer,roxie_input_combined.layer],ignore_index=True)\n",
    "roxie_input_combined.euler   = pd.concat([input_outer_layer.euler,roxie_input_combined.euler],ignore_index=True)\n",
    "roxie_input_combined.design   = pd.concat([input_outer_layer.design,roxie_input_combined.design],ignore_index=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54411d44-715e-4b4a-8487-19c00ffbf6bf",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "roxie_input_combined.layer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "124560c8-bea3-4d5b-9aac-66d8cc2cd4b2",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "roxie_input_combined.design['no'] = roxie_input_combined.design.index + 1\n",
    "#roxie_input_combined.euler['no']= roxie_input_combined.euler.index + 1\n",
    "roxie_input_combined.layer['no']=roxie_input_combined.layer.index+1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d76cd27-c3bb-4985-971b-beb84668b9e3",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "#offset_layer1 = 8\n",
    "#\n",
    "#move_ends(roxie_input,{i:i+offset_layer1 for i in reversed(roxie_input.block3d['ne'])})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04b1d689-1e25-459d-b655-b7f4e0a2b446",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Store them separately\n",
    "datafile_name = tmp_folder / f'{model_name}.data'\n",
    "tmp_folder.mkdir(exist_ok=True)\n",
    "input_inner_layer.build(datafile_name.with_name(f'{model_name}_inner.data'))\n",
    "input_outer_layer.build(datafile_name.with_name(f'{model_name}_outer.data'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0751a2c1-e30b-466f-bb22-bb93ac37b1f1",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Build and store datafile\n",
    "# Store in temp folder\n",
    "datafile_name = str(tmp_folder / f'{model_name}.data')\n",
    "tmp_folder.mkdir(exist_ok=True)\n",
    "roxie_input_combined.build(datafile_name)\n",
    "input_files.append(datafile_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdb9a8eb-3c88-4aa8-94d7-0ae29a06441a",
   "metadata": {},
   "source": [
    "## Roxie execution\n",
    "\n",
    "With dependencies resolved and input generated, Roxie can be called from the script"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "158860a6-5d25-4954-b6e1-551c85a46c8a",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "url_with_port = \"http://roxie:8080\"\n",
    "\n",
    "model_name, timestamp, output_lines, error_lines = init_upload_and_run(url_with_port, model_name, input_files)\n",
    "\n",
    "# Plot Endspacer\n",
    "\n",
    "artefact_list = get_artefact_names(url_with_port,model_name,timestamp)\n",
    "# Download artefacts into temp folder (for now, only output and post.xml file\n",
    "for artefact in [f'{model_name}.output', f'{model_name}.post.xml',f'{model_name}.pdf']:\n",
    "    download_artefact(url_with_port, model_name, timestamp, tmp_folder, artefact)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1f74454-6c90-4e06-8156-4e5e4294cb9c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "plots = RoxiePlotOutputs(\n",
    "    tmp_folder/f'{model_name}.post.xml',\n",
    "    tmp_folder/f'{model_name}.data',\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "437d9a9c-52f6-49c1-adcd-77342d85eb76",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "for error_line in error_lines:\n",
    "    print(error_line)\n",
    "\n",
    "for output_line in output_lines:\n",
    "    print(output_line)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7a8a046-efc9-483d-98de-a54e0d3b5145",
   "metadata": {},
   "source": [
    "# Retreive outputs\n",
    "\n",
    "From the results, we can download the artefacts into the tmp folder\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f225a34d-eeb6-4c99-a6ca-71a68a22b139",
   "metadata": {},
   "source": [
    "## Output plotting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a89d98d2-ea39-40f7-a7bd-4620ec471c38",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import pyvista as pv\n",
    "pv.start_xvfb()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a3f698b-19f0-4fc2-8b09-bbc35017bf31",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "plot_def = Plot3D.create(\"3D coils\")\n",
    "fig = plots.plots3d.plot_3d(plot_def)\n",
    "fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98fe4015-95c3-4d7b-800a-4f85fb9257d9",
   "metadata": {},
   "source": [
    "## Storage of Results\n",
    "\n",
    "For this notebook to act as a model, we need to define its outputs and store them.\n",
    "The following lines of code create two dictionaries: FOM and artefacts.\n",
    "\n",
    "### fom\n",
    "\n",
    "The Figures of Merit (fom) are a list of Key-Value pairs for output values. This could be objectives, or results for specific purposes.\n",
    "\n",
    "More generally, the artefacts output is a list of files, which will be attached as a whole."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69bd7695-ec37-4425-81a9-7ced2e91dde0",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "fom_dct = {}\n",
    "artefacts_dct = {'xml': str(tmp_folder/f'{model_name}.post.xml'), 'data': str(tmp_folder/f'{model_name}.data')}\n",
    "data = {'fom': fom_dct, 'artefacts': artefacts_dct}\n",
    "sb.glue('model_results', data=data, encoder='json')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b72deaf0-9f98-4861-a14c-0dc8c5bdcb68",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
