{ "cells": [ { "cell_type": "markdown", "id": "f4c866d0", "metadata": {}, "source": [ "# Servicing Strategies\n", "\n", "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/WISDEM/WOMBAT/main?filepath=examples)\n", "\n", "In this example, we'll demonstrate the essential differences in scheduled servicing, unscheduled servicing, and tow-to-port repair strategies. Each of the examples demonstrated below will be based on the 2015 Dinwoodie, et al. paper, though the variations will be for demonstration purposes only.\n", "\n", "A Jupyter notebook of this tutorial can be run from\n", "`examples/strategy_demonstration.ipynb`locally, or through\n", "[binder](https://mybinder.org/v2/gh/WISDEM/WOMBAT/main?filepath=examples).\n", "\n", "## WOMBAT Setup and Variables\n", "\n", "The vessels that will be changed in this demonstration are the field support vessel (FSV) with capability: \"SCN\", the heavy lift vessel (HLV) with capability: \"LCN\", and the tugboats, which have capability: \"TOW\".\n", "\n", "```{note}\n", "When running tow-to-port a `Port` configuration is also required, which will control the tugboats. However, the port costs will still be accounted for in the\n", "`FixedCosts` class as port fees are assumed to be constant. These costs are not considered in this example, so differences in cost should be taken with a grain\n", "of salt given the reduction of HLV and FSV operational and mobilization costs.\n", "```\n", "\n", "Scenario descriptions:\n", "- **Scheduled**: exactly the same as the base case (fsv_scheduled.yaml and hlv_scheduled.yaml)\n", "- **Unscheduled: requests**: the FSV and HLV are called to site when 10 requests that\n", " they can service are logged (fsv_requests.yaml and hlv_requests.yaml)\n", "- **Unscheduled: downtime**: the FSV and HLV are called to site once the wind farm's\n", " operating level hits 90% or lower (fsv_downtime.yaml and hlv_downtime.yaml)\n", " - **Unscheduled: tow-to-port**: the FSV and HLV will be replaced with three identical\n", " tugboats (tugboat1.yaml, tugboat2.yaml, tugboat3.yaml), and all the failures associated\n", " with the FSV and HLV will be changed to capability \"TOW\" to trigger the tow-to-port\n", " repairs. These processes will be triggered on the first request (WOMBAT base\n", " assumption that can't be changed for now).\n", "\n", "In this example, we will demonstrate how the results for the base case for the Dinwoodie, et al. example vary based on how each of the vessels are scheduled. The configuration details all remain the same, regardless of details, except for the strategy information, which is defined as follows:\n", "\n", "This example is set up similarly to that of the validation cases to show how the results differ, and not a step-by-step guide for setting up the analyses. We refer the reader to the extensive [documentation](../API/index.md) and [How To example](how_to.md) for more information.\n", "\n", "## Imports and notebook configuration" ] }, { "cell_type": "code", "execution_count": 1, "id": "1ae6d8cd", "metadata": {}, "outputs": [], "source": [ "from copy import deepcopy\n", "from time import perf_counter\n", "\n", "import pandas as pd\n", "\n", "from wombat.core import Simulation\n", "from wombat.core.library import DINWOODIE\n", "\n", "pd.set_option(\"display.max_rows\", 1000)\n", "pd.set_option(\"display.max_columns\", 1000)\n", "pd.options.display.float_format = \"{:,.2f}\".format" ] }, { "cell_type": "markdown", "id": "11e4345c", "metadata": {}, "source": [ "## Simulation and results setup\n", "\n", "Here we're providing the names of the configuration files (found at: dinwoodie / config)\n", "without their .yaml extensions (added in later) and the results that we want to compare\n", "between simulations to understand some of the timing and cost trade-offs between\n", "simulations.\n", "\n", "The dictionary of keys and lists will be used to create the results data frame where the\n", "keys will be the indices and the lists will be the row values for each of the above\n", "configurations." ] }, { "cell_type": "code", "execution_count": 2, "id": "eb110d66", "metadata": {}, "outputs": [], "source": [ "configs = [\n", " \"base_scheduled\",\n", " \"base_requests\",\n", " \"base_downtime\",\n", " \"base_tow_to_port\",\n", "]\n", "\n", "columns = deepcopy(\n", " configs\n", ") # Create a unique copy of the config names for column naming\n", "results = {\n", " \"availability - time based\": [],\n", " \"availability - production based\": [],\n", " \"capacity factor - net\": [],\n", " \"capacity factor - gross\": [],\n", " \"power production\": [],\n", " \"task completion rate\": [],\n", " \"annual direct O&M cost\": [],\n", " \"annual vessel cost\": [],\n", " \"ctv cost\": [],\n", " \"fsv cost\": [],\n", " \"hlv cost\": [],\n", " \"tow cost\": [],\n", " \"annual repair cost\": [],\n", " \"annual technician cost\": [],\n", " \"ctv utilization\": [],\n", " \"fsv utilization\": [],\n", " \"hlv utilization\": [],\n", " \"tow utilization\": [],\n", "}" ] }, { "cell_type": "markdown", "id": "8a6773c8", "metadata": {}, "source": [ "## Run the simulations and display the results" ] }, { "cell_type": "code", "execution_count": 3, "id": "f1b46ec9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Load Time (min)Run Time (min)
Scenario
base_scheduled0.020.35
base_requests0.020.37
base_downtime0.020.37
base_tow_to_port0.020.36
\n", "
" ], "text/plain": [ " Load Time (min) Run Time (min)\n", "Scenario \n", "base_scheduled 0.02 0.35\n", "base_requests 0.02 0.37\n", "base_downtime 0.02 0.37\n", "base_tow_to_port 0.02 0.36" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "timing_df = pd.DataFrame(\n", " [], columns=[\"Load Time (min)\", \"Run Time (min)\"], index=configs\n", ")\n", "timing_df.index.name = \"Scenario\"\n", "\n", "for config in configs:\n", " # Load the simulation\n", " start = perf_counter()\n", " sim = Simulation(DINWOODIE, f\"{config}.yaml\")\n", " end = perf_counter()\n", " timing_df.loc[config, \"Load Time (min)\"] = (end - start) / 60\n", "\n", " # Run the simulation\n", " start = perf_counter()\n", " sim.run()\n", " end = perf_counter()\n", " timing_df.loc[config, \"Run Time (min)\"] = (end - start) / 60\n", "\n", " # Gather the results of interest\n", " years = sim.metrics.events.year.unique().shape[0]\n", " mil = 1000000\n", "\n", " # Gather the high-level results for the simulation\n", " availability_time = sim.metrics.time_based_availability(\n", " frequency=\"project\", by=\"windfarm\"\n", " )\n", " availability_production = sim.metrics.production_based_availability(\n", " frequency=\"project\", by=\"windfarm\"\n", " )\n", " cf_net = sim.metrics.capacity_factor(\n", " which=\"net\", frequency=\"project\", by=\"windfarm\"\n", " )\n", " cf_gross = sim.metrics.capacity_factor(\n", " which=\"gross\", frequency=\"project\", by=\"windfarm\"\n", " )\n", " power_production = sim.metrics.power_production(frequency=\"project\", by=\"windfarm\")\n", " completion_rate = sim.metrics.task_completion_rate(\n", " which=\"both\", frequency=\"project\"\n", " )\n", " parts = sim.metrics.events[[\"materials_cost\"]].sum().sum()\n", " techs = sim.metrics.project_fixed_costs(\n", " frequency=\"project\", resolution=\"low\"\n", " ).operations[0]\n", " total = sim.metrics.events[[\"total_cost\"]].sum().sum()\n", "\n", " # Gather the equipment costs and separate the results by equipment type\n", " equipment = sim.metrics.equipment_costs(frequency=\"project\", by_equipment=True)\n", " equipment_sum = equipment.sum().sum()\n", " hlv = (\n", " equipment[[el for el in equipment.columns if \"Heavy Lift Vessel\" in el]]\n", " .sum()\n", " .sum()\n", " )\n", " fsv = (\n", " equipment[[el for el in equipment.columns if \"Field Support Vessel\" in el]]\n", " .sum()\n", " .sum()\n", " )\n", " ctv = (\n", " equipment[[el for el in equipment.columns if \"Crew Transfer Vessel\" in el]]\n", " .sum()\n", " .sum()\n", " )\n", " tow = equipment[[el for el in equipment.columns if \"Tugboat\" in el]].sum().sum()\n", "\n", " # Gather the equipment utilization data frame and separate the results by equipment type\n", " utilization = sim.metrics.service_equipment_utilization(frequency=\"project\")\n", " hlv_ur = (\n", " utilization[[el for el in utilization.columns if \"Heavy Lift Vessel\" in el]]\n", " .mean()\n", " .mean()\n", " )\n", " fsv_ur = (\n", " utilization[[el for el in utilization.columns if \"Field Support Vessel\" in el]]\n", " .mean()\n", " .mean()\n", " )\n", " ctv_ur = (\n", " utilization[[el for el in utilization.columns if \"Crew Transfer Vessel\" in el]]\n", " .mean()\n", " .mean()\n", " )\n", " tow_ur = (\n", " utilization[[el for el in utilization.columns if \"Tugboat\" in el]].mean().mean()\n", " )\n", "\n", " # Log the results of interest\n", " results[\"availability - time based\"].append(availability_time.values[0][0])\n", " results[\"availability - production based\"].append(\n", " availability_production.values[0][0]\n", " )\n", " results[\"capacity factor - net\"].append(cf_net.values[0][0])\n", " results[\"capacity factor - gross\"].append(cf_gross.values[0][0])\n", " results[\"power production\"].append(power_production.values[0][0])\n", " results[\"task completion rate\"].append(completion_rate.values[0][0])\n", " results[\"annual direct O&M cost\"].append((total + techs) / mil / years)\n", " results[\"annual vessel cost\"].append(equipment_sum / mil / years)\n", " results[\"ctv cost\"].append(ctv / mil / years)\n", " results[\"fsv cost\"].append(fsv / mil / years)\n", " results[\"hlv cost\"].append(hlv / mil / years)\n", " results[\"tow cost\"].append(tow / mil / years)\n", " results[\"annual repair cost\"].append(parts / mil / years)\n", " results[\"annual technician cost\"].append(techs / mil / years)\n", " results[\"ctv utilization\"].append(ctv_ur)\n", " results[\"fsv utilization\"].append(fsv_ur)\n", " results[\"hlv utilization\"].append(hlv_ur)\n", " results[\"tow utilization\"].append(tow_ur)\n", "\n", " # Clear the logs\n", " sim.env.cleanup_log_files()\n", "\n", "timing_df" ] }, { "cell_type": "code", "execution_count": 4, "id": "32006d25", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
base_scheduledbase_requestsbase_downtimebase_tow_to_port
availability - time based0.970.970.970.97
availability - production based0.970.970.970.97
capacity factor - net0.460.460.460.46
capacity factor - gross0.480.480.480.48
power production9,724.349,725.499,742.929,727.74
task completion rate1.001.000.991.00
annual direct O&M cost18.3816.145.479.58
annual vessel cost12.7510.251.922.02
ctv cost1.921.921.921.92
fsv cost0.270.210.000.00
hlv cost10.568.120.000.00
tow cost0.000.000.000.10
annual repair cost4.044.291.953.97
annual technician cost1.601.601.601.60
ctv utilization1.001.001.001.00
fsv utilization1.000.950.000.00
hlv utilization0.970.960.000.00
tow utilization0.000.000.000.83
\n", "
" ], "text/plain": [ " base_scheduled base_requests base_downtime \\\n", "availability - time based 0.97 0.97 0.97 \n", "availability - production based 0.97 0.97 0.97 \n", "capacity factor - net 0.46 0.46 0.46 \n", "capacity factor - gross 0.48 0.48 0.48 \n", "power production 9,724.34 9,725.49 9,742.92 \n", "task completion rate 1.00 1.00 0.99 \n", "annual direct O&M cost 18.38 16.14 5.47 \n", "annual vessel cost 12.75 10.25 1.92 \n", "ctv cost 1.92 1.92 1.92 \n", "fsv cost 0.27 0.21 0.00 \n", "hlv cost 10.56 8.12 0.00 \n", "tow cost 0.00 0.00 0.00 \n", "annual repair cost 4.04 4.29 1.95 \n", "annual technician cost 1.60 1.60 1.60 \n", "ctv utilization 1.00 1.00 1.00 \n", "fsv utilization 1.00 0.95 0.00 \n", "hlv utilization 0.97 0.96 0.00 \n", "tow utilization 0.00 0.00 0.00 \n", "\n", " base_tow_to_port \n", "availability - time based 0.97 \n", "availability - production based 0.97 \n", "capacity factor - net 0.46 \n", "capacity factor - gross 0.48 \n", "power production 9,727.74 \n", "task completion rate 1.00 \n", "annual direct O&M cost 9.58 \n", "annual vessel cost 2.02 \n", "ctv cost 1.92 \n", "fsv cost 0.00 \n", "hlv cost 0.00 \n", "tow cost 0.10 \n", "annual repair cost 3.97 \n", "annual technician cost 1.60 \n", "ctv utilization 1.00 \n", "fsv utilization 0.00 \n", "hlv utilization 0.00 \n", "tow utilization 0.83 " ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "results_df = pd.DataFrame(\n", " results.values(), columns=columns, index=results.keys()\n", ").fillna(0)\n", "results_df" ] } ], "metadata": { "jupytext": { "formats": "md:myst", "text_representation": { "extension": ".md", "format_name": "myst" } }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.11" }, "source_map": [ 11, 51, 63, 76, 105, 109, 183 ] }, "nbformat": 4, "nbformat_minor": 5 }