<jupyter_start><jupyter_text>Calling C++ **Introduction:** Pyton contains multiple ways of calling functions written in C++. This notebooks shows how to use the **cpptools** interface from the **consav** package. **Platforms:** It only works on **Windows** computers.  **Compilers:** One of these compilers must be installed:* **vs**: Free *Microsoft Visual Studio 2017/2019 Community Edition* ([link](https://visualstudio.microsoft.com/downloads/))* **intel:** Costly *Intel Parallel Studio 2018 Composer Edition* ([link](https://software.intel.com/en-us/parallel-studio-xe))For parallization we will use **OpenMP**.  Interface The C++ file used in this example is in **cppfuncs/example.cpp**. **Read the following information:**1. **Functions are automatically detected**. The provided .cpp file should include: `define EXPORT extern "C" __declspec(dllexport)` A function to be called in Python should be **decorated** as e.g.:  `EXPORT void fun_nostruct(double *X, double *Y, int N, double a, double b, int threads, char *txt)`  **Extra:** Functions are also detected in *directly* included .cpp files. 2. The following **argument types** are allowed: `int` `int*` `double` `double*` `bool` `bool*` and `char*` (for strings) + **user-defined structs** (see below).3. The foolowing **return types** are allowed: `void` `int` `double` and `bool`.4. **Class:** `cppfile = cpptools.link_to_cpp(filename,force_compile,structsmap,options,do_print)` returns an interface to an object with the **C++ functions as methods**.     `filename (str)`: C++ file with .cpp extension (full path)        `force_compile (bool,optional)`: compile even if .dll is present (default is True)        `structsmap (dict,optional)`: struct names as keys and associated pythonobj used in C++ as values (see below)        `options (dict,optional)`: compiler options (see below)        `do_print (bool,optional)`: print progress    5. **User-defined structs:** Assume `par` is a Python dict-like object (e.g. SimpleNamespace) then `structsmap={'par_struct':par}` allows `par_struct*` to be used as a type in C++ after `include "par_struct.cpp"`, which is automatically written (in the same folder as the *filename.cpp* file) with automatically inferred fields and their types. 6. **Compiler options:** Dictionary with the following elements:    `compiler (str)`: compiler choice (*vs* (default) or *intel*)        `vs_path (str)`: path to vs compiler (if *None* (default) then newest version found is used, e.g. *C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build/*)        `intel_path (str)`: path to intel compiler        `intel_vs_version (str)`: vs version used by intel compiler        `additional_cpp (str)`: additional cpp files to include (default is '')        `dllfilename (str)`: filename of resulting dll file (if *None* (default) it is *filename.dll* where *filename.cpp*)        `macros (dict/list)`: preprocessor macros (default is *None*)        **NLopt:** For installation see the notebook **02. Using NLopt in C++.ipynb**.    7. **Methods:**    `cppfile.delink()`: Delink C++ file (release the .dll file, so that it can be overwritten when e.g. re-compiling).        `cppfile.recompile()`: Delink, re-compile and re-link to C++ file.        `cppfile.clean_up()`: Delink, remove .dll file, and remove any struct files.  Imports<jupyter_code>%load_ext autoreload
%autoreload 2

import os
from types import SimpleNamespace
import numpy as np
import numba as nb

from consav import cpptools
DO_INTEL = True<jupyter_output><empty_output><jupyter_text>Setup<jupyter_code>par = SimpleNamespace()
par.N = 10
par.X = np.linspace(0,10,par.N)
par.Y = np.zeros(par.N)
par.a = 2.0
par.b = 1.0
par.threads = 4
par.txt = 'SUCCESS!'<jupyter_output><empty_output><jupyter_text>Visual Studio compiler  Link to C++<jupyter_code>example = cpptools.link_to_cpp('cppfuncs/example.cpp',structsmap={'par_struct':par},do_print=True)<jupyter_output>Linking to: cppfuncs/example.cpp

### finding all included files ###

par_struct.cpp
example_include.cpp

### writing structs ###

cppfuncs/par_struct.cpp

 int N;
 double* X;
 double* Y;
 double a;
 double b;
 int threads;
 char *txt;

### analyzing cppfuncs/example.cpp ###

function: fun
return type: void
argument types: ['par_struct*']

function: fun_nostruct
return type: void
argument types: ['double*', 'double*', 'int', 'double', 'double', 'int', 'char*']

### analyzing cppfuncs/par_struct.cpp ###

### analyzing cppfuncs/example_include.cpp ###

function: fun_nostruct_alt
return type: void
argument types: ['double*', 'double*', 'int', 'double', 'double', 'int', 'char*']

function: I_DO_NOTHING
return type: double
argument types: []

### compiling and linking ###

C++ files compiled
C++ files loaded

DONE!<jupyter_text>Calling functions<jupyter_code>example.fun(par)
assert np.allclose(par.X*(par.a+par.b),par.Y)
par.Y = np.zeros(par.N)
example.fun_nostruct(par.X,par.Y,par.N,par.a,par.b,par.threads,par.txt)
assert np.allclose(par.X*(par.a+par.b),par.Y)
par.Y = np.zeros(par.N)
example.fun_nostruct_alt(par.X,par.Y,par.N,par.a,par.b,par.threads,par.txt)
assert np.allclose(par.X*(par.a+par.b),par.Y)<jupyter_output><empty_output><jupyter_text>Re-compile<jupyter_code>example.recompile()
res = example.I_DO_NOTHING()
assert np.isclose(res,5.0)
example.delink()<jupyter_output><empty_output><jupyter_text>See all availible compiler options<jupyter_code>example.options<jupyter_output><empty_output><jupyter_text>Intel compiler<jupyter_code>if DO_INTEL:
    
    example = cpptools.link_to_cpp('cppfuncs/example.cpp',structsmap={'par_struct':par},options={'compiler':'intel'})

    example.fun(par)
    assert np.allclose(par.X*(par.a+par.b),par.Y)
    
    example.delink()<jupyter_output><empty_output><jupyter_text>Using the ModelClass<jupyter_code>from consav import ModelClass<jupyter_output><empty_output><jupyter_text>**Interface:**1. all elements in `namespaces` are availible as C++ structs with `_struct` appended (default, else specify `.cpp_structsmap` in `.settings()`).2. `cpptools.link_to_cpp()` is called by `.link_to_cpp` using `.cpp_filename` and the result is returned in `.cpp`.3. `cpp_options` is the compiler options.4. by default `force_compile=True` when calling `.link_to_cpp`<jupyter_code>class MyModelClass(ModelClass):   
    
    def settings(self):
        
        self.namespaces = []
        self.not_floats = ['N','threads'] # non-float scalar elements in namespaces (for safe type inference)
        
        self.cpp_filename = 'cppfuncs/example.cpp' # required
        self.cpp_options = {'compiler':'vs'} # optional
        self.cpp_structsmap = {'par':'par_struct'} # optional
        
    def setup(self):
        
        par = self.par
        
        par.N = 10
        par.a = 2.0
        par.b = 1.0
        par.threads = 4
        par.txt = 'SUCCESS!'
        
    def allocate(self):
        
        par = self.par
        par.X = np.linspace(0,10,par.N)
        par.Y = np.zeros(par.N)<jupyter_output><empty_output><jupyter_text>**Create:**<jupyter_code>model = MyModelClass('testcpp')
model.link_to_cpp(do_print=True)
par = model.par<jupyter_output>Linking to: cppfuncs/example.cpp

### finding all included files ###

par_struct.cpp
example_include.cpp

### writing structs ###

cppfuncs/sim_struct.cpp


cppfuncs/sol_struct.cpp


cppfuncs/par_struct.cpp

 int N;
 double a;
 double b;
 int threads;
 char *txt;
 double* X;
 double* Y;

### analyzing cppfuncs/example.cpp ###

function: fun
return type: void
argument types: ['par_struct*']

function: fun_nostruct
return type: void
argument types: ['double*', 'double*', 'int', 'double', 'double', 'int', 'char*']

### analyzing cppfuncs/par_struct.cpp ###

### analyzing cppfuncs/example_include.cpp ###

function: fun_nostruct_alt
return type: void
argument types: ['double*', 'double*', 'int', 'double', 'double', 'int', 'char*']

function: I_DO_NOTHING
return type: double
argument types: []

### compiling and linking ###

C++ files compiled
C++ files loaded

DONE!<jupyter_text>**Call C++:**<jupyter_code>model.cpp.fun(par)
assert np.allclose(par.X*(par.a+par.b),par.Y)<jupyter_output><empty_output><jupyter_text>**Re-compile:**<jupyter_code>model.cpp.recompile()<jupyter_output><empty_output><jupyter_text>**Multiple models:**<jupyter_code># a. don't compile when the .dll file is already loaded
model_alt = MyModelClass('testcpp_alt')
model_alt.link_to_cpp(force_compile=False) # default is force_compile = True

# b. function call
par = model_alt.par
model_alt.cpp.fun_nostruct(par.X,par.Y,par.N,par.a,par.b,par.threads,par.txt)
assert np.allclose(par.X*(par.a+par.b),par.Y)

# c. delink without removing the .dll file
model.cpp.delink()

# d. delete model -> automatically calls .cpp.delink()
del model_alt

# e. now we can compile again
model_alt = MyModelClass('testcpp_alt')
model_alt.link_to_cpp()
model_alt.cpp.clean_up()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14439341324073038, 'fraction_numerical': 0.0060490646353758264, 'mean_word_length': 4.873684210526315, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 52, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '281395', 'license': 'MIT', 'n_tokens_mistral': 3071, 'n_tokens_neox': 2991, 'n_words': 799, 'path': '05. C++/01. Calling C++.ipynb', 'repo_name': 'JMSundram/ConsumptionSavingNotebooks', 'size': '8927'}