PSRIO - v0.19.0

Introduction

PSRIO is a scripting language develop on top of LUA designed for:

  1. Querying data held in PSR databases (inputs and outputs)
  2. Perform several user-specified mathematical, statistical and data processing operations
  3. Produce new customized outputs in the same format as the SDDP outputs, that can be processed by the plotting tool

The main objective of this tool is to automate and standardize everyday operations by delegating tasks to an easy-to-use and secure language, avoiding manual errors and repeated work.

Additionally, PSRIO also enables the direct creation of dashboards.

What is a Scripting Language?

  • Dynamic typing, structures, and binding (vs static)
  • Flexibility (vs robustness)
  • Programmer efficiency (vs computer efficiency)
  • Interpreted (vs compiled)
  • Faster cycle “edit-run-test”
  • Automatic memory management
  • Avoids several common bugs (overflows)
  • Interface to other languages/programs

Why Scripting?

  • No language is optimal for everything
  • Programming languages are complex engineering projects
  • Extend your application beyond compile time (“glue language”):
    • System language implements the hard parts (components change little)
    • Scripting language connects those parts (final applications change much)
  • Quick-and-dirt programming (exploratory and rapid prototyping)
  • Friendliness and easiness to end-user programming
  • Examples: Shell/Bash in Unix, NumPy in Python

LUA

  • A scripting language designed by Roberto Ierusalimschy, Luiz H. de Figueiredo, and Waldemar Celes at Tecgraf/PUC-Rio
  • "The simplest thing that could possibly work"
  • Simple, yet powerful, language for embedding and extending applications
  • Portable and small, written in ANSI C, easy to integrate with C/C++
  • TIOBE Index for May 2022:
    • 1st Python; 2nd C; 3rd Java; 4th C++; 5th C#; 7th JavaScript; 18th Lua; 25th Julia; 30th Fortran
  • Widely used in Triple-A games and small devices (embedded systems and IoT)

Scripting + LUA + PSR = PSRIO

  • Treating code as data: every time you build a query, you're treating code as data
  • C++ code implements the complex and stable parts:
    • Data reading and processing, relationships, data structures, unit converting, and dashboarding
  • Lua connects those parts: flexible, easy to change

Support or Contact

Having trouble with PSRIO? Contact our support team via sddp@psr-inc.com and we'll help you sort it out.

Attributes

Attributes are properties that characterize an expression or a study. Expressions related to results of a PSR model, whose data can change over stages, scenarios, and blocks, for example, have a set of attributes in PSRIO that can be useful in some tasks. In the example below, we load the thermal generation:

thermal = Thermal();
gerter = thermal:load("gerter");

And the log shows some expression's attributes information:

[info] Loading gerter (stages: 12 [1:12] [month] [10/2016], blocks: hour, scenarios: 1200, unit: GWh, agents: 3 [thermal])

From the log, it is possible to see that gerter has:

  • 12 stages in a month-level resolution, where the first stage is 1 and the last is 12
  • initial stage is 10 (october) and the initial year is 2016
  • Hourly discretization
  • 1200 scenarios
  • generation data given in GWh
  • 3 agents (thermal plants)

Expression Attributes

All attributes related to stages, block, scenarios, agents and units of an expression and their respective methods are presented in the tables below.

Stages

Method Return Type
exp:first_stage() number
exp:last_stage() number
exp:stage_type() number
exp:initial_stage() number
exp:initial_year() number
exp:final_year() number
exp:week(stage) number
exp:month(stage) number
exp:year(stage) number
exp:stage(index) number
exp:stages() number

Example

stages = gerter:stages() -- 12

stage1 = gerter:stage(1) -- 10
stage2 = gerter:stage(2) -- 11
stage3 = gerter:stage(3) -- 12
stage4 = gerter:stage(4) -- 13

stage_type = gerter:stage_type() -- 2 (monthly)

initial_stage = gerter:initial_stage() -- 10
initial_year = gerter:initial_year() -- 2016
final_year = gerter:final_year() -- 2017

month1 = gerter:month(1) -- 10
month2 = gerter:month(2) -- 11
month3 = gerter:month(3) -- 12
month4 = gerter:month(4) --  1

year1 = gerter:year(1) -- 2016
year4 = gerter:year(4) -- 2017

Blocks

Method Return Type
exp:blocks(stage) number
exp:has_blocks() boolean
exp:is_hourly() boolean
exp:hour_discretization() number

Example

blocks1 = gerter:blocks(1) -- 744
blocks2 = gerter:blocks(2) -- 720

has_blocks = gerter:has_blocks() -- true

is_hourly = gerter:is_hourly() -- true

Scenarios

Method Return Type
exp:scenarios() number

Example

scenarios = gerter:scenarios() -- 1200

Agents

Method Return Type
exp:agents_size() number
exp:agent(index) string
exp:code(index) number
exp:agents() table of strings
exp:codes() table of numbers
exp:has_agent(string) boolean

Example

agents = gerter:agents() -- { "Thermal 1", "Thermal 2", "Thermal 3" }
for i, agent in ipairs(agents) do
    info(agent .. " at index " .. i);
end
size = gerter:agents_size() -- 3
for i = 1,size do
    agent = gerter:agent(i);
    info(agent .. " at index " .. i);
end
[info] Thermal 1 at index 1
[info] Thermal 2 at index 2
[info] Thermal 3 at index 3

Unit

Method Return Type
exp:unit() string

Example

unit = gerter:unit() -- "GWh"

Study Attributes

Stages

Method Return Type
study:stage_type() number
study:initial_stage() number
study:initial_year() number
study:final_year() number
study:stages() number
study:stages_per_year() number

Blocks

Method Return Type
study:blocks(stage) number
study:is_hourly() boolean
study:has_hourly_load() boolean
study:has_hour_block_map() boolean

Scenarios

Method Return Type
study:scenarios() number
study:openings() number

Others

Method Return Type
study:get_parameter(key, fallback_integer) integer
study:get_parameter(key, fallback_double) double

Collections

Collection Constructor Enumerate
Area Area() Collection.AREA
Balancing Area BalancingArea() Collection.BALANCING_AREA
Balancing Area Hydro BalancingAreaHydro() Collection.BALANCING_AREA_HYDRO
Balancing Area Thermal BalancingAreaThermal() Collection.BALANCING_AREA_THERMAL
Battery Battery() Collection.BATTERY
Bus Bus() Collection.BUS
Circuit Circuit() Collection.CIRCUIT
Circuits Sum CircuitsSum() Collection.CIRCUITS_SUM
Concentrated Solar Power ConcentratedSolarPower() Collection.CONCENTRATED_SOLAR_POWER
DC Link DCLink() Collection.DCLINK
Demand Demand() Collection.DEMAND
Demand Segment DemandSegment() Collection.DEMAND_SEGMENT
Expansion Capacity ExpansionCapacity() Collection.EXPANSION_CAPACITY
Expansion Project ExpansionProject() Collection.EXPANSION_PROJECT
Fuel Fuel() Collection.FUEL
Fuel Consumption FuelConsumption() Collection.FUEL_CONSUMPTION
Fuel Contract FuelContract() Collection.FUEL_CONTRACT
Fuel Reservoir FuelReservoir() Collection.FUEL_RESERVOIR
Generator Generator() Collection.GENERATOR
Generation Constraint GenerationConstraint() Collection.GENERATION_CONSTRAINT
Generic Generic() ---
Hydro Hydro() Collection.HYDRO
Interconnection Interconnection() Collection.INTERCONNECTION
Interconnection Sum InterconnectionSum() ---
Power Injection PowerInjection() Collection.POWER_INJECTION
Renewable Renewable() Collection.RENEWABLE
Renewable Gauging Station RenewableGaugingStation() Collection.RENEWABLE_GAUGING_STATION
Reserve Generation Constraint ReserveGenerationConstraint() Collection.RESERVE_GENERATION_CONSTRAINT
Reservoir Set ReservoirSet() Collection.RESERVOIR_SET
Study Study() ---
System System() Collection.SYSTEM
Thermal Thermal() Collection.THERMAL

Loading an Output

Operator Syntax
Load method output = collection:load("filename")

The generic collection can load any output with any agent type.

Example

The following example loads two outputs, `gerhid and fprodt, considering the agents as hydro plants collection:

hydro = Hydro();
gerhid = hydro:load("gerhid");
fprodt = hydro:load("fprodt");

Example

The following example loads two outputs, cmgdem and demand, considering the agents as system collection:

system = System();
cmgdem = system:load("cmgdem");
demand = system:load("demand");

Example 3

The following example loads two outputs, gerter and coster, considering the agents as thermal plants collection:

thermal = Thermal();
gerter = thermal:load("gerter");
coster = thermal:load("coster");

Example 4

The following example loads two outputs, objcop and outdfact, considering the agents as generic:

generic = Generic();
objcop = generic:load("objcop");
outdfact = generic:load("outdfact");

Loading a Generic CSV

Operator Syntax
Load table method table = study:load_table("filename.csv")

The following example loads a generic csv table and iterates through the rows:

Example

file.csv:

Name         , Node, Interval, Startup, Participation
H_1_1        , 1   , 1       , 1      , 0.79
R_1_3        , 1   , 1       , 1      , 0.20
CONTRATO_FLAT, 1   , 1       , 1      , 1

script:

study = Study();
table = study:load_table("file.csv");

info("Name,Node,Interval,Startup,Participation");
for i=1,#table do
    info(
        table[i]["Name"] .. "," .. 
        table[i]["Node"] .. "," .. 
        table[i]["Interval"] .. "," .. 
        table[i]["Startup"] .. "," .. 
        table[i]["Participation"]
    );
end

Loading an Input

Area

Data Unit
area.code ---
area.imported MW
area.exported MW

Battery

Data Unit
battery.code ---
battery.state ---
battery.capacity MW

Bus

Data Unit
bus.code ---
bus.voltage_level kV

Circuit

Data Unit
circuit.code ---
circuit.state ---
circuit.resistance %
circuit.reactance %
circuit.capacity MW
circuit.emergency_capacity MW
circuit.DLR_factor pu
circuit.monitored ---
circuit.monitored_contingencies ---
circuit.is_dc ---
circuit.international_cost_from $/MWh
circuit.international_cost_to $/MWh

Concentrated Solar Power

Data Unit
concentrated_solar_power.hour_generation pu

DC Link

Data Unit
dclink.code ---
dclink.state ---
dclink.capacity_from MW
dclink.capacity_to MW
dclink.wheeling_cost_from $/MWh
dclink.wheeling_cost_to $/MWh

Demand

Data Unit
demand.code ---
demand.is_elastic ---
demand.inelastic_hour MW
demand.inelastic_block GWh
demand.is_flexible ---
demand.max_increase pu
demand.max_decrease pu
demand.curtailment_cost $/MWh
demand.max_curtailment pu
demand.variable_block_duration h

Demand Segment

Data Unit
demand_segment.hour MW
demand_segment.block GWh
demand_segment.hour_price $/MWh
demand_segment.block_price $/MWh

Fuel

Data Unit
fuel.code ---
fuel.cost $/gal
fuel.emission_factor tCO2/MWh
fuel.min_consumption kgal
fuel.max_consumption kgal
fuel.availability kgal

Gauging Station

Data Unit
gauging_station.code ---
gauging_station.inflow m3/s
gauging_station.forward m3/s
gauging_station.backward m3/s
gauging_station.hour_inflow_historical_scenarios_nodata ---
gauging_station.hour_inflow_historical_scenarios m3/s
gauging_station.hour_inflow m3/s

Hydro

Data Unit
hydro.code ---
hydro.state ---
hydro.units ---
hydro.system ---
hydro.max_generation MW
hydro.max_generation_available MW
hydro.forced_outage_rate %
hydro.historical_outage_factor %
hydro.min_storage hm3
hydro.max_storage hm3
hydro.min_turbining_outflow m3/s
hydro.max_turbining_outflow m3/s
hydro.om_cost $/MWh
hydro.irrigation m3/s
hydro.min_total_outflow_modification m3/s
hydro.target_storage_tolerance %
hydro.disconsider_in_stored_and_inflow_energy ---
hydro.mean_production_coefficient MW/(m3/s)
hydro.loss_factor pu
hydro.min_total_outflow m3/s
hydro.min_total_outflow_penalty k$/m3/s
hydro.min_total_outflow_penalty_type ---
hydro.max_total_outflow m3/s
hydro.max_total_outflow_penalty k$/m3/s
hydro.max_total_outflow_penalty_type ---
hydro.min_operative_storage hm3
hydro.min_operative_storage_penalty k$/hm3
hydro.min_operative_storage_penalty_type ---
hydro.max_operative_storage hm3
hydro.max_operative_storage_penalty k$/hm3
hydro.max_operative_storage_penalty_type ---
hydro.flood_control hm3
hydro.alert_storage hm3
hydro.alert_storage_penalty k$/hm3
hydro.alert_storage_penalty_type ---
hydro.min_spillage m3/s
hydro.min_spillage_penalty k$/m3/s
hydro.min_spillage_penalty_type ---
hydro.max_spillage m3/s
hydro.max_spillage_penalty k$/m3/s
hydro.max_spillage_penalty_type ---
hydro.min_bio_spillage %
hydro.min_bio_spillage_penalty k$/hm3
hydro.min_bio_spillage_penalty_type ---
hydro.target_storage hm3
hydro.max_turbining m3/s
hydro.max_turbining_penalty k$/m3/s
hydro.max_turbining_penalty_type ---
hydro.min_turbining_penalty k$/m3/s
hydro.min_turbining_penalty_type ---
hydro.spinning_reserve %
hydro.max_reserve MW

Interconnection

Data Unit
interconnection.code ---
interconnection.state ---
interconnection.capacity_from MW
interconnection.capacity_to MW
interconnection.cost_from $/MWh
interconnection.cost_to $/MWh

Power Injection

Data Unit
power_injection.code ---
power_injection.hour_capacity MW
power_injection.hour_price $/MWh

Renewable

Data Unit
renewable.code ---
renewable.state ---
renewable.units ---
renewable.tech_type ---
renewable.capacity MW
renewable.om_cost $/MWh
renewable.hour_generation pu
renewable.block_generation pu
renewable.operation_factor pu

Renewable Gauging Station

Data Unit
renewable_gauging_station.code ---
renewable_gauging_station.hour_generation pu
renewable_gauging_station.block_generation pu
renewable_gauging_station.hour_historical_generation pu

Reservoir Set

Data Unit
reservoir_set.code ---
reservoir_set.security_energy MWh
reservoir_set.flood_control_energy MWh
reservoir_set.alert_energy MWh

System

System

Data Unit
system.code ---
system.load_level_length ---
system.hour_block_map ---
system.sensitivity ---
system.carbon_credit_cost $/tCO2

Thermal

Data Unit
thermal.code ---
thermal.state ---
thermal.units ---
thermal.system ---
thermal.min_generation MW
thermal.min_generation_available MW
thermal.min_generation_constraint MW
thermal.max_generation MW
thermal.max_generation_available MW
thermal.forced_outage_rate %
thermal.historical_outage_factor %
thermal.startup_cost k$
thermal.startup_cost_constraint k$
thermal.om_cost $/MWh
thermal.specific_consumption_segment_1 gal/MWh
thermal.specific_consumption_segment_2 gal/MWh
thermal.specific_consumption_segment_3 gal/MWh
thermal.fuel_transportation_cost $/gal
thermal.operation_mode ---
thermal.emission_coefficient pu
thermal.ramp_up MW/min
thermal.ramp_down MW/min
thermal.min_uptime hour
thermal.min_downtime hour
thermal.max_startups ---
thermal.max_shutdowns ---
thermal.shutdown_cost k$
thermal.alternative_fuel ---
thermal.spinning_reserve %
thermal.max_reserve MW
thermal.bid_price $/MWh

Expressions

Unary Expressions

PSRIO provides four unary operators that only receive one expression and does not modify any dimension (stages, blocks, scenarios, or agents). The unary minus maps the data values to their additive inverses; the absolute value is the non-negative value of the data without regard to its sign; the round method rounds the data to the specified number of digits after the decimal separator; the convert method determines the unit of the data.


Operator Syntax
Unary Minus exp = -exp1
Absolute Value exp = exp1:abs()
Round exp = exp1:round(int)
Convert exp = exp1:convert(string)
Fill exp = exp1:fill(double)

Example

The example below takes the power flow in a circuit and calculates its absolute values.

circuit = Circuit();
cirflw = circuit:load("cirflw");

abs_cirflw = cirflw:abs();

Example

Here, we use the convert method to act over the generation data of a set of thermal plants given in GWh and convert it to MW.

thermal = Thermal();
thermal_gen = circuit:load("gerter");

converted_thermal_gen = thermal_gen:convert("MW");

Binary Expressions

PSRIO provides binary operators which can change attributes (stages, blocks, scenarios, and agents) depending on inputs. The first table presents the supported arithmetic operators.

Operator Syntax
Addition exp = exp1 + exp2
Subtraction exp = exp1 - exp2
Multiplication exp = exp1 * exp2
Right Division exp = exp1 / exp2
Power exp = exp1 ^ exp2

The second table defines the logical and comparison operators.

Operator Syntax
Equal to exp = exp1:eq(exp2)
Not Equal to exp = exp1:ne(exp2)
Less-than exp = exp1:lt(exp2)
Less-than-or-equals to exp = exp1:le(exp2)
Greater-than exp = exp1:gt(exp2)
Greater-than-or-equals to exp = exp1:ge(exp2)
And exp = exp1 & exp2
Or exp = exp1 | exp2

The third table defines two element-wise max and min methods between the two data arguments.

Operator Syntax
Maximum exp = max(exp1, exp2)
Minimum exp = min(exp1, exp2)

Here are some examples of binary expressions.

Example

Calculating the useful storage of a hydro plant:

hydro = Hydro();
useful_storage = hydro.max_storage - hydro.min_storage;

Example

Comparing the generation of a thermal plant with its maximum capacity:

thermal = Thermal();

thermal_gen = thermal:load("gerter");
thermal_cap = thermal:load("potter");

gen_gt_cap = thermal_gen:convert("MW"):gt(thermal_cap);

Note that, since the generation data is in GWh and the capacity in MW, a unit conversion is needed to compare them.

Example

Getting the highest total generated energy per type of plant:

thermal = Thermal();
hydro = Hydro();

gerter = thermal:load("gerter");
gerhid = hydro:load("gerhid");

total_gerter = gerter:aggregate_agents(BY_SUM(), "Total Thermal Gen");
total_gerhid = gerhid:aggregate_agents(BY_SUM(), "Total Hydro Gen");

max_generation =  max(total_gerter, total_gerhid);

Thermal and hydro generation are not directly compared. To compare them, we first need to aggregate the agents to obtain only one representative agent containing generation per block, scenario, and stage in each data set. Then, we can compare them.


All the above-mentioned binary expressions follow the same rules to define the resulting output's stages, scenarios, blocks, and agents.

Stages and Scenarios

If one expression has n stages, and the other has only 1, the result will have n stages. If the exp1 has n1 stages and exp2 has n2, the resulting expression will have n3 as the minimum number of stages between the two. In other words, n3 = min{n1, n2}. If both expressions have 1 stage, the result will naturally have also 1. The same rule is applied to the scenarios. The table below summarizes the explanation:

exp1 exp2 exp
1 1 1
n1 1 n1
1 n2 n2
n1 n2 min{n1,n2}

Block and Hours

The following table describes the blocks and hours rules. The only operation that is not allowed is mixing expressions that vary per block and hour.

exp1 or exp2 exp
none / none none
block / none block
block / block block
hour / none hour
hour / hour hour
block / hour

Ternary Expressions

The table below presents the ifelse. Likewise, the above-defined operators, the ifelse follow dataframe rules, doing element-wise operations. If the element of exp1 is true, exp2 is the result, otherwise, it is exp3.

Operator Syntax
Conditional exp = ifelse(exp1, exp2, exp3)

Example

In the example below, if the thermal generation is greater than zero, 1 is returned; otherwise, 0 is returned.

thermal = Thermal();

gerter = thermal:load("gerter");

gerter_gt_zero = ifelse(gerter:gt(0.0), 1, 0);

Unit Conversion

The units conversion follows the International System of Units (SI), based on the 2019 redefinition. The PSRIO will perform a multi-step process with all the expressions inputs, producing a conversion factor with the desired unit.

Operator Syntax
Unit Conversion exp = exp1:convert("unit")

Example

hydro = Hydro();
fprodt = hydro:load("fprodt");
    
pothid = min(hydro.max_turbining_outflow * fprodt, hydro.max_generation_available);

In this example we have two inputs with different units: hydro.max_turbining_outflow [m3/s] and fprodt [MW/(m3/s)]. The pothid output will be the multiplication: [m3/s] × [MW/(m3/s)] = 1.0 × [MW].

Example

renewable = Renewable();
gergnd = renewable:load("gergnd");
vergnd = renewable:load("vergnd");
    
captured_prices = (gergnd * vergnd) / (gergnd + vergnd);

The unit conversion output of Example 2 is ([GWh] × [GWh]) / ([GWh] + [GWh]) = 1.0 × [GWh]

Example

thermal = Thermal();
fuel = Fuel();
    
cinte1 = (thermal.specific_consumption_segment_1 * (thermal.transport_cost + fuel.cost) + thermal.om_cost);

The unit conversion output of Example 3 is [gal/MWh] × ([$/gal] + [$/gal]) + [$/MWh] = 1.0 × [$/MWh]

Example 4

hydro = Hydro();
volfin = hydro:load("volfin");
fprodtac = hydro:load("fprodtac");
    
eneemb = ((volfin - hydro.min_storage) * fprodtac):convert("GWh");

The unit conversion output of Example 4 is ([hm3] - [hm3]) × [MW/(m3/s)] = 0.27 × [GWh]

Aggregate Functions

Syntax
BY_SUM()
BY_MULTIPLICATION()
BY_AVERAGE()
BY_CVAR_L(number)
BY_CVAR_R(number)
BY_STDDEV()
BY_REPEATING()
BY_FIRST_VALUE()
BY_ORDER(number)
BY_LAST_VALUE()
BY_MAX()
BY_MAX_INDEX()
BY_MIN()
BY_MIN_INDEX()
BY_KTH_LARGEST(number)
BY_KTH_LARGEST_INDEX(number)
BY_KTH_SMALLEST(number)
BY_KTH_SMALLEST_INDEX(number)
BY_PERCENTILE(number)
BY_PERCENTILE_INDEX(number)

Dimensions: Scenarios

Aggregate Scenarios

$$ \operatorname{exp}=\operatorname{exp1:aggregate\_scenarios}(\operatorname{f}) $$

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_avg = cmgdem:aggregate_scenarios(BY_AVERAGE());
cmgdem_p90 = cmgdem:aggregate_scenarios(BY_PERCENTILE(90));

Aggregate Selected Scenarios

$$ \operatorname{exp}=\operatorname{exp1:aggregate\_scenarios}(\operatorname{f}, \{\operatorname{int}, \operatorname{int}, ...\}) $$

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_max = cmgdem:aggregate_scenarios(BY_MAX(), {1, 2, 3, 4, 5});

Select One Scenario

$$ \operatorname{exp}=\operatorname{exp1:select\_scenario}(\operatorname{int}) $$

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_scenario32 = cmgdem:select_scenario(32);

Select Multiple Scenarios

$$ \operatorname{exp}=\operatorname{exp1:select\_scenarios}(\{\operatorname{int}, \operatorname{int}, ...\}) $$

Select Scenarios Range

$$ \operatorname{exp}=\operatorname{exp1:select\_scenarios}(\operatorname{int}, \operatorname{int}) $$

Remove Multiple Scenarios

$$ \operatorname{exp}=\operatorname{exp1:remove\_scenarios}(\{\operatorname{int}, \operatorname{int}, ...\}) $$

Dimensions: Blocks/Hours

Aggregate Blocks/Hours

$$ \operatorname{exp}=\operatorname{exp1:aggregate\_blocks}(\operatorname{f}) $$

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_agg = cmgdem:aggregate_blocks(BY_AVERAGE());

renewable = Renewable();
gergnd = renewable:load("gergnd");
gergnd_agg = gergnd:aggregate_blocks(BY_SUM());

Select One Block/Hour

$$ \operatorname{exp}=\operatorname{exp1:select\_block}(\operatorname{int}) $$

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_block21 = cmgdem:select_block(21);

Map Blocks into Hours

$$ \operatorname{exp}=\operatorname{exp1:to\_hour}(\operatorname{f}) $$

Where f is BY_AVERAGE() or BY_REPEATING().

Example

system = System();
cmgdem_block = thermal:load("cmgdem");
cmgdem_hourly = cmgdem_block:to_hour(BY_REPEATING());

Map Hours into Blocks

$$ \operatorname{exp}=\operatorname{exp1:to\_block}(\operatorname{f}) $$

Where f is BY_AVERAGE() or BY_SUM().

Example

thermal = Thermal();
gerter_hourly = thermal:load("gerter");
gerter_block = gerter_hourly:to_block(BY_SUM());

Dimensions: Stages

Aggregate Stages

$$ \operatorname{exp}=\operatorname{exp1:aggregate\_stages}(\operatorname{f}) $$

Aggregate Stages into a Profile

$$ \operatorname{exp}=\operatorname{exp1:aggregate\_stages}(\operatorname{f}, \operatorname{profile}) $$

Where profile is the following enumerate:

Profiles
Profile.STAGE
Profile.WEEK
Profile.MONTH
Profile.QUARTER
Profile.YEAR
Profile.PER_WEEK
Profile.PER_MONTH
Profile.PER_QUARTER
Profile.PER_YEAR

Profile.STAGE

The Profile.STAGE is the default value to characterize the aggregation if the user does not inform any profile. The data associated with each stage of the study horizon is aggregated.

exp1 exp (Profile.STAGE)
n (daily) 1 (daily)
n (weekly) 1 (weekly)
n (monthly) 1 (monthly)
n (yearly) 1 (yearly)

Profile.WEEK and Profile.PER_WEEK

When the data has a daily resolution and the aggregation profile is Profile.WEEK, PSRIO will aggregate the data for each day of the weeks in the study period, i.e., that data regarding all Mondays in the data set will be aggregated into one value and the same for Tuesday, Wednesday, and so on. With a daily resolution and the aggregation Profile.PER_WEEK, PSRIO will aggregate the data related to each week of the study.

When the data is weekly and we request is Profile.WEEK, the data associated with each week is aggregated in one week. If the request is the aggregation Profile.PER_WEEK, PSRIO will do nothing and the data will remains the same.

These aggregation profiles are not defined for monthly and yearly resolution data.

exp1 exp (Profile.WEEK) exp (Profile.PER_WEEK)
n (daily) 7 (daily) n/7 (weekly)
n (weekly) 1 (weekly) n (weekly)
n (monthly)
n (yearly)

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_agg = cmgdem:aggregate_blocks(BY_AVERAGE(), profile.WEEK);

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_agg = cmgdem:aggregate_blocks(BY_AVERAGE(), profile.PER_WEEK);

Profile.MONTH and Profile.PER_MONTH

Similar to Profile.WEEK and Profile.PER_WEEK, when the data has daily discretization and we request Profile.MONTH, PSRIO will aggregate the data for each day of the months in the study period. For example, PSRIO will aggregate all 1st days of each month and the same for the others months. If we request the Profile.PER_MONTH, PSRIO will aggregate the data related to each month.

When the data has month-level discretization and we request Profile.MONTH, PSRIO will aggregate the data associated with each month. If we request Profile.PER_MONTH, nothing is done to the data.

These aggregation profiles are not defined for weekly and yearly resolution data.

exp1 exp (Profile.MONTH) exp (Profile.PER_MONTH)
n (daily) 31 (daily) n/~30 (monthly)
n (weekly)
n (monthly) 1 (monthly) n (monthly)
n (yearly)

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_agg = cmgdem:aggregate_blocks(BY_AVERAGE(), profile.MONTH);

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_agg = cmgdem:aggregate_blocks(BY_AVERAGE(), profile.PER_MONTH);

Profile.YEAR and Profile.PER_YEAR

When the data has a daily resolution and we request a Profile.YEAR, the data related to each day of years is aggregated. For example, PSRIO will aggregate all January 1st days in the study period, which is done for the other days of the year. If we request a Profile.PER_YEAR, the data associated with each year is aggregated. The same happens when the data has week, month, or year-level resolution if Profile.PER_YEAR is selected.

If the data has a year resolution and profile.YEAR is selected. the data related to the same year is aggregated.

The profile.YEAR profile is not defined for weekly and monthly resolution data.

exp1 exp (Profile.YEAR) exp (Profile.PER_YEAR)
n (daily) 365 (daily) n/365 (yearly)
n (weekly) 52 (weekly) n/52 (yearly)
n (monthly) 12 (monthly) n/12 (yearly)
n (yearly) 1 (yearly) 1 (yearly)

Example

system = System();
defcit = system:load("defcit");
defcit_per_year = defcit:aggregate_stages(BY_SUM(), Profile.YEAR);

Example

system = System()
defcit = system:load("defcit")
defcit_per_year = defcit:aggregate_stages(BY_SUM(), Profile.PER_YEAR);

Select One Stage

$$ \operatorname{exp}=\operatorname{exp1:select\_stage}(\operatorname{int}) $$

generic = Generic();
objcop = generic:load("objcop");
objcop_1st_stage = objcop:select_stage(1);

Select the First Stage

$$ \operatorname{exp}=\operatorname{exp1:select\_first\_stage}(\operatorname{int}) $$

generic = Generic();
objcop = generic:load("objcop");
objcop_2nd_to_n_stage = objcop:select_first_stage(2);

Select the Last Stage

$$ \operatorname{exp}=\operatorname{exp1:select\_last\_stage}(\operatorname{int}) $$

generic = Generic();
objcop = generic:load("objcop");
objcop_1st_to_2nd_stage = objcop:select_last_stage(2);

Select the First and the Last Stages

$$ \operatorname{exp}=\operatorname{exp1:select\_stages}(\operatorname{int}, \operatorname{int}) $$

generic = Generic();
objcop = generic:load("objcop");
objcop_2nd_to_4th_stage = objcop:select_stages(2, 4);

Select Stages by a First and a Last Years

$$ \operatorname{exp}=\operatorname{exp1:select\_stages\_by\_year}(\operatorname{int}, \operatorname{int}) $$

generic = Generic();
objcop = generic:load("objcop");
objcop_2030_2040 = objcop:select_stages_by_year(2030, 2040);

Select Stages by a Year

$$ \operatorname{exp}=\operatorname{exp1:select\_stages\_by\_year}(\operatorname{int}) $$

generic = Generic();
objcop = generic:load("objcop");
objcop_2035 = objcop:select_stages_by_year(2035);

Reshape Stages

$$ \operatorname{exp}=\operatorname{exp1:reshape\_stages}(\operatorname{Profile.DAILY}) $$

For an hourly represented data, the stage resolution can changed from a week, month or year level to a daily one using that method

exp1 exp
n (daily-hourly) n (daily-hourly)
n (weekly-hourly) 7n (daily-hourly)
n (monthly-hourly) ~30n (daily-hourly)
n (yearly-hourly) 365n (daily-hourly)

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter_daily = gerter:reshape_stages(Profile.DAILY);

Dimensions: Agents

Aggregate All Agents

$$ \operatorname{exp}=\operatorname{exp1:aggregate\_agents}(\operatorname{f}, \operatorname{string}) $$

Example

hydro = Hydro();
gerhid = hydro:load("gerhid");
gerhid_sum = gerhid:aggregate_agents(BY_SUM(), "Total Hydro");

Aggregate Agents into Collection

$$ \operatorname{exp}=\operatorname{exp1:aggregate\_agents}(\operatorname{f}, \operatorname{collection}) $$

Where collection is Collection Enumerate.

Example

hydro = Hydro();
gerhid = hydro:load("gerhid");
gerhid_systems = gerhid:aggregate_agents(BY_SUM(), Collection.SYSTEM);
gerhid_buses = gerhid:aggregate_agents(BY_SUM(), Collection.BUSES);

Select One Agent by Name or Index

$$ \operatorname{exp}=\operatorname{exp1:select\_agent}(\text{string or int}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter_t1 = gerter:select_agent("Thermal 1");
gerter_t2 = gerter:select_agent(2);

Select Mulitple Agents by Names or Indices

$$ \operatorname{exp}=\operatorname{exp1:select\_agents}(\{\text{string or int}, \text{string or int}, ...\}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter_t1_and_t2 = gerter:select_agents({"Thermal 1", 2});

Select Agents within a Collection

$$ \operatorname{exp}=\operatorname{exp1:select\_agents}(\operatorname{collection}) $$

Where collection is Collection Enumerate.

Example

expansion_project = ExpansionProject()
outidec = expansion_project:load("outidec");
outidec_dclinks = outidec:select_agents(Collection.DCLINK);

Select Agents with a Query

$$ \operatorname{exp}=\operatorname{exp1:select\_agents}(\operatorname{string}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
non_zero_gerter = gerter:select_agents(gerter:ne(0));

Remove One Agent by Name or Index

$$ \operatorname{exp}=\operatorname{exp1:remove\_agent}(\text{string or int}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter_t2_and_t3 = gerter:remove_agent("Thermal 1");
gerter_t1_and_t2 = gerter:remove_agent(3);

Remove Mulitple Agents by Names or Indices

$$ \operatorname{exp}=\operatorname{exp1:remove\_agents}(\{\text{string or int}, \text{string or int}, ...\}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter_t1 = gerter:remove_agents({"Thermal 2", 3});

Rename One Agent

$$ \operatorname{exp}=\operatorname{exp1:rename\_agent}(\text{string}) $$

Rename Mulitple Agents with One Name

$$ \operatorname{exp}=\operatorname{exp1:rename\_agents}(\text{string}) $$

Rename Mulitple Agents with Mutiple Names

$$ \operatorname{exp}=\operatorname{exp1:rename\_agents}(\{\text{string}, \text{string}, ...\}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter_renamed = gerter:rename_agents({"T1", "T2", "T3"});

Rename Mulitple Agents by Adding a Suffix

$$ \operatorname{exp}=\operatorname{exp1:add\_suffix}(\text{string}) $$

Rename Mulitple Agents by Adding a Prefix

$$ \operatorname{exp}=\operatorname{exp1:add\_prefix}(\text{string}) $$

Concatenate Agents

$$ \operatorname{exp}=\operatorname{concatenate}(\{\operatorname{exp1}, \operatorname{exp2}, ...\}) $$

Example 1

hydro = Hydro();
thermal = Thermal();
renewable = Renewable();

gerhid = hydro:load("gerhid");
gerter = thermal:load("gerter");
gergnd = renewable:load("gergnd");
generation = concatenate(gerhid, gerter, gergnd);

Dimensions: Others

Moving Function

$$ \operatorname{exp}=\operatorname{exp1:moving}(\operatorname{f}, \operatorname{int}) $$

Example

system = System();
cmgdem = system:load("cmgdem");
cmgdem_12months = cmgdem:moving(BY_AVERAGE(), 12);

Saving

Save Options

Description Syntax Default
Save output as BIN/HDR bin true
Save output as CSV csv false
Save output as DAT dat false

Save

$$ \operatorname{exp1:save}(\operatorname{string}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter:save("example");

Save with Options

$$ \operatorname{exp1:save}(\operatorname{string}, \{\operatorname{options}\}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
gerter:save("example", { csv = true });

Save and Load into an Expression

$$ \operatorname{exp}=\operatorname{exp1:save\_and\_load}(\operatorname{string}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
example = gerter:save_and_load("example");

Save with Options and Load into an Expression

$$ \operatorname{exp}=\operatorname{exp1:save\_and\_load}(\operatorname{string}, \{\operatorname{options}\}) $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
example = gerter:save_and_load("example", { csv = true });

Save Cache

$$ \operatorname{exp}=\operatorname{exp1:save\_cache}() $$

Example

thermal = Thermal();
gerter = thermal:load("gerter");
cache = gerter:save_cache();

Dashboard

PSRIO provides a set of methods that allow users to analyze their results simply and easily.

Create a Tab

$$ \operatorname{tab}=\operatorname{Tab}(\text{string}) $$

Example

tab = Tab("Tab title");

Set the Tab Icon

$$ \operatorname{tab:set\_icon}(\text{string}) $$

https://lucide.dev/

Example

tab = Tab("Tab title");
tab:set_icon("home");

Disable the Tab

$$ \operatorname{tab:set\_disabled}() $$

Example

tab = Tab("Tab title");
tab:set_disabled();

Set the Collapse Flag

$$ \operatorname{tab:set\_collapsed}(\text{bool}) $$

Example

tab = Tab("Tab title");
tab:set_collapsed(true);

Push a Chart to a Tab

$$ \operatorname{tab:push}(\text{chart}) $$

Example

tab = Tab("Tab title");
tab:push("home");

Push a Markdown to a Tab

$$ \operatorname{tab:push}(\text{string}) $$

Example

tab = Tab("Tab title");
tab:push("# Heading 1");

Push a Tab to a Tab

$$ \operatorname{tab:push}(\text{tab}) $$

Example

tab = Tab("Tab title");
subtab = Tab("Sub Tab title");
tab:push(subtab);

Save a Tab

$$ \operatorname{tab:save}(\text{string}) $$

Example

tab = Tab("Tab title");
tab:save("example");

Create a Dashboard

$$ \operatorname{dashboard}=\operatorname{Dashboard}() $$

Example

dashboard = Dashboard();

Push a Tab to a Dashboard

$$ \operatorname{dashboard:push}(\operatorname{tab}) $$

Example

tab = Tab("Tab title");
dashboard = Dashboard();
dashboard:push(tab);

Save a Dashboard

$$ \operatorname{dashboard:save}(\text{string}) $$

Example

dashboard = Dashboard();
dashboard:save("example");

Charts

Charts are the essential elements of dashboards. They provide a visual way of analyzing the data generated by PSR models. To create a chart object, we should use one of the following methods:

Method Syntax
Create a Chart chart = Chart()
Create a Chart with a title chart = Chart("title")
Create a Chart with a title and a subtitle chart = Chart("title", "subtitle")

After creating the chart object, we can start to push the data in it. PSRIO gives multiple options for chart types, which can be seen below:

Method Syntax
Line chart:add_line(exp)
Spline chart:add_spline(exp)
Column chart:add_column(exp)
Column Stacking chart:add_column_stacking(exp)
Column Percent chart:add_column_percent(exp)
Categories chart:add_categories(exp, "label")
Area chart:add_area(exp)
Area Stacking chart:add_area_stacking(exp)
Area Percent chart:add_area_percent(exp)
Area Range chart:add_area_range(exp1, exp2)
Area Spline chart:add_area_spline(exp)
Area Spline Stacking chart:add_area_spline_stacking(exp)
Area Spline Percent chart:add_area_spline_percent(exp)
Area Spline Range chart:add_area_spline_range(exp1, exp2)
Pie chart:add_pie(exp)
Histogram chart:add_histogram(exp)
Heatmap (Hourly) chart:add_heatmap_hourly(exp)
Scatter chart:add_scatter(exp1, exp2, "label")
Probability of Exceedance chart:add_probability_of_exceedance(exp)

Examples

local thermal = Thermal();
local gerter = thermal:load("gerter");

local hydro = Hydro();
local gerhid = hydro:load("gerhid");

local renewable = Renewable();
local gergnd = renewable:load("gergnd");

local tab = Tab("Tutorial");

-- push charts to tab here --

tab:save("tutorial");

Example: Line

local chart = Chart("Line");
chart:add_line(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Spline

local chart = Chart("Spline");
chart:add_spline(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Column

local chart = Chart("Column");
chart:add_column(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Categories

local chart = Chart("Categories");
chart:add_categories(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):aggregate_agents(BY_SUM(), Collection.SYSTEM), "Thermal");
chart:add_categories(gerhid:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):aggregate_agents(BY_SUM(), Collection.SYSTEM), "Hydro");
chart:add_categories(gergnd:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):aggregate_agents(BY_SUM(), Collection.SYSTEM), "Renewable");
tab:push(chart);

Example: Column Stacking

local chart = Chart("Column Stacking");
chart:add_column(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Column Percent

local chart = Chart("Column Percent");
chart:add_column(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Area

local chart = Chart("Area");
chart:add_area(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Area Stacking

local chart = Chart("Area Stacking");
chart:add_area_stacking(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Area Percent

local chart = Chart("Area Percent");
chart:add_area_percent(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Area Range

local chart = Chart("Area Range");
chart:add_area_range(
    gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_PERCENTILE(10)):aggregate_agents(BY_SUM(), "p10"),
    gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_PERCENTILE(90)):aggregate_agents(BY_SUM(), "p90")
);
tab:push(chart);

Example: Area Spline

local chart = Chart("Area Spline");
chart:add_area_spline(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Area Spline Stacking

local chart = Chart("Area Spline Stacking");
chart:add_area_spline_stacking(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Area Spline Percent

local chart = Chart("Area Spline Percent");
chart:add_area_spline_percent(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):select_largest_agents(5));
tab:push(chart);

Example: Area Spline Range

local chart = Chart("Area Spline Range");
chart:add_area_spline_range(
    gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_PERCENTILE(10)):aggregate_agents(BY_SUM(), "p10"),
    gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_PERCENTILE(90)):aggregate_agents(BY_SUM(), "p90")
);
tab:push(chart);

Example: Pie

local chart = Chart("Pie");
chart:add_pie(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):aggregate_stages(BY_SUM()));
tab:push(chart);

Example: Histogram

local chart = Chart("Histogram");
chart:add_histogram(gerter:aggregate_blocks(BY_SUM()):aggregate_agents(BY_SUM(), "Total"));
tab:push(chart);

Example: Probability of Exceedance

local chart = Chart("Probability of Exceedance");
chart:add_probability_of_exceedance(gerter:aggregate_blocks(BY_SUM()));
tab:push(chart);

Example: Multiple (Line and Area Range)

local chart = Chart("Multiple (Line and Area Range)");
chart:add_area_range(
    gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_PERCENTILE(10)):aggregate_agents(BY_SUM(), "p10"),
    gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_PERCENTILE(90)):aggregate_agents(BY_SUM(), "p90")
);
chart:add_line(
    gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):aggregate_agents(BY_SUM(), "avg")
);
tab:push(chart);

Example: Multiple (Line and Column)

local chart = Chart("Multiple (Line and Column)");
chart:add_column(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):aggregate_agents(BY_SUM(), "column"));
chart:add_line(gerter:aggregate_blocks(BY_SUM()):aggregate_scenarios(BY_AVERAGE()):aggregate_agents(BY_SUM(), "line"));
tab:push(chart);

Chart Attributes

Some methods accept arguments to customize your chart, i.e., change its color, define the limits on the y axis, etc.

The arguments are declared in tables inside the methods, as shown in the following example:
chart:add_line(exp, {lineWidth = 2}). The user can also define multiple arguments as follows chart:add_line(exp, {lineWidth = 2, color = "#8583ff"}). The following table describes the available chart arguments:

Layer Attributes

Argument Description
color = ? e.g. #ff0000 or red
lineWidth = ? 0, 1, 2, ...
marker = {enabled = ?} true or false
marker = {symbol = ?} circle, square, diamond, triangle or triangle-down
marker = {radius = ?} 0, 1, 2, ...
dataLabels = {enabled = ?} true or false
dataLabels = {format = ?} e.g. "{point.y:,.0f}"
fillOpacity = ? 0.0 to 1.0
dashStyle = ? solid, shortdash, shortdot, shortdashdot, shortdashdotdot, dot, dash, longdash, dashdot, longdashdot, and longdashdotdo
stops = { {?, ?}, ... } e.g. stops = {{0.0, "#3060cf"}, {0.5, "#fffbbc"}, {0.9, '#c4463a"}}
showInLegend = ? true or false

X Axis Attributes

Argument Description
xGridLineWidth = ? 0, 1, 2, ...
xAllowDecimals = ? true or false

Y Axis Attributes

Argument Description
yAllowDecimals = ? true or false

Examples

local system = System();
local cmgdem = system:load("cmgdem"):aggregate_scenarios(BY_AVERAGE());
local cmgdem_per_agent = cmgdem:aggregate_blocks(BY_AVERAGE());
local cmgdem_per_block = cmgdem:aggregate_agents(BY_SUM(), "Load Marginal Cost");
local cmgdem_aggregated = cmgdem:aggregate_blocks(BY_AVERAGE()):aggregate_agents(BY_SUM(), "Load Marginal Cost");

local tab = Tab("Tutorial");

-- push charts to tab here --

tab:save("tutorial");

Example: Single Color

local chart = Chart("Single Color");
chart:add_line(cmgdem_per_agent, {color = "#8583ff"});
tab:push(chart);

Example: Multiple Colors

local chart = Chart("Multiple Colors");
chart:add_line(cmgdem_per_agent, {color = {"#9b5de5", "#f15bb5", "#fee440", "#00bbf9", "#00f5d4"}});
tab:push(chart);

Example: Multiple Colors (interpolate)

local chart = Chart("Multiple Colors (interpolate)");
chart:add_line(cmgdem_per_agent, {color = PSR.interpolate_colors("#ff0000", "#00ff00", cmgdem_per_agent:agents_size())});
tab:push(chart);

Example: Line Width

local chart = Chart("Line Width");
chart:add_line(cmgdem_aggregated, {lineWidth = 8});
tab:push(chart);

Example: Marker

local chart = Chart("Marker");
chart:add_line(cmgdem_aggregated, {marker = {enabled = true, symbol = "circle", radius = 2}});
tab:push(chart);

Example: Data Labels

local chart = Chart("Data Labels");
chart:add_line(cmgdem_aggregated, {dataLabels = {enabled = true, format = "{point.y:,.0f}"} });
tab:push(chart);

Example: xGrid Line Width

local chart = Chart("xGrid Line Width");
chart:add_line(cmgdem_aggregated, {xGridLineWidth = 1});
tab:push(chart);

Example: Fill Opacity

local chart = Chart("Fill Opacity");
chart:add_area(cmgdem_aggregated, {fillOpacity = 1});
tab:push(chart);

Example: Dash Style

local chart = Chart("Dash Style");
chart:add_line(cmgdem_aggregated, {dashStyle = "dash"});
tab:push(chart);

Example: Show In Legend

local chart = Chart("Show In Legend");
chart:add_line(cmgdem_aggregated, {showInLegend = false});
tab:push(chart);

Markdown

https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax

Headers

# h1 Heading
## h2 Heading
### h3 Heading
#### h4 Heading
##### h5 Heading
###### h6 Heading

h1 Heading

h2 Heading

h3 Heading

h4 Heading

h5 Heading
h6 Heading

Horizontal Rules

___

---

***



Emphasis

*This text will be italic*
_This will also be italic_
**This text will be bold**
__This will also be bold__
*You **can** combine them*
~~This text will be strikethrough~~

This text will be italic
This will also be italic
This text will be bold
This will also be bold
You can combine them
This text will be strikethrough

Unordered List

+ Create a list by starting a line with `+`, `-`, or `*`
+ Sub-lists are made by indenting 2 spaces:
  - Marker character change forces new list start:
    * Ac tristique libero volutpat at
    + Facilisis in pretium nisl aliquet
    - Nulla volutpat aliquam velit
+ Very easy!
  • Create a list by starting a line with +, -, or *
  • Sub-lists are made by indenting 2 spaces:
    • Marker character change forces new list start:
      • Ac tristique libero volutpat at
      • Facilisis in pretium nisl aliquet
      • Nulla volutpat aliquam velit
  • Very easy!

Ordered List

1. Item 1
2. Item 2
3. Item 3
    * Item 3a
    * Item 3b
  1. Item 1
  2. Item 2
  3. Item 3
    • Item 3a
    • Item 3b

Links

[link text](http://psr-inc.com)

[link with tooltip](http://psr-inc.com/ "tooltip text!")

Autoconverted link http://psr-inc.com (enable linkify to see)

link text

link with tooltip

Autoconverted link http://psr-inc.com (enable linkify to see)

Blockquotes

> Blockquotes can also be nested...
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.

Blockquotes can also be nested...

...by using additional greater-than signs right next to each other...

...or with spaces between arrows.

Code

Inline `code`

    // Indented code
    line 1 of code
    line 2 of code
    line 3 of code

```
Block code
```

Inline code

// Indented code
line 1 of code
line 2 of code
line 3 of code
Block code

Tables

| Option | Description                                                               |
|--------|---------------------------------------------------------------------------|
| data   | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default.    |
| ext    | extension to be used for dest files.                                      |
Option Description
data path to data files to supply the data that will be passed into templates.
engine engine to be used for processing templates. Handlebars is the default.
ext extension to be used for dest files.

Images

![Minion](https://octodex.github.com/images/minion.png)

![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")

![Alt text][id]

[id]: https://octodex.github.com/images/dojocat.jpg  "The Dojocat"

Minion

Stormtroopocat

Alt text

Equations

https://katex.org/docs/supported.html

dashboard:push("$$ x^2 + y^2 = z^2 $$");

$$ x^2 + y^2 = z^2 $$

dashboard:push("$$ \sqrt{x^2+1} $$");

$$ \sqrt{x^2+1} $$

dashboard:push("$$ \int\limits_0^1 x^2 + y^2 dx $$");

$$ \int\limits_0^1 x^2 + y^2 dx $$

dashboard:push("$$ \int_0^1 x^2 + y^2 dx $$");

$$ \int_0^1 x^2 + y^2 dx $$

dashboard:push("$$ a_1^2 + a_2^2 = a_3^2 $$");

$$ a_1^2 + a_2^2 = a_3^2 $$

dashboard:push("$$ \sum_{i=1}^{\infty} \frac{1}{n^s} = \prod_p \frac{1}{1 - p^{-s}} $$");

$$ \sum_{i=1}^{\infty} \frac{1}{n^s} = \prod_p \frac{1}{1 - p^{-s}} $$

dashboard:push("$$ \begin{matrix} a & b \\\\ c & d \end{matrix} $$");

$$ \begin{matrix} a & b \\ c & d \end{matrix} $$

Base

PSRIO Base is a library embedded with PSRIO that provides implementations of PSR model outputs. Instead of rewriting recipes or searching for written similar scripts, we can use the functions provided by PSRIO Base. We use the function require to load the scripts defined in the PSRIO Base.

Example: Useful Storage

sddp/useful_storage:

useful_storage = require("sddp/useful_storage");
useful_storage():save("useful_storage");

Example: Circuit Loading

sddp/usecir:

usecir = require("sddp/usecir");
usecir():save("usecir");

Example: Hydro Generation per Bus

sddp/gerhid_per_bus:

gerhid_per_bus = require("sddp/gerhid_per_bus");
gerhid_per_bus():save("gerhid_per_bus");

Example: Deficit Risk per Year

sddp/defcit_risk:

defcit_risk = require("sddp/defcit_risk");
defcit_risk():save("defcit_risk");

Example: Load Marginal Cost Report

sddp-reports/sddpcmgd:

sddpcmgd = require("sddp-reports/sddpcmgd");
sddpcmgd():save("sddpcmgd");

Example: Averaged Load Marginal Cost Report

sddp-reports/sddpcmga:

sddpcmga = require("sddp-reports/sddpcmga");
sddpcmga():save("sddpcmga");

Time Series

Constructor

$$ \operatorname{timeseries}=\operatorname{TimeSeries}() $$

Save

$$ \operatorname{timeseries:save}(\operatorname{string}) $$

Add Expression

$$ \operatorname{timeseries:add}(\operatorname{exp}) $$

Example

generic = Generic();
cmgdem = generic:load("cmgdem");
cmgdem = cmgdem:aggregate_scenarios(BY_AVERAGE());
cmgdem = cmgdem:aggregate_blocks(BY_AVERAGE());
cmgdem = cmgdem:aggregate_agents(BY_SUM(), "Load Marginal Cost");
cmgdem = cmgdem:add_suffix(" (" .. cmgdem:unit() .. ")");

gerter = generic:load("gerter");
gerter = gerter:aggregate_scenarios(BY_AVERAGE());
gerter = gerter:aggregate_blocks(BY_SUM());
gerter = gerter:aggregate_agents(BY_SUM(), "Thermal Generation");
gerter = gerter:add_suffix(" (" .. gerter:unit() .. ")");

timeseries = TimeSeries();
timeseries:add(gerter);
timeseries:add(cmgdem);
timeseries:save("example");

example.csv:

Datetime        , Thermal Generation (GWh), Load Marginal Cost ($/MWh)
2019-04-09 00:00, 99.707000               , 167.770000
2019-04-16 00:00, 97.036000               , 181.197000
2019-04-23 00:00, 117.266000              , 205.882000
2019-04-30 00:00, 117.625000              , 230.813000
2019-05-07 00:00, 122.800000              , 242.538000
2019-05-14 00:00, 140.680000              , 309.360000
...

Relational

Constructor

$$ \operatorname{relational}=\operatorname{Relational}() $$

Save

$$ \operatorname{relational:save}(\operatorname{string}) $$

Add Collection Column

$$ \operatorname{relational:add}(\operatorname{string}, \operatorname{study}, \operatorname{collection}) $$

Example

study = Study();

relational = Relational();
relational:add("hydro", study, Collection.HYDRO);
relational:save("example.csv");

example.csv:

hydro
La Estrella
Los Valles
Fortuna
Bayano
Estí 
Esperanza
Concepción
...

Add Relationship Between Collections Column

$$ \operatorname{relational:add}(\operatorname{string}, \operatorname{study}, \operatorname{collection}, \operatorname{collection}) $$

Example

study = Study();

relational = Relational();
relational:add("hydro", study, Collection.HYDRO);
relational:add("system", study, Collection.HYDRO, Collection.SYSTEM);
relational:save("example.csv");

example.csv:

hydro,       system
La Estrella, PANAMA
Los Valles , PANAMA
Fortuna,     PANAMA
Bayano,      BAYSIS
Estí,        PANAMA
Esperanza,   PANAMA
Concepción,  PANAMA
...

Add Expression Column

$$ \operatorname{relational:add}(\operatorname{string}, \operatorname{expression}) $$

Example

renewable = Renewable();
study = Study();

relational = Relational();
relational:add("Name", study, Collection.RENEWABLE);
relational:add("System", study, Collection.RENEWABLE, Collection.SYSTEM);
relational:add("Code", renewable.code);
relational:add("Tech Type", renewable.tech_type);
relational:save("example.csv");

example.csv:

Name,         System, Code, Tech Type
Rosa de L V,  PANAMA, 1.0,  0.0
Marañon,      PANAMA, 2.0,  0.0
Nuevo Chagre, PANAMA, 3.0,  0.0
Portobello,   PANAMA, 4.0,  0.0
Sarigua,      PANAMA, 5.0,  0.0
Nuevo Ch 2,   PANAMA, 6.0,  0.0
...