In the remainder of this document, it is assumed that an internal object is a C++ or python object that provides a first interface to the initial code. The form of this object and communication between the internal object and the initial code will be different depending on the form of the initial code (executable binary, static or dynamic library, f77, C, C++ or python source files).
In each case, the services and the internal state of the internal object will have to be defined. In particular:
Services will be implemented in the form of public methods and the internal state will be implemented in the form of attributes. If the designer of the object allows the user to access the attributes in read and write, he must provide services to access these attributes.
In the case of Fortran77 routines, C functions and C++ classes, the integrator will simply add a C++ envelope around these functions (see figure C++ internal object), to obtain the internal object. Each method in the object:
C++ internal object
Consider the following f77 fortran routines performing linear algebra calculations on one dimensional floating tables:
addvec.f
subroutine addvec(C,A,B,n)
integer i,n
double precision A(n), B(n), C(n)
C
do i=1, n
C(i) = A(i) + B(i)
enddo
C
return
end
prdscl.f
double precision function prdscl(A,B,n)
integer i,n
double precision A(n), B(n), S
C
S = 0.0D0
do i=1, n
S = S + A(i) * B(i)
enddo
C
prdscl = S
return
end
and a C++ class simulating a (very) rudimentary vector type:
vecteur.hxx (C++ interface)
class vecteur {
public:
vecteur(long n);
~vecteur();
double * x() { return xx; };
long n() { return nn; };
private:
double *xx;
long nn;
};
vecteur.cxx (C++ implementation)
#include "vecteur.hxx"
vecteur::vecteur(long n)
{
xx = new double[n];
nn = n;
}
vecteur::~vecteur()
{
delete [] xx;
}
The internal object (i.e. the C++ class) in the example is:
alglin.hxx
#include "vecteur.hxx"
class alglin {
public:
void addvec(vecteur *C, vecteur *A, vecteur *B);
double prdscl(vecteur *A, vecteur *B);
vecteur * create_vector(long n);
void destroy_vector(vecteur *V);
};
alglin.cxx
#include "alglin.hxx"
extern "C" {
double * valloc(long n);
void vfree(double *x);
void addvec_(double *C, double *A, double *B, long *n);
double prdscl_(double *A, double *B, long *n);
}
void alglin::addvec(vecteur *C, vecteur *A, vecteur *B) {
long n = A->n();
addvec_(C->x(), A->x(), B->x(), &n);
}
double alglin::prdscl(vecteur *A, vecteur *B) {
long n = A->n();
return prdscl_(A->x(), B->x(), &n);
}
vecteur * alglin::create_vector(long n) {
return new vecteur(n);
}
void alglin::destroy_vector(vecteur *V) {
delete V;
}
Notes:
Note the following in the above example:
The internal object can now be used in a C++ code:
alglin CompInterne;
long n = 5;
vecteur *A = CompInterne.create_vector(n);
vecteur *B = CompInterne.create_vector(n);
vecteur *C = CompInterne.create_vector(n);
// ...
CompInterne.addvec(C, A, B);
cout << CompInterne.prdscl(A, B) << endl;
The principle of encapsulation of python functions / classes in an internal object (python) is the same as in the previous case
Python internal object
An example similar to the previous example starts from Python functions to be encapsulated:
func.py
def addvec(x, y):
n = len(x)
z = []
for i in range(n):
z.append(x[i] + y[i])
return z
def prdscl(x, y):
n = len(x)
S = 0
for i in range(n):
S = S + x[i] * y[i]
return S
It is easy to integrate these functions into a python class:
compo.py
import func
class compo:
def prdscl(self, x, y):
return func.prdscl(x, y)
def addvec(self, x, y):
return func.addvec(x, y)
import compo
x = []
y = []
for i in range(5):
ii = i+1
x.append(2*ii)
y.append(ii*ii)
C = compo.compo()
z = C.addvec(x, y)
print 'x = ', x
print 'y = ', y
print 'z = ', z
print C.prdscl(x,y)
This case occurs when there are no internal code sources (or when it is not required to integrate these sources into the internal architecture). It will be assumed that the code is in the form of a binary that can be executed by the operating system. Communications can be made with the code.
- by one or several files,
- by the command line,
- using the keyboard to answer questions from the code
- by one or several files,
- on-screen display.
Communication with executables is made using commands (available in C++ and in python):
The above commands are stored in order of increasing complexity (it is recommended that system should be used as much as possible).
It is required to use a “System” object that has 5 services:
The internal state of the object will be composed of the name of the current directory in which the services of the object (that is set by the cd service) will work.
In Python, the object class could be written:
systeme.py
from os import system, popen
from string import split
class Systeme:
def __init__(self):
self.repertoire = '.'
def cd(self, rep):
self.repertoire = rep
def cp(self, nom1, nom2):
system('cp '
+ self.repertoire + '/' + nom1 + ' '
+ self.repertoire + '/' + nom2)
def touch(self, nom):
system('touch '
+ self.repertoire + '/' + nom)
def rm(self, nom):
system('rm '
+ self.repertoire + '/' + nom)
def dir(self):
f = popen('ls ' + self.repertoire)
s = f.read()
f.close()
return split(s)
and its use from the python interpreter:
import systeme
S = systeme.Systeme()
print "create U1 ..."
S.touch('U1')
print "dir : ", S.dir()
print "copy U1 to U2 ..."
S.cp('U1', 'U2')
print "dir : ", S.dir()
print "delete U1 ..."
S.rm('U1')
print "dir : ", S.dir()
print "delete U2 ..."
S.rm('U2')
print "dir : ", S.dir()
Notes
This example shows a (very) partial interface of a binary executable FreeFem [FreeFem] in the form of a C++ object. The interface provides access to the definition of a 2D geometry through its boundary, and the approximate resolution of a simple equation (forced convection) on this geometry. The different methods of the internal object are:
The internal state of the object is composed of the geometry and the velocity field. The calculation method creates a file starting from its parameters and the internal state, and then starts a calculation loop (by a system call). The object does not recover the calculation results.
Comments
Two versions (C++ and python) are listed below.
FreeFem.hxx
#include <string>
#include <vector>
struct sBord {
string X;
string Y;
int n;
};
class FreeFem
{
public:
void Bords(vector<sBord> &B);
void Flux(string u1, string u2);
void Convection(string Cinit, double dt, long n);
private:
string VX_, VY_;
vector<sBord> B_;
};
FreeFem.cxx
#include "FreeFem.hxx"
#include <fstream>
void FreeFem::Bords(vector<sBord> &B) {
B_ = B;
}
void FreeFem::Flux(string u1, string u2) {
VX_ = u1;
VY_ = u2;
}
void FreeFem::Convection(string Cinit, double dt, long n) {
ofstream f("/tmp/example.edp");
int i, nB = B_.size();
for (i=0; i<nB; i++)
f << "border b" << i << "(t=0,1){"
<< B_[i].X << "; " << B_[i].Y << "; }" << endl;
f << "mesh th = buildmesh(";
for (i=0; i<nB; i++)
f << "b" << i << "(" << B_[i].n << ")"
<< ( i<nB-1 ? '+' : ')');
f << ";" << endl;
f << "fespace Vh(th,P1);" << endl;
f << "Vh v = " << Cinit << ";" << endl << "plot(v);" <<endl;
f << "real dt = " << dt << ", t=0;" << endl;
f << "Vh u1 = " << VX_ << ", u2 = " << VY_ << ";" << endl;
f << "int i;" << endl << "Vh vv,vo;" << endl
<< "for ( i=0; i<" << n << "; i++) {" << endl
<< "t += dt;" << endl << "vo=v;" << endl
<< "v=convect([u1,u2],-dt,vo);" << endl
<< "plot(v,wait=0);" << endl << "};" << endl;
f.close();
system("FreeFem++ /tmp/example.edp");
}
FreeFem.py
import os
class Bord:
def __init__(self, X, Y, n):
self.X = X
self.Y = Y
self.n = n
class FreeFem:
def __init__(self):
self.u1 = "0"
self.u2 = "0"
self.bords = [ Bord("x = cos(2*pi*t)", "y = sin(2*pi*t)", 100) ]
def Bords(self, b):
self.bords = b
def Flux(self, u1, u2):
self.u1 = u1
self.u2 = u2
def Convection(self, cond_init, dt, n):
f = open("/tmp/example.edp", "w")
s = ""
ib = 1
nb = len(self.bords)
for b in self.bords:
f.write("border b" + str(ib) + "(t=0,1){" + \
b.X + "; " + b.Y + "; };\n");
s = s + "b" + str(ib) + "(" + str(b.n)+ ")"
if (ib < nb):
s = s + "+ "
else:
s = s + ");"
ib = ib+1
f.write("mesh th = buildmesh(" + s + "\n");
f.write("fespace Vh(th,P1);\n");
f.write("Vh v = " + cond_init + ";\nplot(v);\n")
f.write("real dt = " + str(dt) + ", t=0;\n");
f.write("Vh u1 = " + str(self.u1) + \
", u2 = " + str(self.u2) + ";\n");
f.write("int i;\nVh vv,vo;\n"
"for ( i=0; i< " + str(n) + " ; i++) {\n"
"t += dt;\nvo=v;\nv=convect([u1,u2],-dt,vo);\n"
"plot(v,wait=0);\n};\n");
f.close()
os.system('FreeFem++ /tmp/example.edp');
Use from a C++ code or a python interpreter is similar in the 2 versions:
version C++
#include "FreeFem.hxx"
int main()
{
FreeFem C;
vector<sBord> B(1);
B[0].X = "x=cos(2*pi*t)";
B[0].Y = "y=sin(2*pi*t)";
B[0].n = 100;
C.Bords(B);
C.Flux("y", "-x");
C.Convection("exp(-10*((x-0.3)^2 +(y-0.3)^2))", 0.1, 40);
}
version python
from FreeFem import *
C = FreeFem()
C.Flux("y", "-x")
C.Bords( [ Bord("x=cos(2*pi*t)", "y=sin(2*pi*t)", 100)] );
C.Convection('exp(-10*((x-0.3)^2 +(y-0.3)^2))', 0.1, 50)