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
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")
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))
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 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
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
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
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)
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