{ "cells": [ { "cell_type": "markdown", "id": "b62d241c", "metadata": {}, "source": [ "# How To Use WOMBAT\n", "\n", "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/WISDEM/WOMBAT/main?filepath=examples)\n", "\n", "This tutorial will walk through the setup, running, and results stages of a WOMBAT\n", "simulation while providing background information about how each component is related.\n", "\n", "A Jupyter notebook of this tutorial can be run from `examples/how_to.ipynb` locally, or\n", "through [binder](https://mybinder.org/v2/gh/WISDEM/WOMBAT/main?filepath=examples).\n", "\n", "## Imports\n", "\n", "The following code block demonstrates a typical setup for working with WOMBAT and\n", "running analyses." ] }, { "cell_type": "code", "execution_count": 1, "id": "02964931", "metadata": {}, "outputs": [], "source": [ "from time import perf_counter # timing purposes only\n", "\n", "import numpy as np\n", "import pandas as pd\n", "\n", "from wombat import Simulation, load_yaml\n", "from wombat.core.library import DINWOODIE\n", "\n", "# Seed the random variable for consistently randomized results\n", "np.random.seed(0)\n", "\n", "# Improve the legibility of DataFrames\n", "pd.set_option(\"display.float_format\", \"{:,.2f}\".format)\n", "pd.set_option(\"display.max_rows\", 1000)\n", "pd.set_option(\"display.max_columns\", 1000)" ] }, { "cell_type": "markdown", "id": "432854c7", "metadata": {}, "source": [ "## Defining the Simulation\n", "\n", "The following will demonstrate the required information to run a simulation. For the\n", "purposes of this tutorial, we'll be working with the data under\n", "`library/code_comparison/dinwoodie` in the Github repository, and specifically the base\n", "case.\n", "\n", "One important item to note is that the library structure is enforced within the code so all\n", "data must be placed in the appropriate locations in your analysis' library as follows:\n", "\n", "```{warning}\n", "As of v0.6, the following structure will be adopted to mirror the format of the\n", "[ORBIT library structure](https://github.com/WISDEM/ORBIT/blob/master/ORBIT/core/library.py#L7-L24)\n", "to increase compatibility between similar libraries.\n", "\n", "As of v0.9, the library structure shown below is the only one that will work.\n", "```\n", "\n", "To help users convert to the new structure, the following method is provided to create\n", "the required folder structure for users.\n", "\n", "```{code-block} python\n", "\n", "from wombat import create_library_structure # located in wombat.core.library\n", "\n", "new_library = \"library\"\n", "create_library_structure(new_library)\n", "```\n", "\n", "The above method call will produce the below folder and subfolder structure.\n", "\n", "```\n", "\n", " \u251c\u2500\u2500 project\n", " \u251c\u2500\u2500 config <- Project-level configuration files\n", " \u251c\u2500\u2500 port <- Port configuration files\n", " \u251c\u2500\u2500 plant <- Wind farm layout files\n", " \u251c\u2500\u2500 cables <- Export and Array cable configuration files\n", " \u251c\u2500\u2500 substations <- Substation configuration files\n", " \u251c\u2500\u2500 turbines <- Turbine configuration and power curve files\n", " \u251c\u2500\u2500 electrolyzers <- Electrolyzer configuration files\n", " \u251c\u2500\u2500 vessels <- Land-based and offshore servicing equipment configuration files\n", " \u251c\u2500\u2500 weather <- Weather profiles\n", " \u251c\u2500\u2500 results <- The analysis log files and any saved output data\n", "```\n", "\n", "As a convenience feature you can import the provided validation data libraries as\n", "`DINWOODIE` or `IEA_26` as is seen in the imports above, and a consistent path will be\n", "enabled.\n", "\n", "In practice, any folder location can be used so long as it follows the subfolder\n", "structure provided here.\n", "\n", "### v0.10 Update\n", "\n", "As of v0.10, a single YAML configuration is enabled for all YAML-based configuration\n", "files. The following structure (inputs based on the COREWIND in situ example) should\n", "be adopted for single-input files.\n", "\n", "```{note}\n", "Electrolyzers can also be provided in the same manner as turbines, substations, cables,\n", "and vessels.\n", "```\n", "\n", "```yaml\n", "# WOMBAT/library/corewind/project/config/morro_bay_in_situ_consolidated.yaml\n", "name: COREWIND Morro Bay In Situ\n", "weather: central_ca.csv\n", "service_equipment:\n", "- - ctv\n", " - 7\n", "- cab\n", "- dsv\n", "- ahv\n", "- hlv\n", "layout: morro_bay_9D_layout.csv\n", "port_distance: 60\n", "inflation_rate: 0\n", "workday_start: 6\n", "workday_end: 22\n", "start_year: 2002\n", "end_year: 2021\n", "project_capacity: 1200\n", "substations:\n", " corewind_substation:\n", " ...\n", "cables:\n", " corewind_export:\n", " ...\n", " corewind_array:\n", " ...\n", "turbines:\n", " corewind_15MW:\n", " ...\n", "vessels:\n", " ctv:\n", " ...\n", " cab:\n", " ...\n", " dsv:\n", " ...\n", " ahv:\n", " ...\n", " hlv:\n", " ...\n", "```\n", "\n", "(how_to:configure:layout)=\n", "### Wind Farm Layout\n", "\n", "The wind farm layout is determined by a csv file, `dinwoodie/windfarm/layout.csv` in\n", "this case. Below is a sample of what information is required and how to use each field,\n", "followed by a visual example. It should be noted that none of the headings are\n", "case-sensitive.\n", "\n", "id (required)\n", ": Unique identifier for the asset; no spaces allowed.\n", "\n", "substation_id (required)\n", ": The id field for the substation that the asset connects to; in the case that this is a\n", " substation, then this field should be the same as `id`; no spaces allowed.\n", "\n", "name (required)\n", ": A descriptive name for the turbine, if desired. This can be the same as `id`.\n", "\n", "type (required)\n", ": One of \"turbine\", \"substation\", or \"electrolyzer\". This is required to accurately model\n", " a multi-substation wind farm. The base assumption is that a substation connects to\n", " itself as a means to model the export cable connecting to the interconnection point,\n", " however, this is not always the case, as substations may be connected through their\n", " export systems. Using this filed allows for that connection to be modeled accurately.\n", "\n", "longitude (optional)\n", ": The longitudinal position of the asset, can be in any geospatial reference; optional.\n", "\n", "latitude (optional)\n", ": The latitude position of the asset, can be in any geospatial reference; optional.\n", "\n", "string (required)\n", ": The integer, zero-indexed, string number for where the turbine will be positioned.\n", "\n", "order (required)\n", ": The integer, zero-indexed position on the string for where the turbine will be\n", " positioned.\n", "\n", "distance (optional)\n", ": The distance to the upstream asset; if this is calculated (input = 0), then the\n", " straight line distance is calculated using the provided coordinates (WGS-84 assumed).\n", "\n", "subassembly (required)\n", ": The file that defines the asset's modeling parameters.\n", "\n", "upstream_cable (required)\n", ": The file that defines the upstream cable's modeling parameters.\n", "\n", "upstream_cable_name (optional)\n", ": The descriptive name to give to the cable that will be used during logging. This\n", " enables users to use a single cable definition file while maintaining the naming\n", " conventions used for the wind farm being simulated.\n", "\n", "```{note}\n", "In the example below, there are a few noteworthy caveats that will set the stage for\n", "later input reviews:\n", " - The cables are not modeled, which has a couple of implications\n", " - There only needs to be one string \"0\"\n", " - The cable specifications are required, even if not being modeled (details later)\n", " - longitude, latitude, and distance are all \"0\" because the spatial locations are not used\n", " - subassembly is all \"vestas_v90.yaml\", but having to input the turbine subassembly model\n", " means that multiple turbine types can be used on a windfarm.\n", " - This same logic applies to the upstream_cable so that multiple cable types can be\n", " used as appopriate.\n", "```\n", "\n", "
\n", "\n", "| id | substation_id | name | type | longitude | latitude | string | order | distance | subassembly | upstream_cable |\n", "| :-- | :-- | :-- | :-- | --: | --: | --: | --: | --: | :-- | :-- |\n", "| OSS1 | OSS1 | OSS1 | substation | 0 | 0 | | | | offshore_substation.yaml | export.yaml |\n", "| S00T1 | OSS1 | S00T1 | turbine | 0 | 0 | 0 | 0 | 0 | vestas_v90.yaml | array.yaml |\n", "| S00T2 | OSS1 | S00T2 | turbine | 0 | 0 | 0 | 1 | 0 | vestas_v90.yaml | array.yaml |\n", "| S00T3 | OSS1 | S00T3 | turbine | 0 | 0 | 0 | 2 | 0 | vestas_v90.yaml | array.yaml |\n", "| ... |\n", "| S00T79 | OSS1 | S00T79 | turbine | 0 | 0 | 0 | 78 | 0 | vestas_v90.yaml | array.yaml |\n", "| S00T80 | OSS1 | S00T80 | turbine | 0 | 0 | 0 | 79 | 0 | vestas_v90.yaml | array.yaml |\n", "
\n", "\n", "#### New in v0.10\n", "\n", "v0.10 introduced the ability to define any YAML-based configuration file within the\n", "primary configuration file. To accommodate this change in the above example, each of\n", "`subassembly` and `upstream_cable` inputs would simply drop \".yaml\" from their input,\n", "and the `Simulation` setup will automatically search for `vestas_v90`, `array`, `export`,\n", "or `offshore_substation` in the primary configuration file.\n", "\n", "#### New in v0.11\n", "\n", "v0.11 introduced the electrolyzer model. To include an electrolyzer in the above definition,\n", "simply create a new row where the electrolyzer is connected to substation via an export\n", "cable. In the above example, the row would look like the following. In the case of\n", "multiple substations, attach it to the furthest downstream substation.\n", "\n", "
\n", "\n", "| id | substation_id | name | type | longitude | latitude | string | order | distance | subassembly | upstream_cable |\n", "| :-- | :-- | :-- | :-- | --: | --: | --: | --: | --: | :-- | :-- |\n", "| ELC1 | OSS1 | ELC1 | electrolyzer | 1 | 0 | | | | electrolyzer.yaml | export.yaml |\n", "
\n", "\n", "\n", "(how_to:configure:weather)=\n", "### Weather Profile\n", "\n", "The weather profile will broadly define the simulation range with its start and stop\n", "points, though a narrower one can be used when defining the simulation (more later).\n", "\n", "The following columns should exist in the data set with the provided guidelines.\n", "\n", "datetime (required)\n", ": A date and time stamp, any format.\n", "\n", "windspeed (required)\n", ": The hourly, mean winds peed, in meters per second at the time stamp.\n", "\n", "waveheight (optional)\n", ": The hourly, mean wave height, in meters, at the time stamp. If waves are not required,\n", " this can be filled with zeros, or be left out entirely.\n", "\n", "Below, is a demonstration of what `weather/alpha_ventus_weather_2002_2014.csv` looks like.\n", "\n", "| datetime | windspeed | waveheight |\n", "| :-- | --: | --: |\n", "| 1/1/02 0:00 | 11.75561096 | 1.281772405 |\n", "| 1/1/02 1:00 | 10.41321252 | 1.586584315 |\n", "| 1/1/02 2:00 | 8.959270788 | 1.725690828 |\n", "| 1/1/02 3:00 | 9.10014808 | 1.680982063 |\n", "| ... |\n", "| 12/31/14 22:00 | 14.40838803 | 0.869625003 |\n", "| 12/31/14 23:00 | 14.04563195 | 0.993031445 |\n", "\n", "(how_to:configure:environment)=\n", "### Environmental Considerations\n", "\n", "In addition to using weather conditions for site characteristics, WOMBAT is able to\n", "model environmental considerations where a port or site cannot be accessed, for example,\n", "when silt builds up and water depths become too low to tow a turbine into the port.\n", "There is also a feature for imposing maximum operating speeds, such as when there are\n", "animal migrations and vessels must slow down to avoid collisions with endangered species.\n", "\n", "Defining the `non_operational_start` and `non_operational_end` at either the servicing\n", "equipment, environment, or port level allows for the creation of an annualized date\n", "range spanning the length of the simulation where operations are not allowed to occur.\n", "When defined at the environment level, all servicing equipment and a port, if defined,\n", "will have this non-operational period applied, and if it's already existing, the more\n", "conservative of the date ranges will be applied. When defined at the port level, all\n", "associated servicing equipment (tugboats) will have the same inuring priority as when\n", "defined at the environment level.\n", "\n", "The same logic applies when defining the `reduced_speed_start` and `reduced_speed_end`\n", "for defining when the operating speeds of servicing equipment are capped at the\n", "`reduced_speed`. As is the case above, these variables can also be defined at the\n", "servicing equipment level for further customization.\n", "\n", "(how_to:configure:fixed-costs)=\n", "### Fixed Costs\n", "\n", "Please see the [`FixedCosts` API documentation](types:misc:fixed-costs) for\n", "details on this optional piece of financial modeling.\n", "\n", "For modeling a tow-to-port strategy that the port rental costs should be included in\n", "this category, and not in the port configuration.\n", "\n", "(how_to:configure:servicing-equipment)=\n", "### Servicing Equipment\n", "\n", "The servicing equipment control the actual repairs within a simulation, and as of v0.5,\n", "there are four different repair strategies that can be used: scheduled, downtime-based\n", "unscheduled, repair-based unscheduled, and tow-to-port. These are options are controlled\n", "through the `capability` settings in each equipment's configuration in conjunction with\n", "the `service_equipment` setting in the maintenance and failure configurations for each\n", "subassembly.\n", "\n", "For complete documentation of how the servicing equipment parameters are defined, please\n", "see the [ServiceEquipmentData API documentation](types:service-equipment)\n", "\n", "Below is a definition of the different equipment codes and their designations to show\n", "the breadth of what can be simulated. These codes do not have separate operating models,\n", "but instead allow the user to specify the types of operations the servicing equipment\n", "will be able to operate on. This model should be aligned with the `service_equipment`\n", "requirements in the subassembly failure and maintenance models.\n", "\n", "RMT\n", ": remote (no actual equipment BUT no special implementation), akin to remote resets\n", "\n", "DRN\n", ": drone, or potentially even helicopters by changing the costs\n", "\n", "CTV\n", ": crew transfer vessel/onsite truck\n", "\n", "OFS\n", ": offsite equipment, such a CTV equivalent for servicing a separately located electrolyzer\n", "\n", "SCN\n", ": small crane (i.e., field support vessel or cherry picker)\n", "\n", "MCN\n", ": medium crane (i.e., anything between a cherry picker and a crawler crane)\n", "\n", "LCN\n", ": large crane (i.e., heavy lift vessel or crawler crane)\n", "\n", "CAB\n", ": cabling-specific vessel/vehicle\n", "\n", "DSV\n", ": diving support vessel\n", "\n", "TOW\n", ": tugboat/towing (a failure with this setting will trigger a tow-to-port scenario where\n", "the simulation's `Port` will dispatch the tugboats as needed)\n", "\n", "AHV\n", ": anchor handling vessel (this is a variation on the tugboat for mooring repairs that\n", "will not tow anything between port and site, but operate at the site)\n", "\n", "VSG\n", ": vessel support group (used as a means for modeling a grouping of vessels used for a\n", "sing repair operation)\n", "\n", "Aside from the TOW and AHV capabilities there are no operations specific to each\n", "capability. The remaining configurations of a servicing equipment such as equipment\n", "rates, mobilization, labor, and operating limits will define the nature of its operation\n", "in tandem with a failure's `time` field. So for a remote reset (RMT), there will be a\n", "trivial equipment rate, if any, associated with it to account for the specific operations\n", "or resetting the subassembly remotely. Similarly, a drone repair or inspection (DRN)\n", "will not require any onboard crew, so labor will 0, or low if assuming an operator that\n", "is unaccounted for in the site `FixedCosts`, but will require more time than a remote\n", "reset in addition to a higher equipment cost.\n", "\n", "In addition to a variety of servicing equipment types, there is support for\n", "3 different equipment-level dispatch strategies, as described below. For a set of\n", "example scenarios, please see the [strategy demonstration](strategy_demonstration.md).\n", "\n", "scheduled\n", ": dispatch servicing equipment for a specified date range each year\n", "\n", "requests\n", ": dispatch the servicing equipment once a `strategy_threshold` number of requests\n", " that the equipment can service has been reached\n", "\n", "downtime\n", ": dispatch the servicing equipment once the wind farm's operating level reaches the\n", " `strategy_threshold` percent downtime.\n", "\n", "### The System Models\n", "\n", "The actual assets on the windfarm such as cables, turbines, and substations, are\n", "referred to as systems in WOMBAT, and each has their own individual model. Within each\n", "of these systems, there are user-defined subassemblies (or components) that rely on\n", "two types of repair models:\n", "\n", "- maintenance: scheduled, fixed time interval-based maintenance tasks\n", "- failures: unscheduled, Weibull distribution-based modeled, maintenance tasks\n", "\n", "The subassemblies keys in the system YAML definition can be user-defined\n", "to accommodate the varying language among industry groups and generations of wind\n", "technologies. The only restrictions are the YAML keys must not contain any special\n", "characters, and cannot be repeated\n", "\n", "In the example below we show a `generator` subassembly with an annual service task and\n", "frequent manual reset. For a thorough definition, please read the API\n", "documentation of the [Maintenance](types:maintenance:scheduled) and\n", "[Failure](types:maintenance:unscheduled) data classes. Note that the yaml definition below\n", "specifies that maintenance tasks are in a bulleted list format and that failure\n", "defintions require a dictionary-style input with keys to match the severity level of a\n", "given failure. For more details on the complete subassembly definition, please visit the\n", "[Subassembly API documentation](types:windfarm:subassembly).\n", "\n", "```{important}\n", "As of v0.10, failure configurations (`failures`) require a list-based definition. To\n", "convert older cable, subastation, and turbine configuration failues, use the\n", "library function `convert_failure_data` as shown in the\n", "[helpers API documentation](importing-and-converting-from-old-versions).\n", "```\n", "\n", "```{code-block} yaml\n", "generator:\n", " name: generator # doesn't need to match the subassembly key that becomes System.id\n", " maintenance:\n", " - description: annual service\n", " time: 60\n", " materials: 18500\n", " service_equipment: CTV\n", " frequency: 365\n", " operation_reduction: 0 # default value\n", " failures:\n", " -\n", " scale: 0.1333\n", " shape: 1\n", " time: 3\n", " materials: 0\n", " service_equipment: CTV\n", " operation_reduction: 0.0\n", " replacement: False # default value\n", " level: 1 # Note that the \"level\" value matches the key \"1\"\n", " description: manual reset\n", "```\n", "\n", "#### Substations\n", "\n", "The substation model relies on two specific inputs, and one subassembly input (transformer).\n", "\n", "capacity_kw\n", ": The capacity of all turbines in the wind farm, neglecting any losses. Only needed if\n", "a $/kw cost basis is being used.\n", "\n", "capex_kw\n", ": The $/kw cost of the machine, if not providing absolute costs.\n", "\n", "Additional keys can be added to represent subassemblies, such as a transformer, in the\n", "same format as the generator example above. Similarly, a user can define as man or as\n", "few of the subassemblies as desired with their preferred naming conventions\n", "\n", "The following is an example of substation YAML definition with no modeled subassemblies.\n", "\n", "```{code-block} yaml\n", "capacity_kw: 670000\n", "capex_kw: 140\n", "transformer:\n", " name: transformer\n", " maintenance:\n", " -\n", " description: n/a\n", " time: 0\n", " materials: 0\n", " service_equipment: CTV\n", " frequency: 0\n", " failures:\n", " -\n", " scale: 0\n", " shape: 0\n", " time: 0\n", " materials: 0\n", " service_equipment: [CTV]\n", " operation_reduction: 0\n", " level: 1\n", " description: n/a\n", "```\n", "\n", "#### Turbines\n", "\n", "The turbine has the most to define out of the three wind-based systems in the wind farm\n", "model. Similar to the substation, it relies mainly on the subassembly model with a few\n", "extra parameters, as defined here:\n", "\n", "capacity_kw\n", ": The capacity of the system. Only needed if a $/kw cost basis is being used.\n", "\n", "capex_kw\n", ": The $/kw cost of the machine, if not providing absolute costs.\n", "\n", "power_curve: file\n", ": File that provides the power curve definition.\n", "\n", "power_curve: bin_width\n", "The desired interval, in m/s, between adjacent points on the power curve to be used for\n", "power calculations.\n", "\n", "The `windfarm/vestas_v90.yaml` data file provides the following definition in addition\n", "to the maintenance and failure definitions that were shown previously.\n", "\n", "```{code-block} yaml\n", "capacity_kw: 3000\n", "capex_kw: 1300\n", "power_curve:\n", " file: vestas_v90_power_curve.csv\n", " bin_width: 0.5\n", "```\n", "\n", "The power curve input CSV requires the following two columns: `windspeed_ms` and\n", "`power_kw` that should be defined using the wind speed for a bin, in m/s and the power\n", "produced at that wind speed, in kW. The current method available for generating the\n", "power curve is the IEC 61400-12-1-2 method for a wind-speed binned power curve. If there\n", "is a need/desire for additional power curve methodologies, then\n", "[please submit an issue on the GitHub](https://github.com/WISDEM/WOMBAT/issues)!\n", "\n", "For an open source listing of a variety of land-based, offshore, and distributed wind\n", "turbine power curves, please visit the\n", "[NREL Turbine Models repository](https://github.com/NREL/turbine-models).\n", "\n", "#### Cables\n", "\n", "The array cable is the simplest format in that you only define a descriptive name,\n", "and the maintenance and failure events as below. It should be noted that the scheme\n", "is a combination of both the system and subassembly configuration.\n", "\n", "For export cables that connect substations, as opposed to an interconnection, they will\n", "not create dependencies because it is assumed they bypass the substation altogether,\n", "and connect directly with the export system. This means that if there is a failure at a\n", "downstream substation, the connecting export cable and its upstream turbine, substation,\n", "and cable connections will continue to operate normally.\n", "\n", "```{code-block} yaml\n", "name: array cable\n", "maintenance:\n", " -\n", " description: n/a\n", " time: 0\n", " materials: 0\n", " service_equipment: CTV\n", " frequency: 0\n", "failures:\n", " -\n", " scale: 0\n", " shape: 0\n", " time: 0\n", " materials: 0\n", " operation_reduction: 0\n", " service_equipment: CAB\n", " level: 1\n", " description: n/a\n", "```\n", "\n", "#### Electrolyzers\n", "\n", "New as of v0.11!\n", "\n", "The electrolyzer has the most to define out of all the systems. Similar to the turbine,\n", "it relies mainly on the subassembly model with a few extra parameters, as defined here:\n", "\n", "stack_capacity_kw\n", ": The capacity of each stack, required.\n", "\n", "n_stacks\n", ": The number of stacks in the electrolyzer, required.\n", "\n", "capex_kw\n", ": The $/kw cost of the machine, if not providing absolute costs.\n", "\n", "power_curve: p1, p2, p3, p4, and p5\n", ": Coefficients for the polynomial to convert the stack's input power to current. Either\n", " the polynomial inputs here, or the `efficiency_rate` should be provided, but not both.\n", "\n", "power_curve: efficiency_rate\n", ": The linear efficiency rate that the electrolyzer can convert energy into hydrogen, in\n", " kWh/kg. Do not use in conjunction with the above `p1` through `p5` parameters.\n", "\n", "power_curve: FE\n", ": Faradic efficiency, defaults to 0.9999999.\n", "\n", "power_curve: n_cells\n", ": Number of cells in each stack to convert energy to H2.\n", "\n", "power_curve: turndown_ratio\n", ": The minimum operating ratio for input power. For example, at 0.1 (10%), a 100 MW\n", "electrolyzer will not operate unless input power is at least 10 MW.\n", "\n", "An example data file provides the following definition in addition to the maintenance\n", "and failure definitions that were shown previously. The assumptions are based on the\n", "[H2 Integrate PEM electrolysis model](https://github.com/NREL/H2Integrate/blob/main/h2integrate/simulation/technologies/hydrogen/electrolysis/PEM_H2_LT_electrolyzer_Clusters.py)\n", "with CapEx and number of stacks being arbitrarily chosen for example purposes.\n", "\n", "```{code-block} yaml\n", "stack_capacity_kw: 1000\n", "capex_kw: 800\n", "n_stacks: 120\n", "power_curve:\n", " p1: 4.0519644766515644e-08\n", " p2: -0.00026186723338675105\n", " p3: 3.8985774154190334\n", " p4: 7.615382921418666\n", " p5: -20.075110413404484\n", " FE: 0.9999999\n", " n_cells: 135\n", " turndown_ratio: 0.1\n", "```\n", "\n", "The power curve input requires the efficiency polynomial curve fit coefficients, though\n", "it is highly recommended to use those included in the above example. For implementation\n", "details, please see the [utilities API documentation](https://wisdem.github.io/WOMBAT/API/utilities.html#wombat.utilities.utilities)\n", "\n", "If there is a need/desire for additional power curve methodologies, then\n", "[please submit an issue on the GitHub](https://github.com/WISDEM/WOMBAT/issues)!\n", "\n", "## Set Up the Simulation\n", "\n", "In the remaining sections of the tutorial, we will work towards setting up and running\n", "a simulation.\n", "\n", "### Define the data library path\n", "\n", "The set library enables WOMBAT to easily access and store data files consistently. Here,\n", "the `DINWOODIE` reference is going to be used again.\n", "\n", "```{note}\n", "If a custom library is being used, the `library_path` must be the full path name to the\n", "location of the folder where the configuration data is contained.\n", "```" ] }, { "cell_type": "code", "execution_count": 2, "id": "aa57a388", "metadata": {}, "outputs": [], "source": [ "library_path = DINWOODIE # or user-defined path for an external data library" ] }, { "cell_type": "markdown", "id": "3647d3f2", "metadata": {}, "source": [ "### The configuration file\n", "\n", "In the configuration below, there are a number of data points that will define our\n", "wind farm layout, weather conditions, working hours, customized start and completion\n", "years, project size, financials, and the servicing equipment to be used. Note that there\n", "can be as many or as few of the servicing equipment units as desired.\n", "\n", "The purpose of an overarching configuration file is to provide a single place to define\n", "the primary inputs for a simulation. Below the base configuration is loaded and displayed\n", "with comments to show where each of files are located in the library structure. WOMBAT\n", "will know where to go for these pointers when the simulation is initialized so the data\n", "is constructed and validated correctly.\n", "\n", "```{note}\n", "As of veraion 0.10, all non-CSV file inputs can be difined in a single configuration\n", "file. Please see [the configuration API details](simulation-api:config) for details.\n", "```" ] }, { "cell_type": "code", "execution_count": 3, "id": "83b6a40e", "metadata": {}, "outputs": [], "source": [ "config = load_yaml(library_path / \"project/config\", \"base.yaml\")" ] }, { "cell_type": "markdown", "id": "f0dd2971", "metadata": {}, "source": [ "```{code-block} yaml\n", "# Contents of: dinwoodie / config / base.yaml\n", "name: dinwoodie_base\n", "weather: alpha_ventus_weather_2002_2014.csv # located in: dinwoodie / weather\n", "service_equipment:\n", "# YAML-encoded list, but could also be created in standard Python list notation with\n", "# square brackets: [ctv1.yaml, ctv2.yaml, ..., hlv_requests.yaml]\n", "# All below equipment configurations are located in: dinwoodie / vessels\n", " - ctv1.yaml\n", " - ctv2.yaml\n", " - ctv3.yaml\n", " - fsv_requests.yaml\n", " - hlv_requests.yaml\n", "layout: layout.csv # located in: dinwoodie / windfarm\n", "inflation_rate: 0\n", "fixed_costs: fixed_costs.yaml # located in: dinwoodie / project / config\n", "workday_start: 7\n", "workday_end: 19\n", "start_year: 2003\n", "end_year: 2012\n", "project_capacity: 240\n", "# port: base_port.yaml <- When running a tow-to-port simulation the port configuration\n", "# pointer is provided here and located in: dinwoodie / project / port\n", "```\n", "\n", "```{note}\n", "As of v0.10, multiple vessels can be modeled with a single configuration, and vessel,\n", "turbine, cable, and substation configurations can be included directly in the contents\n", "of the primary configuration.\n", "```\n", "\n", "Alternatively, the configuration can be simplified to be of the following form to utilize\n", "repeated vessel configurations and the single file configuration. The new keys for where\n", "to embed the data directly correspond to the folder names where the files originally\n", "resided. For instance, below embedding `/vessels/ctv.yaml` into the\n", "main configuration means that we need a new key called `vessels`, with a subkey\n", "underneath called `ctv`.\n", "\n", "```{code-block} yaml\n", "name: dinwoodie_base\n", "weather: alpha_ventus_weather_2002_2014.csv # located in: dinwoodie / weather\n", "service_equipment:\n", "# YAML-encoded list, but could also be created in standard Python list notation with\n", "# square brackets: [ctv1.yaml, ctv2.yaml, ..., hlv_requests.yaml]\n", "# All below equipment configurations are located in: dinwoodie / vessels\n", " - [ctv, 3]\n", " - fsv_requests.yaml\n", " - hlv_requests.yaml\n", "layout: layout.csv # located in: dinwoodie / windfarm\n", "inflation_rate: 0\n", "fixed_costs: fixed_costs.yaml # located in: dinwoodie / project / config\n", "workday_start: 7\n", "workday_end: 19\n", "start_year: 2003\n", "end_year: 2012\n", "project_capacity: 240\n", "vessels:\n", " ctv:\n", " ... # contents of ctv1.yaml without the numbered naming which is created automatically\n", "turbines:\n", " ...\n", "cables:\n", " ...\n", "substations:\n", " ...\n", "```\n", "\n", "For a complete example of this, please see the\n", "`library/corewind/project/config/morro_bay_in_situ_consolidated.yaml` configuration\n", "file.\n", "\n", "## Create a simulation\n", "\n", "There are two ways that this could be done, the first is to use the classmethod\n", "`Simulation.from_config()`, which allows for the full path string, a dictionary, or\n", "`Configuration` object to passed as an input, and the second is through a standard\n", "class initialization.\n", "\n", "### Option 1: `Simulation.from_config()`\n", "\n", "Load the file from the `Configuration` object that was created in the prior code black" ] }, { "cell_type": "code", "execution_count": 4, "id": "33d8ea57", "metadata": {}, "outputs": [], "source": [ "sim = Simulation.from_config(library_path=library_path, config=config)\n", "\n", "# Delete any files that get initialized through the simulation environment\n", "sim.env.cleanup_log_files()" ] }, { "cell_type": "markdown", "id": "023b7810", "metadata": {}, "source": [ "```{important}\n", "This option's workflow ends here, do not call `sim.run()` after calling the cleanup\n", "method. Note that a new simulation is loaded in the next example (Option 2).\n", "```\n", "\n", "### Option 2: `Simulation()`\n", "\n", "Load the configuration file automatically given a library path and configuration file name.\n", "\n", "In this usage, the string \"DINWOODIE\" can be used because the `Simulation` class knows\n", "to look for this library mapping, as well as the \"IEA_26\" mapping for the two validation\n", "cases that we demonstrate in the examples folder.\n", "\n", "```{note}\n", "In Option 2, the config parameter can also be set with a dictionary.\n", "\n", "The library path in the configuration file should match the one provided, or the\n", "setup steps will fail in the simulation.\n", "```" ] }, { "cell_type": "code", "execution_count": 5, "id": "aaa4812f", "metadata": {}, "outputs": [], "source": [ "sim = Simulation(\n", " library_path=\"DINWOODIE\", # automatically directs to the provided library\n", " config=\"base.yaml\",\n", ")\n", "sim.env.cleanup_log_files() # Ends the current demonstration" ] }, { "cell_type": "markdown", "id": "9567fed4", "metadata": {}, "source": [ "### Seeding the simulation random variable\n", "\n", "Using `random_seed` a simulation can be seeded to produce the same results every single\n", "time, or a `random_generator` can be provided to use the same generator for a batch of\n", "identical simulations to better understand the variations in results." ] }, { "cell_type": "code", "execution_count": 6, "id": "019df99b", "metadata": {}, "outputs": [], "source": [ "sim = Simulation(\n", " library_path=\"DINWOODIE\", # automatically directs to the provided library\n", " config=\"base.yaml\",\n", " random_seed=2023, # integer value indicating how to seed the internally-created generator\n", ")\n", "sim.env.cleanup_log_files() # Ends the current demonstration" ] }, { "cell_type": "code", "execution_count": 7, "id": "fc21d990", "metadata": {}, "outputs": [], "source": [ "rng = np.random.default_rng(seed=2023) # create the generator\n", "sim = Simulation(\n", " library_path=\"DINWOODIE\", # automatically directs to the provided library\n", " config=\"base.yaml\",\n", " random_generator=rng, # generator that can be shared among all processes\n", ")" ] }, { "cell_type": "markdown", "id": "72b905cd", "metadata": {}, "source": [ "## Run the analysis\n", "\n", "When the run method is called, the default run time is for the full length of the\n", "simulation, however, if a shorter run than was previously designed is required for\n", "debugging, or something similar, we can use `sum.run(until=)` to do this. In\n", "the `run` method, not only is the simulation run, but the metrics class is loaded at the\n", "end to quickly transition to results aggregation without any further code." ] }, { "cell_type": "code", "execution_count": 8, "id": "bc29c31a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Run time: 0.37 minutes\n" ] } ], "source": [ "# Timing for a demonstration of performance\n", "start = perf_counter()\n", "\n", "sim.run(delete_logs=True, save_metrics_inputs=False)\n", "\n", "end = perf_counter()\n", "\n", "timing = end - start\n", "print(f\"Run time: {timing / 60:,.2f} minutes\")" ] }, { "cell_type": "markdown", "id": "62e81a17", "metadata": {}, "source": [ "### Optional: Delete the logging files\n", "\n", "In the case that a lot of simulations are going to be run, and the processed outputs\n", "are all that is required, then there is a convenience method to clean up these files\n", "automatically once you are done, if not using the `delete_logs=True` parameterization\n", "shown above." ] }, { "cell_type": "code", "execution_count": 9, "id": "df17c66e", "metadata": {}, "outputs": [], "source": [ "sim.env.cleanup_log_files()" ] }, { "cell_type": "markdown", "id": "519547d6", "metadata": {}, "source": [ "For additional convenience, in `Simulation.run()`, `save_metrics_inputs=True` and\n", "`delete_logs=False` can be added to skip a secondary cleanup step. This is particularly\n", "useful when running a series of simulations where the log files will not be needed\n", "afterward.\n", "\n", "## Metric computation\n", "\n", "For a more complete view of what metrics can be compiled, please see the [metrics notebook](metrics_demonstration.md), though for the sake of demonstration a few methods will\n", "be shown here" ] }, { "cell_type": "code", "execution_count": 10, "id": "8b7b0cbc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Net Capacity Factor: 46.2%\n", "Gross Capacity Factor: 47.7%\n" ] } ], "source": [ "net_cf = sim.metrics.capacity_factor(\n", " which=\"net\", frequency=\"project\", by=\"windfarm\"\n", ").squeeze()\n", "gross_cf = sim.metrics.capacity_factor(\n", " which=\"gross\", frequency=\"project\", by=\"windfarm\"\n", ").squeeze()\n", "print(f\" Net Capacity Factor: {net_cf:2.1%}\")\n", "print(f\"Gross Capacity Factor: {gross_cf:2.1%}\")" ] }, { "cell_type": "code", "execution_count": 11, "id": "f7db2962", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Project time-based availability: 97.0%\n", "Project energy-based availability: 97.0%\n", " Project equipment costs: $453,518.85/MW\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Project energy-based availability: 97.0%\n", " Project equipment costs: $453,518.85/MW\n" ] } ], "source": [ "# Report back a subset of the metrics\n", "total = sim.metrics.time_based_availability(frequency=\"project\", by=\"windfarm\")\n", "print(f\" Project time-based availability: {total.windfarm.squeeze():.1%}\")\n", "\n", "total = sim.metrics.production_based_availability(frequency=\"project\", by=\"windfarm\")\n", "print(f\"Project energy-based availability: {total.windfarm.squeeze():.1%}\")\n", "\n", "total = sim.metrics.equipment_costs(frequency=\"project\", by_equipment=False)\n", "print(\n", " f\" Project equipment costs: ${total.values.squeeze() / sim.metrics.project_capacity:,.2f}/MW\"\n", ")" ] } ], "metadata": { "jupytext": { "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": [ 10, 27, 43, 644, 646, 666, 668, 752, 758, 780, 786, 794, 803, 810, 820, 830, 839, 841, 853, 860 ] }, "nbformat": 4, "nbformat_minor": 5 }