From ae662fea5fa05699837035127fef01883699d4cf Mon Sep 17 00:00:00 2001 From: Diego Alejandro Tejada Arango <12887482+datejada@users.noreply.github.com> Date: Wed, 5 Mar 2025 18:51:50 +0100 Subject: [PATCH] Create model schema from a JSON file (#1054) * Remove prettier from the pre-commit * Add JSON schema file * Add JSON package to the dependencies --- .pre-commit-config.yaml | 6 - Project.toml | 2 + docs/Project.toml | 2 + docs/src/50-schemas.md | 64 ++- src/TulipaEnergyModel.jl | 1 + src/input-schemas.jl | 232 +---------- src/input-schemas.json | 820 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 882 insertions(+), 245 deletions(-) create mode 100644 src/input-schemas.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 72eefc50..24cef0e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,12 +37,6 @@ repos: rev: 054bda51dbe278b3e86f27c890e3f3ac877d616c hooks: - id: validate-cff - - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v4.0.0-alpha.8" # Use the sha or tag you want to point at - hooks: - - id: prettier - types_or: [yaml, json] - exclude: ".copier-answers.yml" - repo: https://github.com/adrienverge/yamllint rev: v1.35.1 hooks: diff --git a/Project.toml b/Project.toml index 933e91fa..b9904f0d 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DuckDB = "d2f5444f-75bc-4fdf-ac35-56f514c445e1" DuckDB_jll = "2cbbab25-fc8b-58cf-88d4-687a02676033" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" @@ -23,6 +24,7 @@ CSV = "0.10" DuckDB = "0.10, ~1.0" DuckDB_jll = "0.10, ~1.0" HiGHS = "1" +JSON = "0.21.4" JuMP = "1" MathOptInterface = "1" OrderedCollections = "1" diff --git a/docs/Project.toml b/docs/Project.toml index 2ab1d120..ac9861d7 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -5,7 +5,9 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DuckDB = "d2f5444f-75bc-4fdf-ac35-56f514c445e1" GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" TulipaEnergyModel = "5d7bd171-d18e-45a5-9111-f1f11ac5d04d" TulipaIO = "7b3808b7-0819-42d4-885c-978ba173db11" diff --git a/docs/src/50-schemas.md b/docs/src/50-schemas.md index b55ab917..5039ce70 100644 --- a/docs/src/50-schemas.md +++ b/docs/src/50-schemas.md @@ -2,7 +2,7 @@ The optimization model parameters with the input data must follow the schema below for each table. To create these tables we currently use CSV files that follow this same schema and then convert them into tables using TulipaIO, as shown in the basic example of the [Tutorials](@ref basic-example) section. -The schemas can be accessed at any time after loading the package by typing `TulipaEnergyModel.schema_per_table_name` in the Julia console. Here is the complete list of model parameters in the schemas per table (or CSV file): +The schemas can be found in the `input-schemas.json`. For more advanced users, they can also access the schemas at any time after loading the package by typing `TulipaEnergyModel.schema_per_table_name` in the Julia console. Here is the complete list of model parameters in the schemas per table (or CSV file): !!! info "Optional tables/files and their defaults" The following tables/files are allowed to be missing: "assets\_rep\_periods\_partitions", "assets\_timeframe\_partitions", "assets\_timeframe\_profiles", "flows\_rep\_periods\_partitions", "group\_asset", "profiles\_timeframe". @@ -11,14 +11,56 @@ The schemas can be accessed at any time after loading the package by typing `Tul - If no group table/file is available there will be no group constraints in the model ```@eval -using Markdown, TulipaEnergyModel - -Markdown.parse( - join(["- **`$filename`**\n" * - join( - [" - `$f: $t`" for (f, t) in schema], - "\n", - ) for (filename, schema) in TulipaEnergyModel.schema_per_table_name - ] |> sort, "\n") -) +""" +The output of the following code is a Markdown text with the following structure: + +TABLE_NAME +========= + +PARAMETER_NAME + + • Description: Lorem ipsum + • Type: SQL type of the parameter + • Default: a value or "No default" + • Unit of measure: a value or "No unit" + • Constraints: a table or "No constraints" +""" + +using Markdown, JSON +using OrderedCollections: OrderedDict + +input_schemas = JSON.parsefile("../../src/input-schemas.json"; dicttype = OrderedDict) + +let buffer = IOBuffer() + for (i,(table_name, fields)) in enumerate(input_schemas) + write(buffer, "## Table $i : `$table_name`\n\n") + for (field_name, field_info) in fields + _description = get(field_info, "description", "No description provided") + _type = get(field_info, "type", "Unknown type") + _unit = get(field_info, "unit_of_measure", "No unit") + _default = get(field_info, "default", "No default") + _constraints_values = get(field_info, "constraints", nothing) + + write(buffer, "**`$field_name`**\n\n") + write(buffer, "- Description: $_description\n\n") + write(buffer, "- Type: `$_type`\n") + write(buffer, "- Unit of measure: `$_unit` \n") + write(buffer, "- Default: `$_default`\n") + + if _constraints_values === nothing + write(buffer, "- Constraints: No constraints\n") + elseif isa(_constraints_values, OrderedDict) + write(buffer, "| Constraints | Value |\n| --- | --- |\n") + for (key, value) in _constraints_values + write(buffer, "| $key | `$value` |\n") + end + write(buffer, "\n") + else + write(buffer, "- Constraints: `$(string(_constraints_values))`\n") + end + end + end + Markdown.parse(String(take!(buffer))) +end + ``` diff --git a/src/TulipaEnergyModel.jl b/src/TulipaEnergyModel.jl index d3fc3763..b673add3 100644 --- a/src/TulipaEnergyModel.jl +++ b/src/TulipaEnergyModel.jl @@ -8,6 +8,7 @@ const SQL_FOLDER = joinpath(@__DIR__, "sql") using CSV: CSV using DuckDB: DuckDB, DBInterface using TOML: TOML +using JSON: JSON using TulipaIO: TulipaIO ## Optimization diff --git a/src/input-schemas.jl b/src/input-schemas.jl index 67ba57aa..8cfe33ea 100644 --- a/src/input-schemas.jl +++ b/src/input-schemas.jl @@ -1,232 +1,8 @@ -# At the end of the file, there is a reference relating schemas and files +# read schema from file -const schemas = ( - assets = ( - # Schema for asset.csv - basic = ( - :asset => "VARCHAR", # Name of Asset (geographical?) - :type => "VARCHAR", # Producer/Consumer/Storage/Conversion - :group => "VARCHAR", # Group to which the asset belongs to (missing -> no group) - :capacity => "DOUBLE", # MW - :min_operating_point => "DOUBLE", # Minimum operating point or minimum stable generation level defined as a portion of the capacity of asset [p.u.] - :investment_method => "VARCHAR", # Which method of investment (simple/compact) - :investment_integer => "BOOLEAN", # Whether investment is integer or continuous - :technical_lifetime => "INTEGER", # years - :economic_lifetime => "INTEGER", # years - :discount_rate => "DOUBLE", # p.u. - :consumer_balance_sense => "VARCHAR", # Sense of the consumer balance constraint (default ==) - :capacity_storage_energy => "DOUBLE", # MWh - :is_seasonal => "BOOLEAN", # Whether seasonal storage (e.g. hydro) or not (e.g. battery) - :use_binary_storage_method => "VARCHAR", # Whether to use an extra binary variable for the storage assets to avoid charging and discharging simultaneously (missing;binary;relaxed_binary) - :unit_commitment => "BOOLEAN", # Whether asset has unit commitment constraints - :unit_commitment_method => "VARCHAR", # Which unit commitment method to use (i.e., basic) - :unit_commitment_integer => "BOOLEAN", # Whether the unit commitment variables are integer or not - :ramping => "BOOLEAN", # Whether asset has ramping constraints - :storage_method_energy => "BOOLEAN", # Whether storage method is energy or not (i.e., fixed_ratio) - :energy_to_power_ratio => "DOUBLE", # Hours - :investment_integer_storage_energy => "BOOLEAN", # Whether investment for storage energy is integer or continuous - :max_ramp_up => "DOUBLE", # Maximum ramping up rate as a portion of the capacity of asset [p.u./h] - :max_ramp_down => "DOUBLE", # Maximum ramping down rate as a portion of the capacity of asset [p.u./h] - ), - - # Schema for asset-milestone.csv - milestone = OrderedDict( - :asset => "VARCHAR", - :milestone_year => "INTEGER", - :investable => "BOOLEAN", # Whether able to invest - :peak_demand => "DOUBLE", # MW - :storage_inflows => "DOUBLE", # MWh/year - :initial_storage_level => "DOUBLE", # MWh (Missing -> free initial level) - :min_energy_timeframe_partition => "DOUBLE", # MWh (Missing -> no limit) - :max_energy_timeframe_partition => "DOUBLE", # MWh (Missing -> no limit) - :units_on_cost => "DOUBLE", # Objective function coefficient on `units_on` variable. e.g., no-load cost or idling cost - ), - - # Schema for the asset-commission.csv - commission = OrderedDict( - :asset => "VARCHAR", - :commission_year => "INTEGER", # Year of commissioning - :fixed_cost => "DOUBLE", # kEUR/MW/year - :investment_cost => "DOUBLE", # kEUR/MW - :investment_limit => "DOUBLE", # MWh (Missing -> no limit) - :fixed_cost_storage_energy => "DOUBLE", # kEUR/MWh/year - :investment_cost_storage_energy => "DOUBLE", # kEUR/MWh - :investment_limit_storage_energy => "DOUBLE", # MWh (Missing -> no limit) - ), - - # Schema for the asset-both.csv file. - both = OrderedDict( - :asset => "VARCHAR", # Name of Asset (geographical?) - :milestone_year => "INTEGER", # Year - :commission_year => "INTEGER", # Year of commissioning - :decommissionable => "BOOLEAN", - :initial_units => "DOUBLE", # units - :initial_storage_units => "DOUBLE", # units - ), - - # Schema for the assets-profiles.csv and assets-timeframe-profiles.csv file. - profiles_reference = OrderedDict( - :asset => "VARCHAR", # Asset name - :commission_year => "INTEGER", - :profile_type => "VARCHAR", # Type of profile, used to determine source profile - :profile_name => "VARCHAR", # Name of profile, used to match with the profiles_data - ), - - # Schema for the assets-timeframe-partitions.csv file. - timeframe_partition = OrderedDict( - :asset => "VARCHAR", - :year => "INTEGER", - :specification => "VARCHAR", - :partition => "VARCHAR", - ), - - # Schema for the assets-rep-periods-partitions.csv file. - rep_periods_partition = OrderedDict( - :asset => "VARCHAR", - :year => "INTEGER", - :rep_period => "INTEGER", - :specification => "VARCHAR", - :partition => "VARCHAR", - ), - ), - groups = ( - # Schema for the group-asset.csv file. - data = OrderedDict( - :name => "VARCHAR", # Name of the Group - :milestone_year => "INTEGER", - :invest_method => "BOOLEAN", # true -> activate group constraints; false -> no group investment constraints - :min_investment_limit => "DOUBLE", # MW (Missing -> no limit) - :max_investment_limit => "DOUBLE", # MW (Missing -> no limit) - ), - ), - flows = ( - # Schema for flow.csv - basic = ( - :from_asset => "VARCHAR", # Name of Asset - :to_asset => "VARCHAR", # Name of Asset - :carrier => "VARCHAR", # (Optional?) Energy carrier - :is_transport => "BOOLEAN", # Whether a transport flow - :capacity => "DOUBLE", - :technical_lifetime => "INTEGER", - :economic_lifetime => "INTEGER", - :discount_rate => "DOUBLE", - :investment_integer => "BOOLEAN", # Whether investment is integer or continuous - ), - - # Schema for flow-milestone.csv - milestone = OrderedDict( - :from_asset => "VARCHAR", # Name of Asset - :to_asset => "VARCHAR", # Name of Asset - :milestone_year => "INTEGER", # Year of commissioning - :investable => "BOOLEAN", # Whether able to invest - :variable_cost => "DOUBLE", # kEUR/MWh - ), - - # Schema for the flow-commission.csv - commission = OrderedDict( - :from_asset => "VARCHAR", # Name of Asset - :to_asset => "VARCHAR", # Name of Asset - :commission_year => "INTEGER", # Year of commissioning - :fixed_cost => "DOUBLE", # kEUR/MWh/year - :investment_cost => "DOUBLE", # kEUR/MW - :efficiency => "DOUBLE", # p.u. (per unit) - :investment_limit => "DOUBLE", # MW - ), - - # Schema for the flow-both.csv file. - both = OrderedDict( - :from_asset => "VARCHAR", # Name of Asset - :to_asset => "VARCHAR", # Name of Asset - :milestone_year => "INTEGER", - :commission_year => "INTEGER", # Year of commissioning - :decommissionable => "BOOLEAN", - :initial_export_units => "DOUBLE", # MW - :initial_import_units => "DOUBLE", # MW - ), - - # Schema for the flows-profiles file. - profiles_reference = OrderedDict( - :from_asset => "VARCHAR", # Name of Asset - :to_asset => "VARCHAR", # Name of Asset - :year => "INTEGER", - :profile_type => "VARCHAR", # Type of profile, used to determine source profile - :profile_name => "VARCHAR", # Name of profile, used to match with the profiles_data - ), - - # Schema for the flows-rep-periods-partitions.csv file. - rep_periods_partition = OrderedDict( - :from_asset => "VARCHAR", # Name of Asset - :to_asset => "VARCHAR", # Name of Asset - :year => "INTEGER", - :rep_period => "INTEGER", - :specification => "VARCHAR", - :partition => "VARCHAR", - ), - ), - year = ( - # Schema for year-data.csv - data = ( - :year => "INTEGER", # Unique identifier (currently, the year itself) - :length => "INTEGER", - :is_milestone => "BOOLEAN", # Whether the year is a milestone year of a vintage year - ), - ), - timeframe = ( - # Schema for the profiles-timeframe-.csv file. - profiles_data = OrderedDict( - :profile_name => "VARCHAR", # Profile name - :year => "INTEGER", - :period => "INTEGER", # Period - :value => "DOUBLE", # p.u. (per unit) - ), - ), - rep_periods = ( - # Schema for the rep-periods-data.csv file. - data = OrderedDict( - :year => "INTEGER", - :rep_period => "INTEGER", # Representative period number - :num_timesteps => "INTEGER", # Numer of timesteps - :resolution => "DOUBLE", # Duration of each timestep (hours) - ), - - # Schema for the rep-periods-mapping.csv file. - mapping = OrderedDict( - :year => "INTEGER", - :period => "INTEGER", # Period number - :rep_period => "INTEGER", # Representative period number - :weight => "DOUBLE", # Hours - ), - - # Schema for the profiles-rep-periods-.csv file. - profiles_data = OrderedDict( - :profile_name => "VARCHAR", # Profile name - :year => "INTEGER", - :rep_period => "INTEGER", # Representative period number - :timestep => "INTEGER", # Timestep number - :value => "DOUBLE", # p.u. (per unit) - ), - ), -) +schema = JSON.parsefile("src/input-schemas.json"; dicttype = OrderedDict); const schema_per_table_name = OrderedDict( - "asset" => schemas.assets.basic, - "asset_both" => schemas.assets.both, - "asset_commission" => schemas.assets.commission, - "asset_milestone" => schemas.assets.milestone, - "assets_profiles" => schemas.assets.profiles_reference, - "assets_rep_periods_partitions" => schemas.assets.rep_periods_partition, - "assets_timeframe_partitions" => schemas.assets.timeframe_partition, - "assets_timeframe_profiles" => schemas.assets.profiles_reference, - "flow" => schemas.flows.basic, - "flow_both" => schemas.flows.both, - "flow_commission" => schemas.flows.commission, - "flow_milestone" => schemas.flows.milestone, - "flows_profiles" => schemas.flows.profiles_reference, - "flows_rep_periods_partitions" => schemas.flows.rep_periods_partition, - "group_asset" => schemas.groups.data, - "profiles_rep_periods" => schemas.rep_periods.profiles_data, - "profiles_timeframe" => schemas.timeframe.profiles_data, - "rep_periods_data" => schemas.rep_periods.data, - "rep_periods_mapping" => schemas.rep_periods.mapping, - "year_data" => schemas.year.data, + schema_key => OrderedDict(key => value["type"] for (key, value) in schema_content) for + (schema_key, schema_content) in schema ) diff --git a/src/input-schemas.json b/src/input-schemas.json new file mode 100644 index 00000000..58df8374 --- /dev/null +++ b/src/input-schemas.json @@ -0,0 +1,820 @@ +{ + "asset": { + "asset": { + "description": "Unique identifier with the name of the asset.", + "type": "VARCHAR" + }, + "capacity": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Capacity for one unit of the asset (is therefore multiplied by number of existing plus number of invested assets).", + "type": "DOUBLE", + "unit_of_measure": "MW" + }, + "capacity_storage_energy": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Capacity of one storage unit.", + "type": "DOUBLE", + "unit_of_measure": "MWh" + }, + "consumer_balance_sense": { + "constraints": { + "oneOf": [ + "==", + ">=", + "<=" + ] + }, + "default": "==", + "description": "Is the sense of the consumer balance constraint, equal to, greater than or less than.", + "type": "VARCHAR" + }, + "discount_rate": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": " e.g. 0.05 is 5 %. discount rate for the annuity calculation.", + "type": "DOUBLE", + "unit_of_measure": "ratio" + }, + "economic_lifetime": { + "constraints": { + "minimum": 0 + }, + "default": 1, + "description": "Economic lifetime of the unit for annuity calculation.", + "type": "INTEGER", + "unit_of_measure": "years" + }, + "energy_to_power_ratio": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Fixed ratio between the energy storage capacity [MWh] and the discharge/charge capacity [MW] for energy storage investments where `storage_method_energy = false`.", + "type": "DOUBLE", + "unit_of_measure": "h" + }, + "group": { + "description": "Group to which the asset belongs to (null/empty/missing -> no group).", + "type": "VARCHAR" + }, + "investment_integer": { + "default": false, + "description": "Whether investment decisions are using integer variables.", + "type": "BOOLEAN" + }, + "investment_integer_storage_energy": { + "default": false, + "description": "Whether investment for storage energy is integer or continuous. It only applies for energy storage investments where `storage_method_energy = true`.", + "type": "BOOLEAN" + }, + "investment_method": { + "constraints": { + "oneOf": [ + "none", + "simple", + "compact" + ] + }, + "default": "none", + "description": "How investments are treated.", + "type": "VARCHAR" + }, + "is_seasonal": { + "default": false, + "description": "Whether seasonal storage (e.g., hydro) or not (e.g., battery)", + "type": "BOOLEAN" + }, + "max_ramp_down": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Maximum ramping down rate as a portion of the capacity of asset.", + "type": "DOUBLE", + "unit_of_measure": "p.u./h" + }, + "max_ramp_up": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Maximum ramping up rate as a portion of the capacity of asset.", + "type": "DOUBLE", + "unit_of_measure": "p.u./h" + }, + "min_operating_point": { + "constraints": { + "maximum": 1, + "minimum": 0 + }, + "default": 0, + "description": "Minimum operating point or minimum stable generation level defined as a portion of the capacity of asset.", + "type": "DOUBLE", + "unit_of_measure": "p.u." + }, + "ramping": { + "default": false, + "description": "Whether asset has ramping constraints or not.", + "type": "BOOLEAN" + }, + "storage_method_energy": { + "default": false, + "description": "Whether there is independent investment on storage capacity or not. If false, the investment on storage capacity uses the energy_to_power_ratio as a constant fixed value.", + "type": "BOOLEAN" + }, + "technical_lifetime": { + "constraints": { + "minimum": 0 + }, + "default": 1, + "description": "Technical lifetime of the unit to determine for how long the capacity is considered from the commission year.", + "type": "INTEGER", + "unit_of_measure": "years" + }, + "type": { + "constraints": { + "oneOf": [ + "producer", + "consumer", + "storage", + "conversion", + "hub" + ] + }, + "description": "Type of energy asset.", + "type": "VARCHAR" + }, + "unit_commitment": { + "default": false, + "description": "Whether asset has unit commitment constraints or not", + "type": "BOOLEAN" + }, + "unit_commitment_integer": { + "default": false, + "description": "Whether the unit commitment variables are integer or not.", + "type": "BOOLEAN" + }, + "unit_commitment_method": { + "constraints": { + "oneOf": [ + null, + "basic" + ] + }, + "description": "Which unit commitment method to use (null/empty/missing -> no unit commitment method).", + "type": "VARCHAR" + }, + "use_binary_storage_method": { + "constraints": { + "oneOf": [ + null, + "binary", + "relaxed_binary" + ] + }, + "description": "Whether to use an extra binary variable for the storage assets to avoid charging and discharging simultaneously (null/empty/missing -> no binary).", + "type": "VARCHAR" + } + }, + "asset_both": { + "asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "commission_year": { + "default": 2000, + "description": "Year of commissioning", + "type": "INTEGER" + }, + "decommissionable": { + "default": false, + "description": "Whether the asset can be decomission or not.", + "type": "BOOLEAN" + }, + "initial_storage_units": { + "default": 0, + "description": "Number of existing storage units", + "type": "DOUBLE", + "unit_of_measure": "number" + }, + "initial_units": { + "default": 0, + "description": "Number of existing units", + "type": "DOUBLE", + "unit_of_measure": "number" + }, + "milestone_year": { + "default": 2000, + "description": "Year of investment and operation decisions in the optimization.", + "type": "INTEGER" + } + }, + "asset_commission": { + "asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "commission_year": { + "default": 2000, + "description": "Year of commissioning.", + "type": "INTEGER" + }, + "fixed_cost": { + "description": "Fixed annual cost for the asset capacity.", + "type": "DOUBLE", + "unit_of_measure": "CUR/MW/year" + }, + "fixed_cost_storage_energy": { + "description": "Fixed annual cost for the asset storage capacity", + "type": "DOUBLE", + "unit_of_measure": "CUR/MWh/year" + }, + "investment_cost": { + "description": "Investment cost for the asset capacity.", + "type": "DOUBLE", + "unit_of_measure": "CUR/MW" + }, + "investment_cost_storage_energy": { + "description": "Investment cost for the asset energy storage capacity.", + "type": "DOUBLE", + "unit_of_measure": "CUR/MWh" + }, + "investment_limit": { + "description": "Maximum capacity for the asset investment. If the initial value is null, empty, or missing, it will be no limit.", + "type": "DOUBLE", + "unit_of_measure": "MWh" + }, + "investment_limit_storage_energy": { + "description": "Maximum capacity for the asset storage investment. If the initial value is null, empty, or missing, it will be no limit.", + "type": "DOUBLE", + "unit_of_measure": "MWh" + } + }, + "asset_milestone": { + "asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "initial_storage_level": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "The initial storage level at the beginning of the optimization. The final storage level needs to be above this initial value. If the initial value is null, empty, or missing, it will be optimized using a cycling constraint that links the last period to the initial period.", + "type": "DOUBLE", + "unit_of_measure": "MWh" + }, + "investable": { + "default": false, + "description": "Whether there is an investment variable created for the asset or not.", + "type": "BOOLEAN" + }, + "max_energy_timeframe_partition": { + "constraints": { + "minimum": 0 + }, + "description": "The maximum amount of energy across the timeframe (e.g., a year) that the asset must produce. If the initial value is null, empty, or missing, it will be no limit.", + "type": "DOUBLE", + "unit_of_measure": "MWh" + }, + "milestone_year": { + "default": 2000, + "description": "Year of investment and operation decisions in the optimization.", + "type": "INTEGER" + }, + "min_energy_timeframe_partition": { + "constraints": { + "minimum": 0 + }, + "description": "The minimum amount of energy across the timeframe (e.g., a year) that the asset must produce. If the initial value is null, empty, or missing, it will be no limit.", + "type": "DOUBLE", + "unit_of_measure": "MWh" + }, + "peak_demand": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Value that multiplies the demand profile time series.", + "type": "DOUBLE", + "unit_of_measure": "MW" + }, + "storage_inflows": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Value that multiplies the inflow profile time series.", + "type": "DOUBLE", + "unit_of_measure": "MWh/year" + }, + "units_on_cost": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Cost of keeping unit online for one hour or the objective function coefficient on `units_on` variable. e.g., no_load cost or idling cost", + "type": "DOUBLE", + "unit_of_measure": "CUR/p.u./h" + } + }, + "assets_profiles": { + "asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "commission_year": { + "default": 2000, + "description": "Year of commissioning", + "type": "INTEGER" + }, + "profile_name": { + "description": "Name of profile, used to determine data inside the DuckDB table", + "type": "VARCHAR" + }, + "profile_type": { + "constraints": { + "oneOf": [ + "availability", + "demand", + "inflows", + "max_storage_level", + "min_storage_level" + ] + }, + "description": "Type of profile, used to determine DuckDB table with source profile", + "type": "VARCHAR" + } + }, + "assets_rep_periods_partitions": { + "asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "partition": { + "default": "1", + "description": "Partition or temporal resolution in the representative periods. For example, for a `uniform` specification `1` is hourly, `2` is every two hours.", + "type": "VARCHAR" + }, + "rep_period": { + "description": "Number of the representative period", + "type": "INTEGER", + "unit_of_measure": "number" + }, + "specification": { + "constraints": { + "oneOf": [ + "uniform", + "explicit", + "math" + ] + }, + "default": "uniform", + "description": "Partition (or temporal resolution) specification in the representative periods.", + "type": "VARCHAR" + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "assets_timeframe_partitions": { + "asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "partition": { + "default": "1", + "description": "Partition or temporal resolution in the timeframe. For example, if a period is equivalent to a day then for a `uniform` specification `1` is per day, `2` is every two days.", + "type": "VARCHAR" + }, + "specification": { + "constraints": { + "oneOf": [ + "uniform", + "explicit", + "math" + ] + }, + "default": "uniform", + "description": "Partition (or temporal resolution) specification in the timeframe.", + "type": "VARCHAR" + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "assets_timeframe_profiles": { + "asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "commission_year": { + "default": 2000, + "description": "Year of commissioning", + "type": "INTEGER" + }, + "profile_name": { + "description": "Name of profile, used to determine data inside the DuckDB table", + "type": "VARCHAR" + }, + "profile_type": { + "constraints": { + "oneOf": [ + "max_storage_level", + "min_storage_level", + "max_energy", + "min_energy" + ] + }, + "description": "Type of profile, used to determine DuckDB table with source profile", + "type": "VARCHAR" + } + }, + "flow": { + "capacity": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": "Capacity for one unit of the transport flow (is therefore multiplied by number of existing plus invested number of transport assets).", + "type": "DOUBLE", + "unit_of_measure": "MW" + }, + "carrier": { + "description": "Energy carrier", + "type": "VARCHAR" + }, + "discount_rate": { + "constraints": { + "minimum": 0 + }, + "default": 0, + "description": " e.g. 0.05 is 5 %. discount rate for the annuity calculation.", + "type": "DOUBLE", + "unit_of_measure": "ratio" + }, + "economic_lifetime": { + "constraints": { + "minimum": 0 + }, + "default": 1, + "description": "Economic lifetime of the transport asset for annuity calculation.", + "type": "INTEGER", + "unit_of_measure": "years" + }, + "from_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "investment_integer": { + "default": false, + "description": "Whether investment decisions are using integer variables.", + "type": "BOOLEAN" + }, + "is_transport": { + "default": false, + "description": "Whether a transport flow or not. Transport assets can have flows in both directions and can be invested in.", + "type": "BOOLEAN" + }, + "technical_lifetime": { + "constraints": { + "minimum": 0 + }, + "default": 1, + "description": "Technical lifetime of the transport asset to determine for how long the capacity is considered from the commission year.", + "type": "INTEGER", + "unit_of_measure": "years" + }, + "to_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + } + }, + "flow_both": { + "commission_year": { + "default": 2000, + "description": "Year of commissioning", + "type": "INTEGER" + }, + "decommissionable": { + "default": false, + "description": "Whether the transport asset can be decomission or not.", + "type": "BOOLEAN" + }, + "from_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "initial_export_units": { + "default": 0, + "description": "Number of existing units in `from_asset` -> `to_asset` direction", + "type": "DOUBLE", + "unit_of_measure": "number" + }, + "initial_import_units": { + "default": 0, + "description": "Number of existing units in `to_asset` -> `from_asset` direction", + "type": "DOUBLE", + "unit_of_measure": "number" + }, + "milestone_year": { + "default": 2000, + "description": "Year of investment and operation decisions in the optimization.", + "type": "INTEGER" + }, + "to_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + } + }, + "flow_commission": { + "commission_year": { + "default": 2000, + "description": "Year of commissioning", + "type": "INTEGER" + }, + "efficiency": { + "constraints": { + "minimum": 0 + }, + "default": 1, + "description": "Efficiency of transfer in `from_asset` -> `to_asset` direction", + "type": "DOUBLE", + "unit_of_measure": "ratio" + }, + "fixed_cost": { + "description": "Fixed annual cost for the transport asset capacity.", + "type": "DOUBLE", + "unit_of_measure": "CUR/MW/year" + }, + "from_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "investment_cost": { + "description": "Investment cost for the transport asset capacity.", + "type": "DOUBLE", + "unit_of_measure": "CUR/MW" + }, + "investment_limit": { + "description": "Maximum capacity for the transport asset investment. If the initial value is null, empty, or missing, it will be no limit.", + "type": "DOUBLE", + "unit_of_measure": "MWh" + }, + "to_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + } + }, + "flow_milestone": { + "from_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "investable": { + "default": false, + "description": "Whether there is an investment variable created for the asset or not.", + "type": "BOOLEAN" + }, + "milestone_year": { + "default": 2000, + "description": "Year of investment and operation decisions in the optimization.", + "type": "INTEGER" + }, + "to_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "variable_cost": { + "default": 0, + "description": "Variable cost for the flow.", + "type": "DOUBLE", + "unit_of_measure": "CUR/MWh" + } + }, + "flows_profiles": { + "from_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "profile_name": { + "description": "Name of profile, used to determine data inside the DuckDB table", + "type": "VARCHAR" + }, + "profile_type": { + "constraints": { + "oneOf": [ + "availability" + ] + }, + "description": "Type of profile, used to determine DuckDB table with source profile", + "type": "VARCHAR" + }, + "to_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "flows_rep_periods_partitions": { + "from_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "partition": { + "default": "1", + "description": "Partition or temporal resolution in the representative periods. For example, for a `uniform` specification `1` is hourly, `2` is every two hours.", + "type": "VARCHAR" + }, + "rep_period": { + "description": "Number of the representative period", + "type": "INTEGER", + "unit_of_measure": "number" + }, + "specification": { + "constraints": { + "oneOf": [ + "uniform", + "explicit", + "math" + ] + }, + "default": "uniform", + "description": "Partition (or temporal resolution) specification in the representative periods.", + "type": "VARCHAR" + }, + "to_asset": { + "description": "Name of the asset. Same as the one in the `asset` table.", + "type": "VARCHAR" + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "group_asset": { + "invest_method": { + "description": "true -> activate group constraints; false -> no group investment constraints", + "type": "BOOLEAN" + }, + "max_investment_limit": { + "description": "MW (Missing -> no limit)", + "type": "DOUBLE" + }, + "milestone_year": { + "default": 2000, + "description": "Year of investment and operation decisions in the optimization.", + "type": "INTEGER" + }, + "min_investment_limit": { + "description": "MW (Missing -> no limit)", + "type": "DOUBLE" + }, + "name": { + "description": "Name of the Group", + "type": "VARCHAR" + } + }, + "profiles_rep_periods": { + "profile_name": { + "description": "Profile name.", + "type": "VARCHAR" + }, + "rep_period": { + "default": 1, + "description": "Representative period number.", + "type": "INTEGER" + }, + "timestep": { + "default": 1, + "description": "Timestep number.", + "type": "INTEGER" + }, + "value": { + "description": "Value of the profile.", + "type": "DOUBLE", + "unit_of_measure": "p.u." + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "profiles_timeframe": { + "period": { + "description": "Period.", + "type": "INTEGER" + }, + "profile_name": { + "description": "Profile name.", + "type": "VARCHAR" + }, + "value": { + "description": "value of the profile.", + "type": "DOUBLE", + "unit_of_measure": "p.u." + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "rep_periods_data": { + "num_timesteps": { + "default": 8760, + "description": "Number of timesteps", + "type": "INTEGER" + }, + "rep_period": { + "default": 1, + "description": "Representative period number.", + "type": "INTEGER", + "unit_of_measure": "number" + }, + "resolution": { + "default": 1, + "description": "Duration of each timestep", + "type": "DOUBLE", + "unit_of_measure": "hours" + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "rep_periods_mapping": { + "period": { + "default": 1, + "description": "Period number.", + "type": "INTEGER" + }, + "rep_period": { + "default": 1, + "description": "Representative period number.", + "type": "INTEGER" + }, + "weight": { + "default": 1, + "description": "Hours", + "type": "DOUBLE" + }, + "year": { + "default": 2000, + "description": "Milestone year.", + "type": "INTEGER" + } + }, + "timeframe_data": { + "num_timesteps": { + "default": 8760, + "description": "Number of timesteps of the representative period.", + "type": "INTEGER" + }, + "period": { + "description": "Period.", + "type": "INTEGER" + }, + "year": { + "default": 2000, + "description": "Unique identifier (currently, the year itself)", + "type": "INTEGER" + } + }, + "year_data": { + "is_milestone": { + "default": true, + "description": "Whether the year is a milestone year or a vintage year", + "type": "BOOLEAN" + }, + "length": { + "default": 8760, + "description": "How many hours in a year, e.g., 8760", + "type": "INTEGER" + }, + "year": { + "default": 2000, + "description": "Unique identifier (currently, the year itself)", + "type": "INTEGER" + } + } +}