def __init__( self, workdir, fmu_path, inp, known, est, ideal, lp_n=None, lp_len=None, lp_frame=None, vp=None, ic_param=None, methods=("MODESTGA", "PS"), ga_opts={}, ps_opts={}, scipy_opts={}, modestga_opts={}, ftype="RMSE", default_log=True, logfile="modestpy.log", ): # Default logging configuration? if default_log: config_logger(filename=logfile, level="WARNING") self.logger = logging.getLogger(type(self).__name__) # Sanity checks assert inp.index.equals( ideal.index), "inp and ideal indexes are not matching" init, lo, hi = 0, 1, 2 # Init. value, lower bound, upper bound indices for v in est: assert (est[v][init] >= est[v][lo]) and ( est[v][init] <= est[v][hi]), "Initial value out of limits ({})".format(v) # Input data self.workdir = workdir self.fmu_path = fmu_path self.inp = inp self.known = known self.est = est self.ideal = ideal self.methods = methods self.ftype = ftype # Results placeholders self.best_per_run = pd.DataFrame() self.final = pd.DataFrame() # Estimation options # GA options self.GA_OPTS = { "maxiter": 50, "pop_size": max((4 * len(est.keys()), 20)), "tol": 1e-6, "mut": 0.10, "mut_inc": 0.33, "uniformity": 0.5, "look_back": 50, "lhs": False, "ftype": ftype, } # Default # Default self.GA_OPTS["trm_size"] = max(self.GA_OPTS["pop_size"] // 6, 2) # User options self.GA_OPTS = self._update_opts(self.GA_OPTS, ga_opts, "GA_LEGACY") # PS options self.PS_OPTS = { "maxiter": 500, "rel_step": 0.02, "tol": 1e-11, "try_lim": 1000, "ftype": ftype, } # Default # User options self.PS_OPTS = self._update_opts(self.PS_OPTS, ps_opts, "PS") # SCIPY options self.SCIPY_OPTS = { "solver": "L-BFGS-B", "options": { "disp": True, "iprint": 2, "maxiter": 150, "full_output": True }, "ftype": ftype, } # Default # User options self.SCIPY_OPTS = self._update_opts(self.SCIPY_OPTS, scipy_opts, "SCIPY") # MODESTGA options self.MODESTGA_OPTS = { "workers": 3, # CPU cores to use "generations": 50, # Max. number of generations "pop_size": 30, # Population size "mut_rate": 0.01, # Mutation rate "trm_size": 3, # Tournament size "tol": 1e-3, # Solution tolerance "inertia": 100, # Max. number of non-improving generations "ftype": ftype, } # Default # User options self.MODESTGA_OPTS = self._update_opts(self.MODESTGA_OPTS, modestga_opts, "MODESTGA") # Method dictionary self.method_dict = { "MODESTGA": (MODESTGA, self.MODESTGA_OPTS), "GA_LEGACY": (GA, self.GA_OPTS), "PS": (PS, self.PS_OPTS), "SCIPY": (SCIPY, self.SCIPY_OPTS), } # Key -> method name, value -> (method class, method options) # List of learning periods (tuples with start, stop) self.lp = self._select_lp(lp_n, lp_len, lp_frame) # Validation period (a tuple with start, stop) if vp is not None: self.vp = vp else: self.vp = (ideal.index[0], ideal.index[-1]) # Initial condition parameters # Take first value from time series from 'ideal' column self.ic_param = ic_param # dict (par_name: ideal_column_name)
#!/usr/bin/env python from modestpy.test import run from modestpy.loginit import config_logger if __name__ == "__main__": config_logger(filename='unit_tests.log', level='DEBUG') run.tests()
random.seed(1) np.random.seed(1) ga = GA( self.fmu_path, self.inp, self.known, self.est, self.ideal, maxiter=self.gen, lhs=True, ) indiv = ga.pop.individuals par2 = list() for i in indiv: par2.append(i.get_estimates(as_dict=True)) for d1, d2 in zip(par1, par2): self.assertDictEqual(d1, d2) def suite(): suite = unittest.TestSuite() suite.addTest(TestGA("test_ga")) suite.addTest(TestGA("test_init_pop")) return suite if __name__ == "__main__": config_logger(filename="unit_tests.log", level="DEBUG") unittest.main()
def __init__(self, workdir, fmu_path, inp, known, est, ideal, lp_n=None, lp_len=None, lp_frame=None, vp=None, ic_param=None, methods=('MODESTGA', 'PS'), ga_opts={}, ps_opts={}, scipy_opts={}, modestga_opts={}, ftype='RMSE', default_log=True, logfile='modestpy.log'): # Default logging configuration? if default_log: config_logger(filename=logfile, level='WARNING') self.logger = logging.getLogger(type(self).__name__) # Sanity checks assert inp.index.equals(ideal.index), \ 'inp and ideal indexes are not matching' init, lo, hi = 0, 1, 2 # Init. value, lower bound, upper bound indices for v in est: assert (est[v][init] >= est[v][lo]) \ and (est[v][init] <= est[v][hi]), \ 'Initial value out of limits ({})'.format(v) # Input data self.workdir = workdir self.fmu_path = fmu_path self.inp = inp self.known = known self.est = est self.ideal = ideal self.methods = methods self.ftype = ftype # Results placeholders self.best_per_run = pd.DataFrame() self.final = pd.DataFrame() # Estimation options # GA options self.GA_OPTS = { 'maxiter': 50, 'pop_size': max((4 * len(est.keys()), 20)), 'tol': 1e-6, 'mut': 0.10, 'mut_inc': 0.33, 'uniformity': 0.5, 'look_back': 50, 'lhs': False, 'ftype': ftype } # Default # Default self.GA_OPTS['trm_size'] = max(self.GA_OPTS['pop_size'] // 6, 2) # User options self.GA_OPTS = self._update_opts(self.GA_OPTS, ga_opts, 'GA_LEGACY') # PS options self.PS_OPTS = { 'maxiter': 500, 'rel_step': 0.02, 'tol': 1e-11, 'try_lim': 1000, 'ftype': ftype } # Default # User options self.PS_OPTS = self._update_opts(self.PS_OPTS, ps_opts, 'PS') # SCIPY options self.SCIPY_OPTS = { 'solver': 'L-BFGS-B', 'options': { 'disp': True, 'iprint': 2, 'maxiter': 150, 'full_output': True }, 'ftype': ftype } # Default # User options self.SCIPY_OPTS = \ self._update_opts(self.SCIPY_OPTS, scipy_opts, 'SCIPY') # MODESTGA options self.MODESTGA_OPTS = { 'workers': 3, # CPU cores to use 'generations': 50, # Max. number of generations 'pop_size': 30, # Population size 'mut_rate': 0.01, # Mutation rate 'trm_size': 3, # Tournament size 'tol': 1e-3, # Solution tolerance 'inertia': 100, # Max. number of non-improving generations 'ftype': ftype } # Default # User options self.MODESTGA_OPTS = \ self._update_opts(self.MODESTGA_OPTS, modestga_opts, 'MODESTGA') # Method dictionary self.method_dict = { 'MODESTGA': (MODESTGA, self.MODESTGA_OPTS), 'GA_LEGACY': (GA, self.GA_OPTS), 'PS': (PS, self.PS_OPTS), 'SCIPY': (SCIPY, self.SCIPY_OPTS) } # Key -> method name, value -> (method class, method options) # List of learning periods (tuples with start, stop) self.lp = self._select_lp(lp_n, lp_len, lp_frame) # Validation period (a tuple with start, stop) if vp is not None: self.vp = vp else: self.vp = (ideal.index[0], ideal.index[-1]) # Initial condition parameters # Take first value from time series from 'ideal' column self.ic_param = ic_param # dict (par_name: ideal_column_name)
def __init__(self, workdir, fmu_path, inp, known, est, ideal, lp_n=None, lp_len=None, lp_frame=None, vp=None, ic_param=None, methods=('GA', 'PS'), ga_opts={}, ps_opts={}, scipy_opts={}, fmi_opts={}, ftype='RMSE', seed=None, default_log=True, logfile='modestpy.log'): """ Index in DataFrames ``inp`` and ``ideal`` must be named 'time' and given in seconds. The index name assertion check is implemented to avoid situations in which a user reads DataFrame from a csv and forgets to use ``DataFrame.set_index(column_name)`` (it happens quite often...). TODO: Check index name assertion. Currently available estimation methods: - GA - genetic algorithm - PS - pattern search (Hooke-Jeeves) - SCIPY - interface to algorithms available through scipy.optimize.minimize() Parameters: ----------- workdir: str Output directory, must exist fmu_path: str Absolute path to the FMU inp: pandas.DataFrame Input data, index given in seconds and named ``time`` known: dict(str: float) Dictionary with known parameters (``parameter_name: value``) est: dict(str: tuple(float, float, float)) Dictionary defining estimated parameters, (``par_name: (guess value, lo limit, hi limit)``) ideal: pandas.DataFrame Ideal solution (usually measurements), index in seconds and named ``time`` lp_n: int or None Number of learning periods, one if ``None`` lp_len: float or None Length of a single learning period, entire ``lp_frame`` if ``None`` lp_frame: tuple of floats or None Learning period time frame, entire data set if ``None`` vp: tuple(float, float) or None Validation period, entire data set if ``None`` ic_param: dict(str, str) or None Mapping between model parameters used for IC and variables from ``ideal`` methods: tuple(str, str) List of methods to be used in the pipeline ga_opts: dict Genetic algorithm options ps_opts: dict Pattern search options scipy_opts: dict SciPy solver options fmi_opts: dict Additional options to be passed to the FMI model (e.g. solver tolerance) ftype: string Cost function type. Currently 'NRMSE' (advised for multi-objective estimation) or 'RMSE'. seed: None or int Random number seed. If None, current time or OS specific randomness is used. default_log: bool If true, use default logging settings. Use false if you want to use own logging. logfile: str If default_log=True, this argument can be used to specify the log file name """ # Default logging configuration? if default_log: config_logger(filename=logfile, level='DEBUG') self.logger = logging.getLogger(type(self).__name__) # Sanity checks assert inp.index.equals(ideal.index), \ 'inp and ideal indexes are not matching' init, lo, hi = 0, 1, 2 # Init. value, lower bound, upper bound indices for v in est: assert (est[v][init] >= est[v][lo]) \ and (est[v][init] <= est[v][hi]), \ 'Initial value out of limits ({})'.format(v) # Random seed if seed is not None: self.logger.info('Setting random seed: {}'.format(seed)) random.seed(seed) np.random.seed(seed) # Important for other libraries, like pyDOE # Input data self.workdir = workdir self.fmu_path = fmu_path self.inp = inp self.known = known self.est = est self.ideal = ideal self.methods = methods self.ftype = ftype # Results placeholders self.best_per_run = pd.DataFrame() self.final = pd.DataFrame() # Estimation options # GA options self.GA_OPTS = { 'maxiter': 50, 'pop_size': max((4 * len(est.keys()), 20)), 'tol': 1e-6, 'mut': 0.10, 'mut_inc': 0.33, 'uniformity': 0.5, 'look_back': 50, 'lhs': False, 'ftype': ftype, 'fmi_opts': fmi_opts } # Default # Default self.GA_OPTS['trm_size'] = max(self.GA_OPTS['pop_size'] // 6, 2) # User options self.GA_OPTS = self._update_opts(self.GA_OPTS, ga_opts, 'GA') # PS options self.PS_OPTS = { 'maxiter': 500, 'rel_step': 0.02, 'tol': 1e-11, 'try_lim': 1000, 'ftype': ftype, 'fmi_opts': fmi_opts } # Default # User options self.PS_OPTS = self._update_opts(self.PS_OPTS, ps_opts, 'PS') # SCIPY options self.SCIPY_OPTS = { 'solver': 'L-BFGS-B', 'options': { 'disp': True, 'iprint': 2, 'maxiter': 150, 'full_output': True }, 'ftype': ftype, 'fmi_opts': fmi_opts } # Default # User options self.SCIPY_OPTS = \ self._update_opts(self.SCIPY_OPTS, scipy_opts, 'SCIPY') # Method dictionary self.method_dict = { 'GA': (GA, self.GA_OPTS), 'PS': (PS, self.PS_OPTS), 'SCIPY': (SCIPY, self.SCIPY_OPTS) } # Key -> method name, value -> (method class, method options) # List of learning periods (tuples with start, stop) self.lp = self._select_lp(lp_n, lp_len, lp_frame) # Validation period (a tuple with start, stop) if vp is not None: self.vp = vp else: self.vp = (ideal.index[0], ideal.index[-1]) # Initial condition parameters # Take first value from time series from 'ideal' column self.ic_param = ic_param # dict (par_name: ideal_column_name)
This code is licensed under BSD 2-clause license. See LICENSE file in the project root for license terms. """ import json import os import pandas as pd from modestpy import Estimation from modestpy.utilities.sysarch import get_sys_arch from modestpy.loginit import config_logger if __name__ == "__main__": """ This file is supposed to be run from the root directory. Otherwise the paths have to be corrected. """ config_logger(filename='simple.log', level='DEBUG') # DATA PREPARATION ============================================== # Resources platform = get_sys_arch() assert platform, 'Unsupported platform type!' fmu_file = 'Simple2R1C_ic_' + platform + '.fmu' fmu_path = os.path.join('examples', 'simple', 'resources', fmu_file) inp_path = os.path.join('examples', 'simple', 'resources', 'inputs.csv') ideal_path = os.path.join('examples', 'simple', 'resources', 'result.csv') est_path = os.path.join('examples', 'simple', 'resources', 'est.json') known_path = os.path.join('examples', 'simple', 'resources', 'known.json') # Working directory workdir = os.path.join('examples', 'simple', 'workdir')
""" import json import os import pandas as pd from modestpy import Estimation from modestpy.loginit import config_logger from modestpy.utilities.sysarch import get_sys_arch if __name__ == "__main__": """ This file is supposed to be run from the root directory. Otherwise the paths have to be corrected. """ config_logger(filename="simple.log", level="DEBUG") # DATA PREPARATION ============================================== # Resources platform = get_sys_arch() assert platform, "Unsupported platform type!" fmu_file = "Simple2R1C_ic_" + platform + ".fmu" fmu_path = os.path.join("examples", "simple", "resources", fmu_file) inp_path = os.path.join("examples", "simple", "resources", "inputs.csv") ideal_path = os.path.join("examples", "simple", "resources", "result.csv") est_path = os.path.join("examples", "simple", "resources", "est.json") known_path = os.path.join("examples", "simple", "resources", "known.json") # Working directory workdir = os.path.join("examples", "simple", "workdir")