Exemplo n.º 1
0
 def _get_model_instance(fmu_path, inputs, known_pars, est, output_names):
     model = Model(fmu_path)
     model.set_input(inputs)
     model.set_param(known_pars)
     model.set_param(estpars_2_df(est))
     model.set_outputs(output_names)
     return model
Exemplo n.º 2
0
    def test_simulation(self):
        output_names = self.ideal.columns.tolist()
        model = Model(self.fmu_path)
        model.inputs_from_df(self.inp)
        model.specify_outputs(output_names)
        model.parameters_from_df(self.known_df)

        res1 = model.simulate(reset=True)
        res2 = model.simulate(reset=False)

        self.assertTrue(res1.equals(res2), "Dataframes not equal")

        input_size = self.inp.index.size
        result_size = res1.index.size
        self.assertTrue(input_size == result_size,
                        "Result size different than input")
Exemplo n.º 3
0
class Model(object):
    """ Model for static parameter estimation """
    def __init__(self, fmu_path, opts=None):
        self.logger = logging.getLogger(type(self).__name__)

        self.model = FmiModel(fmu_path, opts=opts)

        # Log level
        try:
            self.model.model.set_log_level(FMI_WARNING)
        except AttributeError as e:
            self.logger.error(e.message)
            self.logger.error('Proceeding with standard log level...')

        # Simulation count
        self.sim_count = 0

    def set_input(self, df, exclude=list()):
        """ Sets inputs.

        :param df: Dataframe, time given in seconds
        :param exclude: list of strings, names of columns to be excluded
        :return: None
        """
        self.model.inputs_from_df(df, exclude)

    def set_param(self, df):
        """ Sets parameters. It is possible to set only a subset of model parameters.

        :param df: Dataframe with header and a single row of data
        :return: None
        """
        self.model.parameters_from_df(df)

    def set_outputs(self, outputs):
        """ Sets output variables.

        :param outputs: list of strings
        :return: None
        """
        self.model.specify_outputs(outputs)

    def simulate(self, com_points=None):
        # TODO: com_points should be adjusted to the number of samples
        self.sim_count += 1
        self.info('Simulation count = ' + str(self.sim_count))
        return self.model.simulate(com_points=com_points)

    def info(self, txt):
        class_name = self.__class__.__name__
        if VERBOSE:
            if isinstance(txt, str):
                print('[' + class_name + '] ' + txt)
            else:
                print('[' + class_name + '] ' + repr(txt))
Exemplo n.º 4
0
    def __init__(self, fmu_path, opts=None):
        self.logger = logging.getLogger(type(self).__name__)

        self.model = FmiModel(fmu_path, opts=opts)

        # Log level
        try:
            self.model.model.set_log_level(FMI_WARNING)
        except AttributeError as e:
            self.logger.error(e.message)
            self.logger.error('Proceeding with standard log level...')

        # Simulation count
        self.sim_count = 0
Exemplo n.º 5
0
    def validate(self, vp=None):
        """
        Performs a simulation with estimated parameters
        for the previously selected validation period. Other period
        can be chosen with the `vp` argument. User chosen `vp` in this method
        does not override the validation period chosen during instantiation
        of this class.

        Parameters
        ----------
        vp: tuple or None
            Validation period given as a tuple of start and stop time in
            seconds.

        Returns
        -------
        dict
            Validation error, keys: 'tot', '<var1>', '<var2>', ...
        pandas.DataFrame
            Simulation result
        """
        # Get estimates
        est = self.final
        est.index = [0]  # Reset index (needed by model.set_param())

        self.logger.info("Validation of parameters: {}".format(
            str(est.iloc[0].to_dict())))

        # Slice data
        if vp is None:
            start, stop = self.vp[0], self.vp[1]
        else:
            start, stop = vp[0], vp[1]
        inp_slice = self.inp.loc[start:stop]
        ideal_slice = self.ideal.loc[start:stop]

        # Initialize IC parameters and add to known
        if self.ic_param:
            for par in self.ic_param:
                ic = ideal_slice[self.ic_param[par]].iloc[0]
                self.known[par] = ic

        # Initialize model
        model = Model(self.fmu_path)
        model.set_input(inp_slice)
        model.set_param(est)
        model.set_param(self.known)
        model.set_outputs(list(self.ideal.columns))

        # Simulate and get error
        try:
            result = model.simulate()
        except Exception as e:
            msg = "Problem found inside FMU. Did you set all parameters?"
            self.logger.error(str(e))
            self.logger.error(msg)
            raise e

        err = modestpy.estim.error.calc_err(result, ideal_slice)

        # Create validation plot
        ax = plot_comparison(result, ideal_slice, f=None)
        fig = figures.get_figure(ax)
        fig.set_size_inches(Estimation.FIG_SIZE)
        fig.savefig(os.path.join(self.workdir, "validation.png"),
                    dpi=Estimation.FIG_DPI)

        # Remove temp dirs
        self._clean()

        # Return
        return err, result
Exemplo n.º 6
0
    This file is supposed to be run from the root directory.
    Otherwise the paths have to be corrected.
    """

    # Compile FMU =====================================================
    platform = get_sys_arch()
    mo_path = os.path.join('examples', 'sin', 'resources', 'sin_model.mo')
    fmu_path = os.path.join('examples', 'sin', 'resources',
                            'sin_model_{}.fmu'.format(platform))
    model_name = "sin_model"

    print('Compiling FMU for this platform')
    mo_2_fmu(model_name, mo_path, fmu_path)

    # Simulate FMU ====================================================
    model = Model(fmu_path)

    # Inputs
    inp = pd.DataFrame()
    t = np.arange(0, 86401, 300)
    inp['time'] = t
    inp['u'] = np.full(t.shape, 10.)
    inp = inp.set_index('time')
    # inp.to_csv(os.path.join('examples', 'sin', 'resources', 'input.csv'))

    # True parameters
    a = 3.
    b = 1.5
    par = pd.DataFrame(index=[0])
    par['a'] = a
    par['b'] = b
Exemplo n.º 7
0
 def _get_model_instance(self, fmu_path, inputs, known_pars, est,
                         output_names):
     self.logger.debug("Getting model instance...")
     self.logger.debug(f"inputs = {inputs}")
     self.logger.debug(f"known_pars = {known_pars}")
     self.logger.debug(f"est = {est}")
     self.logger.debug(f"estpars_2_df(est) = {estpars_2_df(est)}")
     self.logger.debug(f"output_names = {output_names}")
     model = Model(fmu_path)
     model.inputs_from_df(inputs)
     model.parameters_from_df(known_pars)
     model.parameters_from_df(estpars_2_df(est))
     model.specify_outputs(output_names)
     self.logger.debug(f"Model instance initialized: {model}")
     self.logger.debug(f"Model instance initialized: {model.model}")
     res = model.simulate()
     self.logger.debug(f"test result: {res}")
     return model
Exemplo n.º 8
0
 def instantiate_model(self, opts):
     self.model = Model(self.fmu_path, opts=opts)
     self.model.set_input(self.inputs)
     self.model.set_param(self.known_pars)
     self.model.set_outputs(self.outputs)
Exemplo n.º 9
0
class Population(object):
    def __init__(
        self,
        fmu_path,
        pop_size,
        inp,
        known,
        est,
        ideal,
        init=True,
        opts=None,
        ftype="NRMSE",
        init_pop=None,
    ):
        """
        :param fmu_path: string
        :param pop_size: int
        :param inp: DataFrame
        :param known: DataFrame
        :param est: List of EstPar objects
        :param ideal: DataFrame
        :param init: bool
        :param dict opts: Additional FMI options to be passed to the simulator
        :param string ftype: Cost function type. Currently 'NRMSE' or 'RMSE'.
        :param DataFrame init_pop: Initial population, DataFrame with initial
                                   guesses for estimated parameters
        """
        self.logger = logging.getLogger(type(self).__name__)

        # Initialize list of individuals
        self.individuals = list()

        # Assign attributes
        self.fmu_path = fmu_path
        self.pop_size = pop_size
        self.inputs = inp
        self.known_pars = known
        self.estpar = est
        self.outputs = [var for var in ideal]
        self.ideal = ideal
        self.ftype = ftype

        # Instantiate model
        self.model = None

        if init:
            # Instiate individuals before initialization
            self.instantiate_model(opts=opts)
            self._initialize(init_pop)
            self.calculate()

    def instantiate_model(self, opts):
        self.model = Model(self.fmu_path, opts=opts)
        self.model.set_input(self.inputs)
        self.model.set_param(self.known_pars)
        self.model.set_outputs(self.outputs)

    def add_individual(self, indiv):
        assert isinstance(indiv,
                          Individual), "Only Individual instances allowed..."
        indiv.reset()
        self.individuals.append(indiv)

    def calculate(self):
        for i in self.individuals:
            i.calculate()

    def size(self):
        return self.pop_size

    def get_fittest(self):
        fittest = self.individuals[0]
        for ind in self.individuals:
            if ind.error["tot"] < fittest.error["tot"]:
                fittest = ind
        fittest = copy.copy(fittest)
        return fittest

    def get_fittest_error(self):
        return self.get_fittest().error["tot"]

    def get_population_errors(self):
        err = list()
        for i in self.individuals:
            err.append(i.error["tot"])
        return err

    def get_fittest_estimates(self):
        return self.get_fittest().get_estimates()

    def get_all_estimates_and_errors(self):
        all_estim = pd.DataFrame()
        i = 1
        for ind in self.individuals:
            i_estim = ind.get_estimates_and_error()
            i_estim["individual"] = i
            all_estim = pd.concat([all_estim, i_estim])
            i += 1
        return all_estim

    def get_estpars(self):
        """Returns EstPar list"""
        return self.estpar

    def _initialize(self, init_pop=None):
        self.logger.debug(
            "Initialize population with init_pop=\n{}".format(init_pop))

        self.individuals = list()

        # How to initialize? Random or explicit initial guess?
        if init_pop is not None:
            assert (
                len(init_pop.index) == self.pop_size
            ), "Population size does not match initial guess {} != {}".format(
                init_pop.index.size, self.pop_size)
            init_guess = True
        else:
            init_guess = False

        for i in range(self.pop_size):
            # --------------------------------------------------> BUG HERE
            if init_guess:
                # Update value in EstPar objects with the next initial guess
                for n in range(len(self.estpar)):
                    self.estpar[n].value = init_pop.loc[i, self.estpar[n].name]
                    self.logger.debug("Individual #{} <- {}".format(
                        i, self.estpar[n]))

            self.add_individual(
                Individual(
                    est_objects=self.estpar,
                    population=self,
                    ftype=self.ftype,
                    use_init_guess=init_guess,
                ))
            # <-------------------------------------------------- BUG HERE

    def __str__(self):
        fittest = self.get_fittest()
        s = repr(self)
        s += "\n"
        s += "Number of individuals: " + str(len(self.individuals)) + "\n"
        if len(self.individuals) > 0:
            for i in self.individuals:
                s += str(i) + "\n"
            s += "-" * 110 + "\n"
            s += "Fittest: " + str(fittest) + "\n"
            s += "-" * 110 + "\n"
        else:
            s += "EMPTY"
        return s