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)
Minimum | Unit | Description | Name | Maximum |
---|---|---|---|---|
missing | s | Solar time | weaStareaWeaSolTimy | missing |
missing | W | Pump electrical power | reaPPum_y | missing |
missing | 1 | Sky cover measurement | weaStareaWeaNToty | missing |
(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)
time | reaPPum_y | weaStareaWeaNToty | weaStareaWeaTBlaSkyy |
---|---|---|---|
900.0 | 12.25 | 0.2 | 258.46 |
1800.0 | 12.25 | 0.2 | 258.46 |
2700.0 | 12.25 | 0.2 | 258.46 |
3600.0 | 12.25 | 0.2 | 258.46 |
4500.0 | 12.25 | 0.2 | 258.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))
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 measurementsgetkpi
to get the KPI for the test case (calculated by BOPTEST)advance!
, to step the plant one time step with user-specified control inputinitialize!
to re-initialize the plantsetscenario!
to set the scenerio on an existing plantstop!
, 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.BOPTestPlant
— TypeBOPTestPlant(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.
BOPTestAPI.CachedBOPTestPlant
— TypeCachedBOPTestPlant(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
.
Accessors
BOPTestAPI.forecast_points
— Functionforecast_points(p::AbstractBOPTestPlant)
Return forecast points as DataFrame
.
BOPTestAPI.input_points
— Functioninput_points(p::AbstractBOPTestPlant)
Return forecast points as DataFrame
.
BOPTestAPI.measurement_points
— Functionmeasurement_points(p::AbstractBOPTestPlant)
Return measurement points as DataFrame
.
BOPTestAPI.forecasts
— Functionforecasts(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.
BOPTestAPI.inputs_sent
— Functioninputs_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.
BOPTestAPI.measurements
— Functionmeasurements(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.
Interaction
BOPTestAPI.initialize!
— Functioninitialize!(api_endpoint::AbstractBOPTestEndpoint; init_vals, timeout)
initialize!(plant::AbstractBOPTestPlant; init_vals, timeout)
Arguments
Keyword arguments
init_vals::AbstractDict
: Parameters for the initialization. Default isDict("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
.
BOPTestAPI.initboptest!
— Functioninitboptest!(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
.
BOPTestAPI.setscenario!
— Functionsetscenario!(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.
BOPTestAPI.getforecasts
— Functiongetforecasts(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 toFloat64
, defaulttrue
. If set tofalse
, the columns will have typeAny
.
Available forecast points are available using forecast_points(plant)
.
BOPTestAPI.getmeasurements
— Functiongetmeasurements(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 toFloat64
, defaulttrue
. If set tofalse
, the columns will have typeAny
.
To obtain available points, use measurement_points(plant)
and input_points(plant), which each return a
DataFramewith a column
:Name` that contains all available signals.
BOPTestAPI.getkpi
— Functiongetkpi(plant::AbstractBOPTestPlant)
Get KPI from BOPTEST server as Dict
.
BOPTestAPI.getstep
— Functiongetstep(plant::AbstractBOPTestPlant; timeout = _DEF_TIMEOUT)
Get plant time step as Float64
.
BOPTestAPI.advance!
— Functionadvance!(plant::AbstractBOPTestPlant, u::AbstractDict)
Step the plant using control input u.
Returns the payload as Dict{String, Vector}
.
BOPTestAPI.stop!
— Functionstop!(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).
Utils
BOPTestAPI.controlinputs
— Functioncontrolinputs([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.