.. _sec-ModelingComponents: Overview of Capital Budgeting Modeling Components ================================================= We consider a capital budgeting problem for a nuclear generation station, with possible extension to a larger fleet of plants. Due to limited resources, we can only select a subset from a number of candidate investment projects. Our goal is to maximize overall net present value (NPV), or a variant of this objective when we incorporate uncertainty into project cost and revenue streams. In doing so, we must respect resource limits and capture key structural and stochastic dependencies of the system. Example projects include upgrading a steam turbine, refurbishing or replacing a set of reactor coolant pumps, and replacing a set of feed-water heaters. Selecting an individual project has multiple facets and implications. - **Rewards or Net Present Values**: Selecting a project can improve revenue (e.g., upgrading a steam turbine may lead to an uprate in plant capacity resulting in larger revenue from selling power). Replacing a key system component can improve reliability, increasing revenue due to a reduction in forced outages as well as operations and maintenance (O\&M) costs. Choosing to perform minimum maintenance versus refurbishing a component or replacing and improving a system can produce reward streams that can be negative or positive depending on the selection. The parameter :xmlNode:`net_present_values` is used to specify the rewards (see Section :ref:`subsec-Parameters`). - **Resources and Liabilities**: Critical resources include: 1. capital costs, 2. O\&M costs, 3. time and labor-hours during a planned outage, and 4. personnel, installation and maintenance equipment, workspaces, etc. Within these categories, resources can be further sub-categorized (each subcategory having its own budget) according to the plant’s organizational structure to provide multiple “colors” of money within capital costs, O\&M costs, personnel availability, etc. The set :xmlNode:`resources` and the parameter :xmlNode:`available_capitals` are used to specify the resources and liabilities (see Sections :ref:`subsec-Sets` and :ref:`subsec-Parameters`). - **Costs**: Selecting a project in year :math:`t` induces multiple cost streams in year :math:`t` as well as in subsequent years; we interpret “cost” broadly to include commitment of critical resources. The parameter :xmlNode:`costs` is used to specify the costs (see Section :ref:`subsec-Parameters`). - **Time Periods**: Multiple capital projects can compete for the same time period, limiting project selection. The set :xmlNode:`time_periods` is used to provide indices for **costs** and **available capitals** (see Section :ref:`subsec-Sets`). - **Options**: The goal of selecting a project is typically to improve or maintain a particular function the plant performs, and there may be multiple ways to carry out the task. A project may be performed over a three-year period—say, years “:math:`t, t+1, t+2`”—or the start of the project could instead be two years hence, changing the period to “:math:`t+2, t+3, t+4`”. Alternatively, at increased cost and benefit, it may be possible to complete the project in two years: “:math:`t, t+1`” or “:math:`t+2, t+3`”. When selecting a project to uprate plant capacity, we may have the options of increasing it by 3\% or 6\%. In all these cases, we can perform the project in at most one particular way out of a collection of options. We represent this by cloning a “project” into multiple project–option pairs, and adding a constraint saying that we can select at most one from this set of options. The set :xmlNode:`options` is used to provide indices for these multiple project–option pairs (see Section :ref:`subsec-Sets`). - **Capitals**: If we consider maintenance for multiple units in an NPP in parallel, it must be decided whether to accept a particular replacement and, if so, in which unit to conduct the corresponding replacement. In this case, the set :xmlNode:`capitals` is used to provide indices for these units (see Section :ref:`subsec-Sets`). - **Available Capitals**: These are the budgets available for resources/units. The parameter :xmlNode:`available_capitals` is used to specify the available capital for different resources/units at different :math:`t` (see Section :ref:`subsec-Parameters`). - **Non-Selection**: Not selecting a project also has implications, such as a growth in O\&M costs in future years, a decrease in plant production, an increase in forced outages, and even risking a premature end to plant life. Thus, not selecting a project can be seen as one more “option” for how a larger project is executed, expanding the list discussed earlier. Selection of the “do nothing” option is reflected in both liability streams and reward streams. This can be activated by setting :xmlNode:`nonSelection` to :xmlString:`True` (see Section :ref:`subsec-Settings`). - **Uncertainty**: One limitation of traditional optimization models for capital budgeting is that they do not account for uncertainty in reward and cost streams associated with individual projects, nor do they account for uncertainty in resource availability in future years. Projects can incur cost overruns, especially when projects are large, performed infrequently, or when there is uncertainty regarding technical viability, external contractors, and/or suppliers of requisite parts and materials. Occasionally, projects are performed ahead of schedule and with savings in cost. Planned budgets for capital improvements can be cut, and key personnel may be lost. Or, there may be surprise budgetary windfalls for maintenance activities due to decreased costs for “unplanned” maintenance. The XML node :xmlNode:`Uncertainties` is used to specify such uncertainties (see Section :ref:`subsec-Uncertainties`). LOGOS consists of a collection of modeling entities/components that define different aspects of the model, including :xmlNode:`Sets`, :xmlNode:`Parameters`, :xmlNode:`Uncertainties`, and :xmlNode:`ExternalConstraints`. In addition, the :xmlNode:`Settings` block specifies how the overall computation should run. .. _subsec-Sets: Sets ---- This subsection describes the XML nodes used to define the :xmlNode:`Sets` of the optimization model being performed through LOGOS. :xmlNode:`Sets` specifies collections of data, possibly including numeric data (e.g., real or integer values) as well as symbolic data (e.g., strings), typically used to specify the valid indices for indexed components. .. note:: Numeric data provided in :xmlNode:`Sets` is treated as strings. :xmlNode:`Sets` accepts the following sub-nodes: - :xmlNode:`investments`, comma/space-separated string, **required** Specifies the valid indices for investment projects. - :xmlNode:`capitals`, comma/space-separated string, *optional* Specifies the indices for NPP units. - :xmlNode:`time_periods`, comma/space-separated string, *optional* Specifies the indices for time. - :xmlNode:`resources`, comma/space-separated string, *optional* Specifies indices for the resources and liabilities. - :xmlNode:`options`, semi-colon-separated list of strings, *optional* Specifies the indices for multiple project–option pairs. This sub-node accepts the following attribute: - :xmlAttr:`index`, string, **required** Specifies the index dependence. The valid index is :xmlString:`investments`. Example XML: .. code-block:: xml HPFeedwaterHeaterUpgrade PresurizerReplacement ... ReplaceMoistureSeparatorReheater year1 year2 year3 year4 year5 CapitalFunds OandMFunds PlanA PlanB DoNothing; PlanA PlanB PlanC; ... PlanA PlanB PlanC DoNothing .. _subsec-Parameters: Parameters ---------- This subsection describes the XML nodes used to define the :xmlNode:`Parameters` of the optimization model being performed through LOGOS: - :xmlNode:`net_present_values`, comma/space-separated string, **required** Specifies the NPVs for capital projects or project–option pairs. Optional attribute: - :xmlAttr:`index`, comma-separated string, *optional* Specifies the indices of this parameter; keywords should be predefined in :xmlNode:`Sets`. Valid keywords are :xmlString:`investments` and :xmlString:`options`. **Default**: :xmlString:`investments`. - :xmlNode:`costs`, comma/space-separated string, **required** Specifies the costs for capital projects or project–option pairs. Optional attribute: - :xmlAttr:`index`, comma-separated string, *optional* Specifies the indices of this parameter; keywords should be predefined in :xmlNode:`Sets`. Valid keywords are: * :xmlString:`investments` * :xmlString:`investments, time_periods` * :xmlString:`options` * :xmlString:`options, resources` * :xmlString:`options, time_periods` * :xmlString:`options, resources, time_periods` **Default**: :xmlString:`investments`. - :xmlNode:`available_capitals`, comma/space-separated string, **required** Specifies the available capitals for capital projects or project–option pairs. Optional attribute: - :xmlAttr:`index`, comma-separated string, *optional* Specifies the indices of this parameter; keywords should be predefined in :xmlNode:`Sets`. Valid keywords are: * :xmlString:`resources` * :xmlString:`time_periods` * :xmlString:`capitals` * :xmlString:`resources, time_periods` * :xmlString:`capitals, time_periods` **Default**: None. Example XML: .. code-block:: xml 27.98 27.17 0. -10.07 -9.78 -9.22 ... 8.26 7.56 7.34 0. 12.99 1.3 0 0 0 ... 0.01 0 0 0 0 22.6 36.7 20.6 23.6 22.7 0.08 0.17 0.05 0.15 0.14 .. _subsec-Uncertainties: Uncertainties ------------- This subsection describes the XML nodes used to define the :xmlNode:`Uncertainties` of the optimization model being performed through LOGOS: - :xmlNode:`available_capitals`, *optional* Specifies the scenarios associated with available capitals. This node accepts the attribute :xmlAttr:`index`, which should be consistent with the :xmlNode:`available_capitals` defined in :xmlNode:`Parameters`. It accepts the following sub-nodes: - :xmlNode:`totalScenarios`, integer, **required** Specifies the total number of scenarios for this parameter. - :xmlNode:`probabilities`, comma/space-separated float, **required** Specifies the probability for each scenario. The length must equal the total number of scenarios. - :xmlNode:`scenarios`, comma/space-separated float, **required** Specifies all scenarios for this parameter. The length must equal (total number of scenarios) × (length of the parameter as defined in :xmlNode:`Parameters`). - :xmlNode:`net_present_values`, *optional* Specifies the scenarios associated with :xmlNode:`net_present_values`. This node accepts the attribute :xmlAttr:`index`, which should be consistent with :xmlNode:`net_present_values` as defined in :xmlNode:`Parameters`. It accepts the following sub-nodes: - :xmlNode:`totalScenarios`, integer, **required** - :xmlNode:`probabilities`, comma/space-separated float, **required** - :xmlNode:`scenarios`, comma/space-separated float, **required** The overall number of scenarios is the total number of scenarios for :xmlNode:`available_capitals` multiplied by the total number of scenarios for :xmlNode:`net_present_values`. Example XML: .. code-block:: xml 10 0.5, 0.5 20.0 34.0 17.0 20.0 18.0 0.08 0.17 0.05 0.15 0.14 23.0 38.0 22.0 25.0 24.0 0.08 0.17 0.05 0.15 0.14 9 0.3 0.8 13.3129 12.0228 0.0 -10.07 ... External Constraints -------------------- .. _subsec-ExternalConstraints: This subsection describes the XML nodes used to define the :xmlNode:`ExternalConstraints` of the optimization model being performed through LOGOS. This node accepts the following sub-node(s): - :xmlNode:`constraint`, string, **required** Specifies the external Python module file name along with its absolute or relative path. This external Python module contains the user-defined additional constraint. .. note:: If a relative path is specified, the code first checks relative to the working directory, then relative to the location of the input file. The working directory can be specified in :xmlNode:`Settings` (see Section :ref:`subsec-Settings`). The extension ``.py`` is optional for the module file name. This sub-node also requires the following attribute: - :xmlAttr:`name`, string, **required** Specifies the name of the constraint that will be added to the optimization problem. Example XML: .. code-block:: xml externalConst externalConstII.py These constraints are Python modules with a format automatically interpretable by LOGOS. Users can define their own constraint, and LOGOS will embed and use the constraint as though it were an internal part of the optimization model. Example Python function: .. code-block:: python # External constraint function import numpy as np import pyomo.environ as pyomo def initialize(): """ Optional method. Optimization model parameter values can be updated/modified without directly accessing the optimization model. Values will be updated in-place. Returns ------- updateDict : dict Dictionary of the form {paramName: paramInfoDict}, where paramInfoDict contains {Indices: Values}. Indices are parameter indices (either strings or tuples of strings, depending on the parameter's dimensionality). Values are the new values being assigned. """ updateDict = { "available_capitals": {"None": 16}, "costs": { "1": 1, "2": 3, "3": 7, "4": 4, "5": 8, "6": 9, "7": 6, "8": 10, "9": 2, "10": 5, }, } return updateDict def constraint(var, sets, params): """ Required method. External constraint provided by the user that will be added to the optimization problem. Parameters ---------- var : object The internally used decision variable. The dimensions/indices depend on the type of optimization problem (i.e., the ```` from the LOGOS input file). Currently supported types: 1. "singleknapsack": var[:] indexed by the elements of the ``investments`` set. 2. "multipleknapsack": var[:,:] indexed by combinations of ``investments`` and ``capitals``. 3. "mckp": var[:,:] indexed by combinations of ``investments`` and ``options``. Note: all index elements are converted to strings. sets : dict All sets from the LOGOS input file, stored as {setName: setObject}. params : dict All parameters from the LOGOS input file, stored as {paramName: paramObject}. Returns ------- tuple Either (constraintRule,) or (constraintRule, indices). Notes ----- Any modifications to "sets" and "params" in this function have impact only locally within this module. Internal constraints and objectives in LOGOS use the original versions. """ investments = sets["investments"] def constraintRule(self, i): """ Expression for the user-provided external constraint. Parameters ---------- self : object Required by Pyomo, but not used here. i : str Element from the index set. Returns ------- expression A Pyomo expression defining the constraint for index i. """ # Example: place an upper bound on the i-th decision variable return var[i] <= 1 # Return the rule and the indexing set for iterative construction return (constraintRule, investments) .. _subsec-Settings: Settings: Options for Optimization ---------------------------------- This subsection describes the XML nodes used to define the :xmlNode:`Settings` of the optimization model being performed through LOGOS: - :xmlNode:`problem_type`, string, **required** Specifies the type of optimization problem. Available types include: * :xmlString:`SingleKnapsack`, * :xmlString:`MultipleKnapsack`, * :xmlString:`MCKP` for risk-informed stochastic optimization; * :xmlString:`droskp`, :xmlString:`dromkp`, :xmlString:`dromckp` for distributionally robust optimization; * :xmlString:`cvarskp`, :xmlString:`cvarmkp`, :xmlString:`cvarmckp` for CVaR-based optimization. - :xmlNode:`solver`, string, *optional* Specifies the solver, e.g.: * :xmlString:`cbc` from ``https://github.com/coin-or/Cbc.git`` * :xmlString:`glpk` from ``https://www.gnu.org/software/glpk/`` - :xmlNode:`sense`, string, *optional* Specifies :xmlString:`minimize` or :xmlString:`maximize` for minimization or maximization, respectively. **Default**: :xmlString:`minimize`. - :xmlNode:`mandatory`, comma/space-separated string, *optional* Specifies regulatorily mandated or must-do projects. - :xmlNode:`nonSelection`, boolean, *optional* Indicates whether the investment options include a *DoNothing* option. **Default**: :xmlString:`False`. - :xmlNode:`lowerBounds`, comma/space-separated integers, *optional* Specifies the lower bounds for decision variables. - :xmlNode:`upperBounds`, comma/space-separated integers, *optional* Specifies the upper bounds for decision variables. - :xmlNode:`consistentConstraintI`, string, *optional* Indicates whether this constraint is enabled. **Default**: :xmlString:`True`. - :xmlNode:`consistentConstraintII`, string, *optional* Indicates whether this constraint is enabled. **Default**: :xmlString:`False`. - :xmlNode:`solverOptions`, *optional* Accepts additional options for the solver provided in :xmlNode:`solver`. A simple XML node containing only tags and text can be used to pass options, for example: .. code-block:: xml 1 EF If the problem type is distributionally robust optimization, an additional option :xmlNode:`radius_ambiguity` can be used to control the Wasserstein distance .. (see Section :ref:`sec-DROCapitalBudgeting`). If the problem type uses conditional value-at-risk, additional options are available: .. (see Section :ref:`sec-CVaR`) - :xmlNode:`risk_aversion`, float in :math:`[0,1]`, *optional* Indicates the weight on maximizing expected NPV versus penalizing solutions that yield low-NPV scenarios. - :xmlNode:`confidence_level`, float in :math:`[0,1]`, *optional* Indicates the confidence level, i.e., the :math:`\alpha`-percentile of the loss. Example XML: .. code-block:: xml PresurizerReplacement ... ReplaceInstrumentationAndControlCables True True True cbc 1 EF maximize mckp