BOPTestAPI.jl

Quickstart

Installation

BOPTestAPI is available from the Julia general registry. Thus, you can add it like

import Pkg; Pkg.add("BOPTestAPI")

Self-contained example

See further below for some explanations.

using BOPTestAPI
using DataFrames
using Plots

dt = 900.0 # time step in seconds
testcase = "bestest_hydronic"

plant = BOPTestPlant("http://api.boptest.net", testcase, dt = dt)

# Get available measurement points
mpts = measurement_points(plant)
MinimumUnitDescriptionNameMaximum
missingsSolar timeweaStareaWeaSolTimymissing
missingWPump electrical powerreaPPum_ymissing
missing1Sky cover measurementweaStareaWeaNTotymissing

(Output truncated)

N = 100

# Get forecast data as well (for plotting later)
fc = getforecasts(plant, N * dt, dt)

# Run N time steps of baseline control
res = []
for t = 1:N
    u = Dict() # here you would put your own controller
    y = advance!(plant, u)
    push!(res, y)
end
stop!(plant)

dfres = DataFrame(res)
timereaPPum_yweaStareaWeaNTotyweaStareaWeaTBlaSkyy
900.012.250.2258.46
1800.012.250.2258.46
2700.012.250.2258.46
3600.012.250.2258.46
4500.012.250.2258.46

(Output truncated in both columns and rows)

And that's it! You successfully simulated a building HVAC system in the cloud using BOPTEST-Service. The following code will just make some plots of the results.

# Create single df with all data
df = leftjoin(dfres, fc, on = :time => :time)

pl1 = plot(
    df.time ./ 3600,
    Matrix(df[!, ["reaTRoo_y", "LowerSetp[1]"]]);
    xlabel = "t [h]",
    ylabel = "T [K]",
    labels = ["actual" "target"],
)
pl2 = plot(
    df.time ./ 3600,
    df.reaQHea_y ./ 1000;
    xlabel = "t [h]",
    ylabel = "Qdot [kW]",
    labels = "Heating"
)
plot(pl1, pl2; layout = (2, 1))
Example block output

Usage

(See also the README on Github)

The general idea is that the BOPTEST services are abstracted away as a BOPTestPlant, which only stores metadata about the plant such as the endpoints to use.

The package then defines common functions to operate on the plant, which are translated to REST API calls and the required data formattings.

Initialization

Use the BOPTestPlant or CachedBOPTestPlant constructor:

dt = 900.0 # time step in seconds

testcase = "bestest_hydronic"
plant = BOPTestPlant("http://localhost", testcase, dt = dt)

n_forecast = 24
plant_with_cache = CachedBOPTestPlant("http://api.boptest.net", testcase, n_forecast, dt = dt)

# For old BOPTEST < v0.7, use the deprecated initboptest! function
old_plant = initboptest!("http://127.0.0.1:5000", dt = dt)

The initialization functions also query and store the available signals (as DataFrame), since they are constant for a testcase. The signals are available as

  • forecast_points(plant)
  • input_points(plant)
  • measurement_points(plant)

Interaction with the plant

The package then defines common functions to operate on the plant, namely

  • getforecasts, getmeasurements to get the actual time series data for forecast or past measurements
  • getkpi to get the KPI for the test case (calculated by BOPTEST)
  • advance!, to step the plant one time step with user-specified control input
  • initialize! to re-initialize the plant
  • setscenario! to set the scenerio on an existing plant
  • stop!, to stop a test case (BOPTEST-Service only)

Querying data

The time series functions return a DataFrame with the time series. By default, a conversion to Float64 is attempted (else the datatypes would be Any). You can use the keyword argument convert_f64=false to disable conversion.

# Query forecast data for 24 hours, with 1/dt sampling frequency
# The column "Name" contains all available forecast signal names
fc_pts = forecast_points(plant)
fc = getforecasts(plant, 24*3600, dt, fc_pts.Name)

# For a CachedBOPTestPlant, the forecasts are part of the local cache
# So the following won't result in a REST API call
fc2 = forecasts(plant_with_cache)

Advancing

The advance! function requires the control inputs u as a Dict. Allowed control inputs are test case specific, but can be queried as property input_points.

ipts = input_points(plant)

# Alternative: Create a simple Dict directly
# This will by default overwrite all baseline values with the lowest allowed value
u = controlinputs(plant)

# Simulate 100 steps open-loop
res_ol = [advance!(plant, u) for _ = 1:100]
df_ol = DataFrame(res_ol)

# KPI
kpi = getkpi(plant)

Stop

Stop a test case when no longer needed:

stop!(plant)

API

Types

BOPTestAPI.BOPTestPlantType
BOPTestPlant(boptest_url, testcase[; dt, init_vals, scenario])

Initialize a testcase in BOPTEST service.

Arguments

  • boptest_url::AbstractString: URL of the BOPTEST-Service API to initialize.
  • testcase::AbstractString : Name of the test case, list here.

Keyword arguments

  • dt::Real: Time step in seconds.
  • init_vals::AbstractDict: Parameters for the initialization.
  • scenario::AbstractDict : Parameters for scenario selection.
source
BOPTestAPI.CachedBOPTestPlantType
CachedBOPTestPlant(boptest_url, testcase, N[; dt, init_vals, scenario])

[Warning: Experimental] Initialize a testcase in BOPTEST service, with a local cache.

In addition to the properties and methods of the normal BOPTestPlant, this type also stores submitted inputs, received measurements, and the current forecast. These values are updated when calling advance!.

Arguments

  • boptest_url::AbstractString: URL of the BOPTEST-Service API to initialize.
  • testcase::AbstractString: Name of the test case, list here.
  • N::Int: Forecast cache size

Keyword arguments

See the documentation for BOPTestPlant.

source

Accessors

BOPTestAPI.forecastsFunction
forecasts(p::CachedBOPTestPlant)
forecasts(p::CachedBOPTestPlant; rows, columns)

Return forecasts from current time step as DataFrame.

Use valid row and column selectors from DataFrames.jl for the keyword arguments.

source
BOPTestAPI.inputs_sentFunction
inputs_sent(p::CachedBOPTestPlant)
inputs_sent(p::CachedBOPTestPlant; rows, columns)

Return past inputs sent to the plant as DataFrame.

Note that this contains values as sent; if out of bounds, the plant might use other values. Use measurements to get a DataFrame with the actually used inputs. In case the default was used for a signal, the entry here will be missing. Use valid row and column selectors from DataFrames.jl for the keyword arguments.

source
BOPTestAPI.measurementsFunction
measurements(p::CachedBOPTestPlant)
measurements(p::CachedBOPTestPlant; rows, columns)

Return measurements as DataFrame.

Unlike getmeasurements(p, ...), this method uses the local cache. This also means that the time step corresponds to the controller time step. Use valid row and column selectors from DataFrames.jl for the keyword arguments.

source

Interaction

BOPTestAPI.initialize!Function
initialize!(api_endpoint::AbstractBOPTestEndpoint; init_vals, timeout)
initialize!(plant::AbstractBOPTestPlant; init_vals, timeout)

Arguments

Keyword arguments

  • init_vals::AbstractDict: Parameters for the initialization. Default is Dict("start_time" => 0, "warmup_period" => 0).
  • timeout::Real: Timeout for the BOPTEST-Service API, in seconds. Default is 30.

(Re-)Initialize the plant and return the payload from the BOPTEST-Service API. Also re-initializes the caches for a CachedBOPTestPlant.

source
BOPTestAPI.initboptest!Function
initboptest!(boptest_url[; dt, init_vals, scenario])

[Warning: Deprecated.] Initialize the local BOPTEST server.

Arguments

  • boptest_url::AbstractString: URL of the BOPTEST server to initialize.

Keyword arguments

  • dt::Real: Time step in seconds.
  • init_vals::AbstractDict: Parameters for the initialization.
  • scenario::AbstractDict : Parameters for scenario selection.

Return a BOPTestPlant instance, or throw an ErrorException on error.

Warning: This function is deprecated, since BOPTEST v0.7 switched to the BOPTEST-Service API. Use it for a locally deployed BOPTEST < v0.7.

source
BOPTestAPI.setscenario!Function
setscenario!(api_endpoint::AbstractBOPTestEndpoint, d; timeout)
setscenario!(plant::AbstractBOPTestPlant, d; timeout)

Arguments

  • d::AbstractDict: Parameters for scenario selection.

Keyword arguments

  • timeout::Real: Timeout for the BOPTEST-Service API, in seconds. Default is 30.

Set the scenario for a BOPTEST plant and return the selected scenario.

source
BOPTestAPI.getforecastsFunction
getforecasts(plant, horizon, interval[, points])

Query forecast from BOPTEST server and return as DataFrame.

Arguments

  • plant::AbstractBOPTestPlant : The plant to query forecast from.
  • horizon::Real : Forecast time horizon from current time step, in seconds.
  • interval::Real : Time step size for the forecast data, in seconds.
  • points::AbstractVector{AbstractString} : The forecast point names to query. Optional.

Keyword Arguments

  • convert_f64::Bool : whether to convert column types to Float64, default true. If set to false, the columns will have type Any.

Available forecast points are available using forecast_points(plant).

source
BOPTestAPI.getmeasurementsFunction
getmeasurements(plant, starttime, finaltime[, points])

Query measurements from BOPTEST server and return as DataFrame.

Arguments

  • plant::AbstractBOPTestPlant : The plant to query measurements from.
  • starttime::Real : Start time for measurements time series, in seconds.
  • finaltime::Real : Final time for measurements time series, in seconds.
  • points::AbstractVector{AbstractString} : The measurement point names to query. Optional.

Keyword Arguments

  • convert_f64::Bool : whether to convert column types to Float64, default true. If set to false, the columns will have type Any.

To obtain available points, use measurement_points(plant) and input_points(plant), which each return aDataFramewith a column:Name` that contains all available signals.

source
BOPTestAPI.getstepFunction
getstep(plant::AbstractBOPTestPlant; timeout = _DEF_TIMEOUT)

Get plant time step as Float64.

source
BOPTestAPI.advance!Function
advance!(plant::AbstractBOPTestPlant, u::AbstractDict)

Step the plant using control input u.

Returns the payload as Dict{String, Vector}.

source
BOPTestAPI.stop!Function
stop!(plant::AbstractBOPTestPlant)
stop!([base_url = "http://localhost",] testid::AbstractString)

Stop a BOPTestPlant from running.

This method does nothing for plants run in normal BOPTEST (i.e. not BOPTEST-Service).

source

Utils

BOPTestAPI.controlinputsFunction
controlinputs([f::Function, ]plant::AbstractBOPTestPlant)

Return Dict with control signals for BOPTEST.

This method calls input_points(plant) to gather available inputs, and then creates a Dict with the available inputs as keys and default values defined by function f.

f is a function that is applied to a DataFrame constructed from the input points that have a suffix "_u", i.e. control inputs. The DataFrame normally has columns :Name, :Minimum, :Maximum, :Unit, :Description.

The default for f is df -> df[!, :Minimum], i.e. use the minimum allowed input.

source