예제 #1
0
def create_function(name, inp, out, options):
    codegen = options['codegen']
    if options['verbose'] >= 1:
        print('Building function %s ... ' % name, end=' ')
    t0 = time.time()
    fun = Function(name, inp, out).expand()
    if codegen['build'] == 'jit':
        if options['verbose'] >= 1:
            print(('[jit compilation with flags %s]' % (codegen['flags'])),
                  end=' ')
        fun.generate(name)
        compiler = Compiler(name + '.c', 'clang', {'flags': codegen['flags']})
        fun = external(name, compiler)
        os.remove(name + '.c')
    elif codegen['build'] == 'shared':
        if os.name == 'nt':
            raise ValueError('Build option is not supported for Windows!')
        directory = os.path.join(os.getcwd(), 'build')
        if not os.path.isdir(directory):
            os.makedirs(directory)
        path = os.path.join(directory, name)
        if options['verbose'] >= 1:
            print(('[compile to .so with flags %s]' % (codegen['flags'])),
                  end=' ')
        if os.path.isfile(path + '.so'):
            os.remove(path + '.so')
        fun.generate(name + '.c')
        shutil.move(name + '.c', path + '.c')
        os.system('gcc -fPIC -shared %s %s.c -o %s.so' %
                  (codegen['flags'], path, path))
        fun = external(name, path + '.so')
        os.remove(path + '.c')
    elif codegen['build'] == 'existing':
        if os.name == 'nt':
            raise ValueError('Build option is not supported for Windows!')
        directory = os.path.join(os.getcwd(), 'build')
        path = os.path.join(directory, name)
        if not os.path.isfile(path + '.so'):
            raise ValueError('%s.so does not exist!', path)
        if options['verbose'] >= 1:
            print(('[using shared object %s.so]' % path), end=' ')
        fun = external(name, path + '.so')
    elif codegen['build'] is None:
        fun = fun
    else:
        raise ValueError('Invalid build option.')
    t1 = time.time()
    if options['verbose'] >= 1:
        print('in %5f s' % (t1 - t0))
    return fun, (t1 - t0)
예제 #2
0
def export_external_ode_model():

    model_name = 'external_ode'

    # Declare model variables
    x = MX.sym('x', 2)
    u = MX.sym('u', 1)
    xDot = MX.sym('xDot', 2)
    cdll.LoadLibrary('./test_external_lib/build/libexternal_ode_casadi.so')
    f_ext = external('libexternal_ode_casadi', 'libexternal_ode_casadi.so',
                     {'enable_fd': True})
    f_expl = f_ext(x, u)

    f_impl = xDot - f_expl

    model = AcadosModel()

    model.f_impl_expr = f_impl
    model.f_expl_expr = f_expl
    model.x = x
    model.xdot = xDot
    model.u = u
    model.p = []
    model.name = model_name

    return model
예제 #3
0
파일: ilqr.py 프로젝트: ADVRHumanoids/ilqr
    def _make_jit_function(f: cs.Function):
        """
        Compiles casadi function into a shared object and return it
        :return:
        """

        import filecmp
        import os

        gen_code_path = 'ilqr_generated_{}.c'.format(f.name())
        f.generate(gen_code_path)

        gen_lib_path = 'ilqr_generated_{}.so'.format(f.name())
        gcc_cmd = 'gcc {} -shared -fPIC -O3 -o {}'.format(
            gen_code_path, gen_lib_path)

        if os.system(gcc_cmd) != 0:
            raise SystemError('Unable to compile function "{}"'.format(
                f.name()))

        jit_f = cs.external(f.name(), './' + gen_lib_path)

        os.remove(gen_code_path)
        os.remove(gen_lib_path)

        return jit_f
예제 #4
0
    def test_generate_casadi(self):
        model = pybamm.BaseModel()
        t = pybamm.t
        a = pybamm.Variable("a")
        b = pybamm.Variable("b")
        p = pybamm.InputParameter("p")
        q = pybamm.InputParameter("q")
        model.rhs = {a: -a * p}
        model.algebraic = {b: a - b}
        model.initial_conditions = {a: q, b: 1}
        model.variables = {"a+b": a + b - t}

        # Generate C code
        model.generate("test.c", ["a+b"])

        # Compile
        subprocess.run(["gcc", "-fPIC", "-shared", "-o", "test.so",
                        "test.c"])  # nosec

        # Read the generated functions
        x0_fn = casadi.external("x0", "./test.so")
        z0_fn = casadi.external("z0", "./test.so")
        rhs_fn = casadi.external("rhs_", "./test.so")
        alg_fn = casadi.external("alg_", "./test.so")
        jac_rhs_fn = casadi.external("jac_rhs", "./test.so")
        jac_alg_fn = casadi.external("jac_alg", "./test.so")
        var_fn = casadi.external("variables", "./test.so")

        # Test that function values are as expected
        self.assertEqual(x0_fn([0, 5]), 5)
        self.assertEqual(z0_fn([0, 0]), 1)
        self.assertEqual(rhs_fn(0, 3, 2, [7, 2]), -21)
        self.assertEqual(alg_fn(0, 3, 2, [7, 2]), 1)
        np.testing.assert_array_equal(np.array(jac_rhs_fn(5, 6, 7, [8, 9])),
                                      [[-8, 0]])
        np.testing.assert_array_equal(np.array(jac_alg_fn(5, 6, 7, [8, 9])),
                                      [[1, -1]])
        self.assertEqual(var_fn(6, 3, 2, [7, 2]), -1)

        # Remove generated files.
        os.remove("test.c")
        os.remove("test.so")
예제 #5
0
파일: api.py 프로젝트: mdecourse/pymoca
def load_model(model_folder: str, model_name: str, compiler_options: Dict[str, str]) -> CachedModel:
    """
    Loads a precompiled CasADi model into a CachedModel instance.

    :param model_folder: Folder where the precompiled CasADi model is located.
    :param model_name: Name of the model.
    :param compiler_options: Dictionary of compiler options.

    :returns: CachedModel instance.
    """

    db_file = os.path.join(model_folder, model_name)

    if compiler_options.get('mtime_check', True):
        # Mtime check
        cache_mtime = os.path.getmtime(db_file)
        for folder in [model_folder] + compiler_options.get('library_folders', []):
            for root, dir, files in os.walk(folder, followlinks=True):
                for item in fnmatch.filter(files, "*.mo"):
                    filename = os.path.join(root, item)
                    if os.path.getmtime(filename) > cache_mtime:
                        raise InvalidCacheError("Cache out of date")

    # Create empty model object
    model = CachedModel()

    # Load metadata
    with open(db_file, 'rb') as f:
        db = pickle.load(f)

        if db['version'] != __version__:
            raise InvalidCacheError('Cache generated for a different version of pymoca')

        # Check compiler options. We ignore the library folders, as they have
        # already been checked, and checking them will impede platform
        # portability of the cache.
        exclude_options = ['library_folders']
        old_opts = {k: v for k, v in db['options'].items() if k not in exclude_options}
        new_opts = {k: v for k, v in compiler_options.items() if k not in exclude_options}

        if old_opts != new_opts:
            raise InvalidCacheError('Cache generated for different compiler options')

        # Pickles are platform independent, but dynamic libraries are not
        if compiler_options['codegen']:
            if db['library_os'] != os.name:
                raise InvalidCacheError('Cache generated for incompatible OS')

        # Include references to the shared libraries
        for o in ['dae_residual', 'initial_residual', 'variable_metadata', 'delay_arguments']:
            if isinstance(db[o], str):
                # Path to codegen'd library
                f = ca.external(o, db[o])
            else:
                # Pickled CasADi Function; use as is
                assert isinstance(db[o], ca.Function)
                f = db[o]

            setattr(model, '_' + o + '_function', f)

        # Load variables per category
        variables_with_metadata = ['states', 'alg_states', 'inputs', 'parameters', 'constants']
        variable_dict = {}
        for key in variables_with_metadata:
            variables = getattr(model, key)
            for i, d in enumerate(db[key]):
                variable = Variable.from_dict(d)
                variables.append(variable)
                variable_dict[variable.symbol.name()] = variable

        model.der_states = [Variable.from_dict(d) for d in db['der_states']]
        model.outputs = db['outputs']
        model.delay_states = db['delay_states']

        # Evaluate variable metadata:
        parameter_vector = ca.veccat(*[v.symbol for v in model.parameters])
        metadata = dict(zip(variables_with_metadata, model.variable_metadata_function(parameter_vector)))
        independent_metadata = dict(zip(variables_with_metadata, model.variable_metadata_function(ca.veccat(*[np.nan for v in model.parameters]))))

        for k, key in enumerate(variables_with_metadata):
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(CASADI_ATTRIBUTES):
                    if ca.depends_on(ca.MX(metadata[key][i, j]), parameter_vector):
                        setattr(variable, tmp, metadata[key][i, j])
                    else:
                        setattr(variable, tmp, independent_metadata[key][i, j])

        # Evaluate delay arguments:
        args = [model.time,
                ca.veccat(*model._symbols(model.states)),
                ca.veccat(*model._symbols(model.der_states)),
                ca.veccat(*model._symbols(model.alg_states)),
                ca.veccat(*model._symbols(model.inputs)),
                ca.veccat(*model._symbols(model.constants)),
                ca.veccat(*model._symbols(model.parameters))]
        delay_arguments_raw = model.delay_arguments_function(*args)

        nan_args = [ca.repmat(np.nan, *arg.size()) for arg in args]
        independent_delay_arguments_raw = model.delay_arguments_function(*nan_args)

        if delay_arguments_raw is not None:
            all_symbols = ca.veccat(*args)

            delay_expressions_raw = delay_arguments_raw[::2]
            delay_durations_raw = delay_arguments_raw[1::2]
            independent_delay_durations_raw = independent_delay_arguments_raw[1::2]

            assert 1 == len({len(delay_expressions_raw), len(delay_durations_raw),
                len(independent_delay_durations_raw)})

            for i, expr in enumerate(delay_expressions_raw):
                assert ca.depends_on(ca.MX(expr), all_symbols), \
                    "Expression to be delayed can not be a constant"

                if ca.depends_on(ca.MX(delay_durations_raw[i]), all_symbols):
                    dur = delay_durations_raw[i]
                    false_deps = ca.vertcat(*[
                        var for var in ca.symvar(dur) if not ca.depends_on(dur, var)])
                    if false_deps.size1() > 0:
                        [dur] = ca.substitute(
                            [dur],
                            [false_deps],
                            [ca.DM(np.full(false_deps.size1(), np.nan))])
                else:
                    dur = independent_delay_durations_raw[i]

                model.delay_arguments.append(DelayArgument(expr, dur))

        # Try to coerce parameters into their Python types
        for p in model.parameters:
            for attr in CASADI_ATTRIBUTES:
                v = getattr(p, attr)
                v_mx = ca.MX(v)
                if v_mx.is_constant() and v_mx.is_regular():
                    setattr(p, attr, p.python_type(v))

    # Done
    return model
예제 #6
0
파일: api.py 프로젝트: vdwees/pymoca
def load_model(model_folder: str, model_name: str, compiler_options: Dict[str, str]) -> CachedModel:
    """
    Loads a precompiled CasADi model into a CachedModel instance.

    :param model_folder: Folder where the precompiled CasADi model is located.
    :param model_name: Name of the model.
    :param compiler_options: Dictionary of compiler options.

    :returns: CachedModel instance.
    """

    db_file = os.path.join(model_folder, model_name)

    if compiler_options.get('mtime_check', True):
        # Mtime check
        cache_mtime = os.path.getmtime(db_file)
        for folder in [model_folder] + compiler_options.get('library_folders', []):
            for root, dir, files in os.walk(folder, followlinks=True):
                for item in fnmatch.filter(files, "*.mo"):
                    filename = os.path.join(root, item)
                    if os.path.getmtime(filename) > cache_mtime:
                        raise InvalidCacheError("Cache out of date")

    # Create empty model object
    model = CachedModel()

    # Load metadata
    with open(db_file, 'rb') as f:
        db = pickle.load(f)

        if db['version'] != __version__:
            raise InvalidCacheError('Cache generated for a different version of pymoca')

        # Check compiler options. We ignore the library folders, as they have
        # already been checked, and checking them will impede platform
        # portability of the cache.
        exclude_options = ['library_folders']
        old_opts = {k: v for k, v in db['options'].items() if k not in exclude_options}
        new_opts = {k: v for k, v in compiler_options.items() if k not in exclude_options}

        if old_opts != new_opts:
            raise InvalidCacheError('Cache generated for different compiler options')

        # Pickles are platform independent, but dynamic libraries are not
        if compiler_options['codegen']:
            if db['library_os'] != os.name:
                raise InvalidCacheError('Cache generated for incompatible OS')

        # Include references to the shared libraries
        for o in ['dae_residual', 'initial_residual', 'variable_metadata', 'delay_arguments']:
            if isinstance(db[o], str):
                # Path to codegen'd library
                f = ca.external(o, db[o])
            else:
                # Pickled CasADi Function; use as is
                assert isinstance(db[o], ca.Function)
                f = db[o]

            setattr(model, '_' + o + '_function', f)

        # Load variables per category
        variables_with_metadata = ['states', 'alg_states', 'inputs', 'parameters', 'constants']
        variable_dict = {}
        for key in variables_with_metadata:
            variables = getattr(model, key)
            for i, d in enumerate(db[key]):
                variable = Variable.from_dict(d)
                variables.append(variable)
                variable_dict[variable.symbol.name()] = variable

        model.der_states = [Variable.from_dict(d) for d in db['der_states']]
        model.outputs = db['outputs']
        model.delay_states = db['delay_states']

        # Evaluate variable metadata:
        parameter_vector = ca.veccat(*[v.symbol for v in model.parameters])
        metadata = dict(zip(variables_with_metadata, model.variable_metadata_function(parameter_vector)))
        independent_metadata = dict(zip(variables_with_metadata, model.variable_metadata_function(ca.veccat(*[np.nan for v in model.parameters]))))

        for k, key in enumerate(variables_with_metadata):
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(CASADI_ATTRIBUTES):
                    if ca.depends_on(ca.MX(metadata[key][i, j]), parameter_vector):
                        setattr(variable, tmp, metadata[key][i, j])
                    else:
                        setattr(variable, tmp, independent_metadata[key][i, j])

    # Done
    return model
예제 #7
0
파일: api.py 프로젝트: gitlyap/pymoca
def load_model(model_folder: str, model_name: str,
               compiler_options: Dict[str, str]) -> CachedModel:
    """
    Loads a precompiled CasADi model into a CachedModel instance.

    :param model_folder: Folder where the precompiled CasADi model is located.
    :param model_name: Name of the model.
    :param compiler_options: Dictionary of compiler options.

    :returns: CachedModel instance.
    """

    db_file = os.path.join(model_folder, model_name + ".pymoca_cache")

    if compiler_options.get('mtime_check', True):
        # Mtime check
        cache_mtime = os.path.getmtime(db_file)
        for folder in [model_folder] + compiler_options.get(
                'library_folders', []):
            for root, dir, files in os.walk(folder, followlinks=True):
                for item in fnmatch.filter(files, "*.mo"):
                    filename = os.path.join(root, item)
                    if os.path.getmtime(filename) > cache_mtime:
                        raise InvalidCacheError("Cache out of date")

    # Create empty model object
    model = CachedModel()

    # Load metadata
    with open(db_file, 'rb') as f:
        db = pickle.load(f)

        if db['version'] != __version__:
            raise InvalidCacheError(
                'Cache generated for a different version of pymoca')

        # Check compiler options. We ignore the library folders, as they have
        # already been checked, and checking them will impede platform
        # portability of the cache.
        exclude_options = ['library_folders']
        old_opts = {
            k: v
            for k, v in db['options'].items() if k not in exclude_options
        }
        new_opts = {
            k: v
            for k, v in compiler_options.items() if k not in exclude_options
        }

        if old_opts != new_opts:
            raise InvalidCacheError(
                'Cache generated for different compiler options')

        # Pickles are platform independent, but dynamic libraries are not
        if compiler_options.get('codegen', False):
            if db['library_os'] != os.name:
                raise InvalidCacheError('Cache generated for incompatible OS')

        # Include references to the shared libraries
        for o in [
                'dae_residual', 'initial_residual', 'variable_metadata',
                'delay_arguments'
        ]:
            if isinstance(db[o], str):
                # Path to codegen'd library
                f = ca.external(o, db[o])
            else:
                # Pickled CasADi Function; use as is
                assert isinstance(db[o], ca.Function)
                f = db[o]

            setattr(model, '_' + o + '_function', f)

        # Load variables per category
        variables_with_metadata = [
            'states', 'alg_states', 'inputs', 'parameters', 'constants'
        ]
        variable_dict = {}
        for key in variables_with_metadata:
            variables = getattr(model, key)
            for i, d in enumerate(db[key]):
                variable = Variable.from_dict(d)
                variables.append(variable)
                variable_dict[variable.symbol.name()] = variable

        model.der_states = [Variable.from_dict(d) for d in db['der_states']]
        model.outputs = db['outputs']
        model.delay_states = db['delay_states']
        model.alias_relation = db['alias_relation']

        # Evaluate variable metadata:
        parameter_vector = ca.veccat(*[v.symbol for v in model.parameters])
        metadata = dict(
            zip(variables_with_metadata,
                model.variable_metadata_function(parameter_vector)))
        independent_metadata = dict(
            zip(variables_with_metadata,
                (np.array(x) for x in model.variable_metadata_function(
                    ca.veccat(*[np.nan for v in model.parameters])))))

        for k, key in enumerate(variables_with_metadata):
            m = db[key + "__metadata_dependent"]
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(CASADI_ATTRIBUTES):
                    if m[i, j]:
                        setattr(variable, tmp, metadata[key][i, j])
                    else:
                        setattr(variable, tmp, independent_metadata[key][i, j])

        # Evaluate delay arguments:
        if model.delay_states:
            args = [
                model.time,
                ca.veccat(*model._symbols(model.states)),
                ca.veccat(*model._symbols(model.der_states)),
                ca.veccat(*model._symbols(model.alg_states)),
                ca.veccat(*model._symbols(model.inputs)),
                ca.veccat(*model._symbols(model.constants)),
                ca.veccat(*model._symbols(model.parameters))
            ]
            delay_arguments_raw = model.delay_arguments_function(*args)

            nan_args = [ca.repmat(np.nan, *arg.size()) for arg in args]
            independent_delay_arguments_raw = model.delay_arguments_function(
                *nan_args)

            delay_expressions_raw = delay_arguments_raw[::2]
            delay_durations_raw = delay_arguments_raw[1::2]
            independent_delay_durations_raw = independent_delay_arguments_raw[
                1::2]

            assert 1 == len({
                len(delay_expressions_raw),
                len(delay_durations_raw),
                len(independent_delay_durations_raw)
            })

            all_symbols = [
                model.time, *model._symbols(model.states),
                *model._symbols(model.der_states),
                *model._symbols(model.alg_states),
                *model._symbols(model.inputs),
                *model._symbols(model.constants),
                *model._symbols(model.parameters)
            ]

            duration_dependencies = db['__delay_duration_dependent']

            # Get rid of false dependency symbols not used in any delay
            # durations. This significantly reduces the work the (slow)
            # substitute() calls have to do later on.
            actual_deps = sorted(set(np.array(duration_dependencies).ravel()))

            actual_dep_symbols = [np.nan] * len(all_symbols)
            for i in actual_deps:
                actual_dep_symbols[i] = all_symbols[i]

            delay_durations_simplified = ca.Function(
                'replace_false_deps', all_symbols,
                delay_durations_raw).call(actual_dep_symbols)

            # Get rid of remaining hidden dependencies in the delay durations
            for i, expr in enumerate(delay_expressions_raw):
                if duration_dependencies[i]:
                    dur = delay_durations_simplified[i]

                    if len(duration_dependencies[i]) < len(actual_deps):
                        deps = set(ca.symvar(dur))
                        actual_deps = {
                            all_symbols[j]
                            for j in duration_dependencies[i]
                        }
                        false_deps = deps - actual_deps

                        if false_deps:
                            [dur] = ca.substitute([dur], list(false_deps),
                                                  [np.nan] * len(false_deps))
                    else:
                        # Already removed all false dependencies
                        pass
                else:
                    dur = independent_delay_durations_raw[i]

                model.delay_arguments.append(DelayArgument(expr, dur))

        # Try to coerce parameters into their Python types
        for p in model.parameters:
            for attr in CASADI_ATTRIBUTES:
                v = getattr(p, attr)
                v_mx = ca.MX(v)
                if v_mx.is_constant() and v_mx.is_regular():
                    setattr(p, attr, p.python_type(v))

    # Done
    return model
예제 #8
0
def _load_model(model_folder: str, model_name: str,
                compiler_options: Dict[str, str]) -> CachedModel:
    db_file = os.path.join(model_folder, model_name)

    if compiler_options.get('mtime_check', True):
        # Mtime check
        cache_mtime = os.path.getmtime(db_file)
        for folder in [model_folder] + compiler_options.get(
                'library_folders', []):
            for root, dir, files in os.walk(folder, followlinks=True):
                for item in fnmatch.filter(files, "*.mo"):
                    filename = os.path.join(root, item)
                    if os.path.getmtime(filename) > cache_mtime:
                        raise InvalidCacheError("Cache out of date")

    # Create empty model object
    model = CachedModel()

    # Compile shared libraries
    objects = {
        'dae_residual': ObjectData('dae_residual', ''),
        'initial_residual': ObjectData('initial_residual', ''),
        'variable_metadata': ObjectData('variable_metadata', '')
    }

    # Load metadata
    with open(db_file, 'rb') as f:
        db = pickle.load(f)

        if db['version'] != __version__:
            raise InvalidCacheError(
                'Cache generated for a different version of pymola')

        if db['library_os'] != os.name:
            raise InvalidCacheError('Cache generated for incompatible OS')

        # Include references to the shared libraries
        for o, d in objects.items():
            f = ca.external(o, db[d.key])

            setattr(model, '_' + o + '_function', f)

        # Load variables per category
        variables_with_metadata = [
            'states', 'alg_states', 'inputs', 'parameters', 'constants'
        ]
        variable_dict = {}
        for key in variables_with_metadata:
            variables = getattr(model, key)
            for i, d in enumerate(db[key]):
                variable = Variable.from_dict(d)
                variables.append(variable)
                variable_dict[variable.symbol.name()] = variable

        model.der_states = [Variable.from_dict(d) for d in db['der_states']]
        model.outputs = db['outputs']
        model.delayed_states = db['delayed_states']

        # Evaluate variable metadata:
        # We do this in three passes, so that we have constant attributes available through the API,
        # and non-constant expressions as Function calls.

        # 1.  Extract independent parameter values
        metadata = dict(
            zip(
                variables_with_metadata,
                model.variable_metadata_function(
                    ca.veccat(*[np.nan for v in model.parameters]))))
        for key in variables_with_metadata:
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(ast.Symbol.ATTRIBUTES):
                    setattr(variable, tmp, metadata[key][i, j])

        # 2.  Plug independent values back into metadata function, to obtain values (such as bounds, or starting values)
        # dependent upon independent parameter values.
        metadata = dict(
            zip(
                variables_with_metadata,
                model.variable_metadata_function(
                    ca.veccat(*[
                        v.value if v.value.is_regular() else np.nan
                        for v in model.parameters
                    ]))))
        for key in variables_with_metadata:
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(ast.Symbol.ATTRIBUTES):
                    setattr(variable, tmp, metadata[key][i, j])

        # 3.  Fill in any irregular elements with expressions to be evaluated later.
        # Note that an expression is neccessary only if the function value actually depends on the inputs.
        # Otherwise, we would be dealing with a genuine NaN value.
        parameter_vector = ca.veccat(*[v.symbol for v in model.parameters])
        metadata = dict(
            zip(
                variables_with_metadata,
                model.variable_metadata_function(
                    ca.veccat(*[
                        v.value if v.value.is_regular() else v.symbol
                        for v in model.parameters
                    ]))))
        for k, key in enumerate(variables_with_metadata):
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(ast.Symbol.ATTRIBUTES):
                    if not getattr(variable, tmp).is_regular():
                        if (not isinstance(metadata[key][i, j], ca.DM)
                                and ca.depends_on(metadata[key][i, j],
                                                  parameter_vector)):
                            setattr(variable, tmp, metadata[key][i, j])

    # Done
    return model
예제 #9
0
파일: api.py 프로젝트: jgoppert/pymola
def load_model(model_folder: str, model_name: str, compiler_options: Dict[str, str]) -> CachedModel:
    """
    Loads a precompiled CasADi model into a CachedModel instance.

    :param model_folder: Folder where the precompiled CasADi model is located.
    :param model_name: Name of the model.
    :param compiler_options: Dictionary of compiler options.

    :returns: CachedModel instance.
    """

    db_file = os.path.join(model_folder, model_name + ".pymoca_cache")

    if compiler_options.get('mtime_check', True):
        # Mtime check
        cache_mtime = os.path.getmtime(db_file)
        for folder in [model_folder] + compiler_options.get('library_folders', []):
            for root, dir, files in os.walk(folder, followlinks=True):
                for item in fnmatch.filter(files, "*.mo"):
                    filename = os.path.join(root, item)
                    if os.path.getmtime(filename) > cache_mtime:
                        raise InvalidCacheError("Cache out of date")

    # Create empty model object
    model = CachedModel()

    # Load metadata
    with open(db_file, 'rb') as f:
        db = pickle.load(f)

        if db['version'] != __version__:
            raise InvalidCacheError('Cache generated for a different version of pymoca')

        # Check compiler options. We ignore the library folders, as they have
        # already been checked, and checking them will impede platform
        # portability of the cache.
        exclude_options = ['library_folders']
        old_opts = {k: v for k, v in db['options'].items() if k not in exclude_options}
        new_opts = {k: v for k, v in compiler_options.items() if k not in exclude_options}

        if old_opts != new_opts:
            raise InvalidCacheError('Cache generated for different compiler options')

        # Pickles are platform independent, but dynamic libraries are not
        if compiler_options.get('codegen', False):
            if db['library_os'] != os.name:
                raise InvalidCacheError('Cache generated for incompatible OS')

        # Include references to the shared libraries
        for o in ['dae_residual', 'initial_residual', 'variable_metadata', 'delay_arguments']:
            if isinstance(db[o], str):
                # Path to codegen'd library
                f = ca.external(o, db[o])
            else:
                # Pickled CasADi Function; use as is
                assert isinstance(db[o], ca.Function)
                f = db[o]

            setattr(model, '_' + o + '_function', f)

        # Load variables per category
        variables_with_metadata = ['states', 'alg_states', 'inputs', 'parameters', 'constants']
        variable_dict = {}
        for key in variables_with_metadata:
            variables = getattr(model, key)
            for i, d in enumerate(db[key]):
                variable = Variable.from_dict(d)
                variables.append(variable)
                variable_dict[variable.symbol.name()] = variable

        model.der_states = [Variable.from_dict(d) for d in db['der_states']]
        model.outputs = db['outputs']
        model.delay_states = db['delay_states']
        model.alias_relation = db['alias_relation']

        # Evaluate variable metadata:
        parameter_vector = ca.veccat(*[v.symbol for v in model.parameters])
        metadata = dict(zip(variables_with_metadata, model.variable_metadata_function(parameter_vector)))
        independent_metadata = dict(zip(
            variables_with_metadata,
            (np.array(x) for x in model.variable_metadata_function(ca.veccat(*[np.nan for v in model.parameters])))))

        for k, key in enumerate(variables_with_metadata):
            m = db[key + "__metadata_dependent"]
            for i, d in enumerate(db[key]):
                variable = variable_dict[d['name']]
                for j, tmp in enumerate(CASADI_ATTRIBUTES):
                    if m[i, j]:
                        setattr(variable, tmp, metadata[key][i, j])
                    else:
                        setattr(variable, tmp, independent_metadata[key][i, j])

        # Evaluate delay arguments:
        if model.delay_states:
            args = [model.time,
                    ca.veccat(*model._symbols(model.states)),
                    ca.veccat(*model._symbols(model.der_states)),
                    ca.veccat(*model._symbols(model.alg_states)),
                    ca.veccat(*model._symbols(model.inputs)),
                    ca.veccat(*model._symbols(model.constants)),
                    ca.veccat(*model._symbols(model.parameters))]
            delay_arguments_raw = model.delay_arguments_function(*args)

            nan_args = [ca.repmat(np.nan, *arg.size()) for arg in args]
            independent_delay_arguments_raw = model.delay_arguments_function(*nan_args)

            delay_expressions_raw = delay_arguments_raw[::2]
            delay_durations_raw = delay_arguments_raw[1::2]
            independent_delay_durations_raw = independent_delay_arguments_raw[1::2]

            assert 1 == len({len(delay_expressions_raw), len(delay_durations_raw),
                len(independent_delay_durations_raw)})

            all_symbols = [model.time,
                           *model._symbols(model.states),
                           *model._symbols(model.der_states),
                           *model._symbols(model.alg_states),
                           *model._symbols(model.inputs),
                           *model._symbols(model.constants),
                           *model._symbols(model.parameters)]

            duration_dependencies = db['__delay_duration_dependent']

            # Get rid of false dependency symbols not used in any delay
            # durations. This significantly reduces the work the (slow)
            # substitute() calls have to do later on.
            actual_deps = sorted(set(np.array(duration_dependencies).ravel()))

            actual_dep_symbols = [np.nan] * len(all_symbols)
            for i in actual_deps:
                actual_dep_symbols[i] = all_symbols[i]

            delay_durations_simplified = ca.Function(
                'replace_false_deps',
                all_symbols,
                delay_durations_raw).call(
                    actual_dep_symbols)

            # Get rid of remaining hidden dependencies in the delay durations
            for i, expr in enumerate(delay_expressions_raw):
                if duration_dependencies[i]:
                    dur = delay_durations_simplified[i]

                    if len(duration_dependencies[i]) < len(actual_deps):
                        deps = set(ca.symvar(dur))
                        actual_deps = {all_symbols[j] for j in duration_dependencies[i]}
                        false_deps = deps - actual_deps

                        if false_deps:
                            [dur] = ca.substitute(
                                [dur],
                                list(false_deps),
                                [np.nan] * len(false_deps))
                    else:
                        # Already removed all false dependencies
                        pass
                else:
                    dur = independent_delay_durations_raw[i]

                model.delay_arguments.append(DelayArgument(expr, dur))

        # Try to coerce parameters into their Python types
        for p in model.parameters:
            for attr in CASADI_ATTRIBUTES:
                v = getattr(p, attr)
                v_mx = ca.MX(v)
                if v_mx.is_constant() and v_mx.is_regular():
                    setattr(p, attr, p.python_type(v))

    # Done
    return model