import libraries

from besos import eppy_funcs as ef
from besos.eplus_funcs import print_available_outputs
from besos.evaluator import EvaluatorEP
from besos.objectives import MeterReader, clear_outputs
from besos.optimizer import NSGAII
from besos.parameters import FieldSelector, Parameter, RangeParameter, wwr
from besos.problem import EPProblem, Problem

Objectives and Constraints

Evaluators support two types of outputs: Objectives and Constraints. These are both made using the MeterReader and VariableReader classes. The only difference is how they are used by the problem.

First we load the EnergyPlus example file, clear any output data and define some parameters.

building = ef.get_building()
clear_outputs(building)
inputs = [
    wwr(),
    Parameter(
        FieldSelector(
            class_name="Material",
            object_name="Mass NonRes Wall Insulation",
            field_name="Thickness",
        ),
        RangeParameter(0.01, 0.99),
    ),
]

Objectives and constraints can be specified in various ways. + The most explicit is by calling the relevant constructor.

objectives = [
    MeterReader(
        key_name="Electricity:Facility", class_name="Output:Meter", frequency="Hourly"
    )
]
EPProblem(outputs=objectives)
EPProblem(outputs=[MeterReader(class_name='Output:Meter', frequency='Hourly', func=<function sum_values at 0x7f68014b4dd0>, key_name='Electricity:Facility')], minimize_outputs=[True], converters={'outputs': <class 'besos.objectives.MeterReader'>, 'constraints': <class 'besos.objectives.MeterReader'>})
  • The most concise is a list of the key_names.

The constructor has defaults, so we can often omit class_name and frequency. A list of key names will be automatically be converted by EPProblem. Meters and variables that do not have a frequency specified will default to any frequency that is already used for that output, or if none is used yet then they will use Hourly.

objectives = ["Electricity:Facility"]
EPProblem(outputs=objectives)
EPProblem(outputs=[MeterReader(class_name='Output:Meter', func=<function sum_values at 0x7f68014b4dd0>, key_name='Electricity:Facility')], minimize_outputs=[True], converters={'outputs': <class 'besos.objectives.MeterReader'>, 'constraints': <class 'besos.objectives.MeterReader'>})
  • Using Problem

If we do not need the output-reading features of meters, we can use Problem instead of EPProblem, and they will be converted to Objective objects which act as placeholders. EPProblem converts them to Meter:Reader objects. Either of these conversions can be overriden using the converters argument.

objectives = ["any", "names", "work"]
Problem(outputs=objectives)
Problem(outputs=[Objective(name='any'), Objective(name='names'), Objective(name='work')], minimize_outputs=[True, True, True], converters={'outputs': <class 'besos.IO_Objects.Objective'>, 'constraints': <class 'besos.IO_Objects.Objective'>})
  • Specifying the aggregation function

The func argument is used define how to aggregate the individual time series results. By default, all measurements are summed. If we wanted to instead minimize the variance, we can write our own aggrgation function. Here we define two electricity objectives, the first summing the hourly values and the second taking the variance.

def variance(result):
    return result.data["Value"].var()


objectives = [
    MeterReader("Electricity:Facility", name="Electricity Usage"),
    MeterReader("Electricity:Facility", func=variance, name="Electricity Variance"),
]

When we want to specify the direction of optimisation, we can use minmize_outputs (defaults to true for all objectives). Here we say we want to search for a design that has: + low electricty use (minimize objective 1 defined above) + high variability of electricity use (maximize objective 2 defined above) + less than 800 kgCO2 (constraint)

evaluator = EvaluatorEP(
    EPProblem(
        inputs=inputs,
        outputs=objectives,
        minimize_outputs=[True, True],
        constraints=["CO2:Facility"],
        constraint_bounds=["<=800"],
    ),
    building,
    out_dir="outputdir",
)
# this cell runs the optimisation
results1 = NSGAII(evaluator, evaluations=1, population_size=10)
results1
Window to Wall Ratio RangeParameter [0.01, 0.99] Electricity Usage Electricity Variance CO2:Facility violation pareto-optimal
0 0.119989 0.882785 1.743436e+09 6.988464e+14 684.446725 0 True
1 0.585907 0.287694 1.762123e+09 7.073158e+14 695.797631 0 False
2 0.931292 0.554707 1.751476e+09 7.021104e+14 689.509729 0 False
3 0.969365 0.011515 1.830906e+09 7.600471e+14 780.888404 0 False
4 0.757057 0.332694 1.759230e+09 7.058550e+14 695.124565 0 False
5 0.489655 0.284131 1.762302e+09 7.074258e+14 695.944665 0 False
6 0.661926 0.549753 1.751542e+09 7.021496e+14 689.568055 0 False
7 0.811219 0.691923 1.748593e+09 7.016378e+14 686.908295 0 False
8 0.330034 0.464210 1.754723e+09 7.039286e+14 690.890998 0 False
9 0.778857 0.132968 1.775151e+09 7.142855e+14 707.998154 0 False
results1.describe()
Window to Wall Ratio RangeParameter [0.01, 0.99] Electricity Usage Electricity Variance CO2:Facility violation
count 10.000000 10.000000 1.000000e+01 1.000000e+01 10.000000 10.0
mean 0.643530 0.419238 1.763948e+09 7.103602e+14 701.707722 0.0
std 0.268330 0.262017 2.514026e+07 1.797485e+13 28.578856 0.0
min 0.119989 0.011515 1.743436e+09 6.988464e+14 684.446725 0.0
25% 0.513718 0.285022 1.751493e+09 7.021202e+14 689.524310 0.0
50% 0.709492 0.398452 1.756976e+09 7.048918e+14 693.007782 0.0
75% 0.803129 0.553469 1.762257e+09 7.073983e+14 695.907907 0.0
max 0.969365 0.882785 1.830906e+09 7.600471e+14 780.888404 0.0
# this cell runs the optimisation
results2 = NSGAII(evaluator, evaluations=10, population_size=10)
results2
Window to Wall Ratio RangeParameter [0.01, 0.99] Electricity Usage Electricity Variance CO2:Facility violation pareto-optimal
0 0.416852 0.853750 1.744062e+09 6.988618e+14 685.337017 0 True
1 0.027703 0.669789 1.748995e+09 7.018551e+14 687.160633 0 False
2 0.686940 0.903360 1.745100e+09 7.001653e+14 685.100750 0 False
3 0.944074 0.429213 1.756518e+09 7.046349e+14 691.612061 0 False
4 0.656314 0.908240 1.745160e+09 7.002909e+14 685.101769 0 False
5 0.762704 0.848589 1.744128e+09 6.989036e+14 685.377176 0 False
6 0.845933 0.928448 1.745011e+09 7.002508e+14 684.991962 0 False
7 0.950455 0.055710 1.793358e+09 7.273931e+14 729.680123 0 False
8 0.713841 0.444785 1.755086e+09 7.041377e+14 691.220545 0 False
9 0.986133 0.056327 1.793116e+09 7.272010e+14 729.358551 0 False
results2.describe()
Window to Wall Ratio RangeParameter [0.01, 0.99] Electricity Usage Electricity Variance CO2:Facility violation
count 10.000000 10.000000 1.000000e+01 1.000000e+01 10.000000 10.0
mean 0.699095 0.609821 1.757053e+09 7.063694e+14 695.494059 0.0
std 0.291322 0.344819 1.958696e+07 1.120168e+13 18.104944 0.0
min 0.027703 0.055710 1.744062e+09 6.988618e+14 684.991962 0.0
25% 0.663970 0.433106 1.745033e+09 7.001867e+14 685.160581 0.0
50% 0.738273 0.759189 1.747077e+09 7.010730e+14 686.268905 0.0
75% 0.919539 0.890958 1.756160e+09 7.045106e+14 691.514182 0.0
max 0.986133 0.928448 1.793358e+09 7.273931e+14 729.680123 0.0

Get available objectives

The user can use print_available_outputs to print out the available objectives for this building

building = ef.get_building(mode="idf")
print_available_outputs(building, name="facility", frequency="monthly")
['Electricity:Facility', 'Monthly']
['Gas:Facility', 'Monthly']
['CO2:Facility', 'Monthly']
['CO:Facility', 'Monthly']
['CH4:Facility', 'Monthly']
['NOx:Facility', 'Monthly']
['N2O:Facility', 'Monthly']
['SO2:Facility', 'Monthly']
['PM:Facility', 'Monthly']
['PM10:Facility', 'Monthly']
['PM2.5:Facility', 'Monthly']
['NH3:Facility', 'Monthly']
['NMVOC:Facility', 'Monthly']
['Hg:Facility', 'Monthly']
['Pb:Facility', 'Monthly']
['WaterEnvironmentalFactors:Facility', 'Monthly']
['Nuclear High:Facility', 'Monthly']
['Nuclear Low:Facility', 'Monthly']
['Carbon Equivalent:Facility', 'Monthly']