Constraint Macros
MacroEnergy.@add_balance — Macro
@add_balance(component, balance_id, equation)A macro to add a balance definition to a Node, Transformation, or Storage.
@add_balance is the primary balance-definition interface for asset modelers. It supports equality and inequality balances with flow(...) terms and scalar or time-varying coefficients.
@add_balance lets a modeler write a named flow relationship in ordinary algebraic form and attach it to a vertex. Macro stores that relationship as normalized balance data, which BalanceConstraint later enforces at each time step.
Arguments
component: The component object to add balance data tobalance_id: Symbol key for the balance equation (e.g.,:energy,:mass)equation: Balance equation with operators:==,=,<=,<,>=,>
Supported terms
flow(edge)
Coefficients
Coefficients may be:
- a scalar number
- a length-1 vector
- a vector with one coefficient per time step of the host vertex
Coefficients should be written before flow(edge), for example heat_rate * flow(edge).
@add_balance expects linear terms. Expressions such as c / flow(edge) are nonlinear because they divide by a decision variable. Write every flow term as a coefficient multiplied by a flow, such as (1 / efficiency) * flow(edge).
Examples
@add_balance(transform, :energy, flow(elec_edge) == 0.5 * flow(h2_edge))
@add_balance(transform, :energy_lb, flow(elec_edge) >= eff * flow(h2_edge))Method
@add_balance parses the equation, moves all terms onto the left-hand side, records the constraint sense (:eq, :le, or :ge), and converts each flow(...) term into a BalanceTerm. For flow terms, the stored coefficient is pre-adjusted using the edge incidence at the host vertex so that the compiled balance reproduces the algebraic equation the user wrote.
MacroEnergy.@add_stoichiometric_balance — Macro
@add_stoichiometric_balance(component, balance_id, equation, base_term)Expand a stoichiometric-style balance written with the --> operator into multiple pairwise balances anchored on base_term.
This macro is a convenience wrapper for recipe-style or chemical-style relationships. For general balances, @add_balance is the preferred interface.
@add_stoichiometric_balance lets a modeler describe a recipe using incoming_terms --> outgoing_terms, then expands that shorthand into one or more ordinary balances that Macro can store and enforce.
Example
@add_stoichiometric_balance(
electrolyzer,
:energy,
flow(elec_edge) --> efficiency * flow(h2_edge),
flow(h2_edge),
)Method
The macro parses the left and right sides of --> into weighted flow(...) terms, validates that left-hand terms are incoming and right-hand terms are outgoing relative to the host component, identifies the coefficient on base_term, and generates one pairwise @add_balance call for each remaining term using the proportional rule base_coeff * flow(term) - term_coeff * flow(base_term) == 0. This keeps the recipe syntax compact while delegating final balance construction to @add_balance.
MacroEnergy.@add_to_balance — Macro
@add_to_balance(component, balance_id, expression)Add one or more terms to an existing named balance without writing an explicit constraint sense. This stores the provided expression as a raw additive contribution to balance_id.
Modelers should usually write positive coefficients for both incoming and outgoing flow(...) terms. MacroEnergy applies edge-direction handling later when compiling the balance, so outgoing flows contribute with a negative sign in the common case.
Example
@add_to_balance(transform, :energy, flow(fuel_edge))
@add_to_balance(transform, :energy, 0.5 * flow(elec_edge))MacroEnergy.@add_to_storage_balance — Macro
@add_to_storage_balance(storage, expression)
@add_to_storage_balance(storage, :storage, expression)Add terms to the reserved :storage balance on a storage component. This macro is a convenience wrapper around @add_to_balance that defaults the balance ID to :storage.
Modelers should usually write positive coefficients for both inflow and outflow terms. MacroEnergy applies edge-direction handling later when compiling the final storage balance, so outgoing storage flows reduce storage in the common case.
Example
@add_to_storage_balance(storage, charge_efficiency * flow(charge_edge))
@add_to_storage_balance(storage, 1 / discharge_efficiency * flow(discharge_edge))MacroEnergy.@inspect_stoichiometric_balance — Macro
@inspect_stoichiometric_balance(component, balance_id, equation, base_term)
@inspect_stoichiometric_balance(component, balance_id, equation, base_term, verify_edge_directions = true)Return the pairwise algebraic balance equations that @add_stoichiometric_balance would generate.
This is a debugging helper for understanding the recipe expansion without reading the lower-level BalanceTerm machinery. The macro returns a vector of Pair{Symbol, Expr} values mapping each generated balance ID to the plain algebraic equation that would be passed to @add_balance.
By default the macro skips orientation validation so the algebraic expansion can be inspected without requiring live component and edge objects. Pass verify_edge_directions = true to reuse the same orientation validation as @add_stoichiometric_balance.
Grouped constraint configuration
MacroEnergy.AbstractConstraintConfig — Type
AbstractConstraintConfigAbstract supertype for typed, inline constraint configuration payloads. Constraint types that support object values in an input constraints block define a concrete subtype.
MacroEnergy.AbstractGroupedConstraintConfig — Type
AbstractGroupedConstraintConfig <: AbstractConstraintConfigAbstract supertype for configurations that define one or more asset groups. Each group selects assets and constrains a capacity-like quantity on a named edge field.
MacroEnergy.GroupConfig — Type
GroupConfig(name, selector, edge, value)One asset group in a grouped constraint configuration.
name: unique input key used to identify the group in diagnostics and model references.selector: typed criteria used to select assets.edge: field name of the constrained edge on every selected asset.value: upper or lower bound, depending on the enclosing constraint.
MacroEnergy.GroupSelector — Type
GroupSelector(asset_type, all, any, exclude)Typed asset selector for a grouped constraint. An asset matches when it matches asset_type (when provided), contains every tag in all, contains at least one tag in any (when nonempty), and contains no tag in exclude.
MacroEnergy.MaxCapacityConstraintConfig — Type
MaxCapacityConstraintConfig(groups)Typed payload for a system-wide or location-level MaxCapacityConstraint. groups contains GroupConfig entries whose value is an upper bound on the sum of total capacity over the selected assets.
MacroEnergy.MinCapacityConstraintConfig — Type
MinCapacityConstraintConfig(groups)Typed payload for a system-wide or location-level MinCapacityConstraint. groups contains GroupConfig entries whose value is a lower bound on the sum of total capacity over the selected assets.
MacroEnergy.MaxNewCapacityConstraintConfig — Type
MaxNewCapacityConstraintConfig(groups)Typed payload for a system-wide or location-level MaxNewCapacityConstraint. groups contains GroupConfig entries whose value is an upper bound on the sum of newly built capacity over the selected assets.
MacroEnergy.configure_constraint! — Function
configure_constraint!(ct::AbstractTypeConstraint, cfg)Store inline configuration cfg on a constraint instance. cfg is the value parsed from a constraints block when it is an object rather than true (see check_and_convert_constraints!). Only constraint types that support inline configuration define a method; the generic fallback errors.
MacroEnergy.validate_required_constraint_configs! — Function
validate_required_constraint_configs!(constraints, scope)Ensure constraints attached at scope have the configuration payload required by that scope. Component-level constraints remain compatible with their legacy Boolean form; grouped constraints at system and location scope require a typed object payload.
MacroEnergy.parse_grouped_constraint_config — Function
parse_grouped_constraint_config(raw, config_type, constraint_name)Parse the object payload used by a grouped capacity constraint into its concrete, typed config. Each payload entry is either a legacy type-only group with edge and value, or a named group with select, edge, and value. select accepts optional asset_type, all, any, and exclude fields. The current grouped capacity constraints all use the shared GroupConfig entry type while retaining separate outer configuration schemas.
MacroEnergy.select_assets — Function
select_assets(system, selector)Return the assets that match selector. Asset type matching uses the Julia type hierarchy; tag criteria use the normalized tags stored on each asset.
MacroEnergy.build_grouped_capacity_constraints — Function
build_grouped_capacity_constraints(config, system, model; variable, sense, constraint_name, location)Build one capacity-like constraint for each group in config and return its JuMP references, keyed by group name. variable selects the summed quantity (capacity or new_capacity).
Grouped capacity constraint methods
MacroEnergy.add_model_constraint! — Method
add_model_constraint!(ct::MaxNewCapacityConstraint, y::Union{AbstractEdge,AbstractStorage}, model::Model)Add a max new capacity constraint to the edge or storage y. The functional form of the constraint is:
\[\begin{aligned} \text{new\_capacity(y)} \leq \text{max\_new\_capacity(y)} \end{aligned}\]
MacroEnergy.add_model_constraint! — Method
add_model_constraint!(ct::MaxCapacityConstraint, system::System, model::Model)Add a system-wide max capacity constraint capping, for each configured asset type, the total capacity of a named edge across all assets of that type. Configuration is carried on ct.config (populated from the constraints block in system_data.json). The functional form is:
\[\begin{aligned} \sum_{a \in \mathcal{A}}\text{capacity}(a.\text{edge}) \leq \text{value}(\mathcal{A}) \end{aligned}\]
MacroEnergy.add_model_constraint! — Method
add_model_constraint!(ct::MaxCapacityConstraint, location::Location, model::Model)Add a per-location max capacity constraint: same as the system-wide form, but only assets whose capped edge is located in location contribute. Configuration is carried on ct.config (populated from the constraints block of this location in locations.json).
MacroEnergy.add_model_constraint! — Method
add_model_constraint!(ct::MinCapacityConstraint, system::System, model::Model)Add a system-wide min capacity constraint requiring, for each configured asset type, the total capacity of a named edge across all assets of that type to be at least value. Configuration is carried on ct.config (populated from the constraints block in system_data.json). The functional form is:
\[\begin{aligned} \sum_{a \in \mathcal{A}}\text{capacity}(a.\text{edge}) \geq \text{value}(\mathcal{A}) \end{aligned}\]
MacroEnergy.add_model_constraint! — Method
add_model_constraint!(ct::MinCapacityConstraint, location::Location, model::Model)Add a per-location min capacity constraint: same as the system-wide form, but only assets whose capped edge is located in location contribute. Configuration is carried on ct.config (populated from the constraints block of this location in locations.json).
MacroEnergy.add_model_constraint! — Method
add_model_constraint!(ct::MaxNewCapacityConstraint, system::System, model::Model)Add a system-wide max new capacity constraint capping, for each configured asset type, the total newly built capacity of a named edge across all assets of that type. Configuration is carried on ct.config (populated from the constraints block in system_data.json). The functional form is:
\[\begin{aligned} \sum_{a \in \mathcal{A}}\text{new\_capacity}(a.\text{edge}) \leq \text{value}(\mathcal{A}) \end{aligned}\]
MacroEnergy.add_model_constraint! — Method
add_model_constraint!(ct::MaxNewCapacityConstraint, location::Location, model::Model)Add a per-location max new capacity constraint: same as the system-wide form, but only assets whose capped edge is located in location contribute. Configuration is carried on ct.config (populated from the constraints block of this location in locations.json).