# ---
# jupyter:
#   jupytext:
#     formats: ipynb,py:light
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.3'
#       jupytext_version: 0.8.6
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# This notebook covers how to use the two simplest evaluators, the EnergyPlus Evaluator (`EvaluatorEP`) and the surrogate Evaluator (`EvaluatorSR`)

from besos.evaluator import EvaluatorEP, EvaluatorSR
from besos import eppy_funcs as ef
from besos.parameters import FieldSelector, Parameter
from besos.problem import EPProblem, Problem

# ## EvaluatorEP
# This evaluator needs a building, and a problem with parameters that can modify it, and objectives that correspond to measurements of the building.  
# Parameters that have a selector of the type `FieldSelector` or `FilterSelecor` will work, and `GenericSelector` could work, if the right functions are provided.  
# Objectives of the type `MeterReader` or `VariableReader` will work.

# +
building = ef.get_building()

parameters = [Parameter(FieldSelector(object_name='Mass NonRes Wall Insulation',
                                      field_name='Thickness'))]

objectives = ['Electricity:Facility', 'Gas:Facility'] # the default is just 'Electricity:Facility'
problem=EPProblem(parameters, objectives) #EPP Problem automatically converts these to MeterReaders

evaluator = EvaluatorEP(problem, building)

evaluator([0.5]) # run with thickness set to 0.5 
# -

# ## EvaluatorSR
# This evaluator needs a function that takes the correct number of inputs and produces outputs in the format `(objectives, constraints)` where objectives and constraints are tuples.  
# Since this evaluator only uses it's parameters to track the number of inputs and outputs (and their names), we can use numbered placeholders that are automatically generated by `Problem`

# +
def function(values):
    return ((values[0], values[0]**2), ())
    
print('function value at 4 is:',function([4]))

new_problem = Problem(1,2,0) # this denotes a problem which takes 1 input, produces 2 outputs and no
#contraints. The placeholder parameters/objectives will be generated automatically.

# These names are used on the headings of DataFrames generated by the problem and evaluators that use it
print(problem.names(), new_problem.names())

evaluator_1 = EvaluatorSR(function, problem)
evaluator_2 = EvaluatorSR(function, new_problem)
print(evaluator_1([4], separate_constraints=True))
print(evaluator_2([4], separate_constraints=True))
# -

# separate constraints=True uses the `(objectives, constraints)` format for output.
# It can also be set to `False` or `None`  
# `False`: concatenates the tuples  
# `None`: Same as True if there is at least one constraint, else the same as False  

evaluator_1([4])


