From 1cadd6c5f233828ed3f98c566d9420c5c5018d6a Mon Sep 17 00:00:00 2001 From: Jean-Matthieu Etancelin <jean-matthieu.etancelin@imag.fr> Date: Fri, 6 Jul 2012 14:16:32 +0000 Subject: [PATCH] Cleaning parmepy (in progress ...) --- Examples/mainJM.py | 101 +++++---- HySoP/hysop/ParticularSolvers/basic.py | 173 ++++++++------- HySoP/hysop/ParticularSolvers/solver.py | 9 +- HySoP/hysop/domain/box.py | 3 + HySoP/hysop/fields/__init__.py | 6 + HySoP/hysop/fields/continuous.py | 23 +- HySoP/hysop/fields/discrete.py | 52 ++++- .../operator/{Advection.py => Transport.py} | 16 +- HySoP/hysop/operator/advection_d.py | 198 +++++++++--------- HySoP/hysop/operator/continuous.py | 15 +- HySoP/hysop/operator/discrete.py | 8 - HySoP/hysop/problem/ContinuousProblem.py | 130 ------------ .../problem/ContinuousTransportProblem.py | 90 -------- HySoP/hysop/problem/__init__.py | 10 +- HySoP/hysop/problem/continuous.py | 79 +++++++ .../{DiscreteProblem.py => discrete.py} | 0 HySoP/hysop/problem/problem.py | 90 ++++++++ 17 files changed, 516 insertions(+), 487 deletions(-) rename HySoP/hysop/operator/{Advection.py => Transport.py} (65%) delete mode 100644 HySoP/hysop/problem/ContinuousProblem.py delete mode 100644 HySoP/hysop/problem/ContinuousTransportProblem.py create mode 100644 HySoP/hysop/problem/continuous.py rename HySoP/hysop/problem/{DiscreteProblem.py => discrete.py} (100%) create mode 100644 HySoP/hysop/problem/problem.py diff --git a/Examples/mainJM.py b/Examples/mainJM.py index 3e262b37b..31370b43b 100644 --- a/Examples/mainJM.py +++ b/Examples/mainJM.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import time -import new_ParMePy as pp +import parmepy as pp def vitesse(x): @@ -16,56 +16,73 @@ def scalaire(x): def run(): # Parameter - dim = 3 - nb = 128 - boxlength = [1., 1., 1.] - boxMin = [0., 0., 0.] - nbElem = [nb, nb, nb] - - timeStep = 0.02 - FinalTime = 1. - outputFilePrefix = './res/RK2_' - outputModulo = 0 + # timeStep = 0.02 + # FinalTime = 1. + # outputFilePrefix = './res/RK2_' + # outputModulo = 0 t0 = time.time() - box = pp.Box(dimension=dim, - length=boxlength, - minPosition=boxMin) - velo = pp.AnalyticalVariable(domain=box, - dimension=dim, - formula=vitesse, name='Velocity') - scal = pp.AnalyticalVariable(domain=box, - dimension=1, - formula=scalaire, scalar=True, name='Scalar') - advec = pp.Advection(velo, scal) - cTspPb = pp.ContinuousTransportProblem(advection=advec) - - cTspPb.discretize(dSpec=[nbElem]) - - InterpolationMethod = pp.Linear(grid=box.discreteDomain, gvalues=velo.discreteVariable) - RemeshingMethod = pp.M6Prime(grid=box.discreteDomain, gvalues=scal.discreteVariable) - ODESolver = pp.RK2(InterpolationMethod, box.discreteDomain.applyConditions) - ## cpu - #solver = pp.ParticularSolver(cTspPb, ODESolver, InterpolationMethod, RemeshingMethod) - ## gpu - solver = pp.GPUParticularSolver(cTspPb, - ODESolver, InterpolationMethod, RemeshingMethod, - device_type='gpu', src='./examples/mainJM_kernels.cl' - ) - #solver = pp.GPUParticularSolver_GLRender(cTspPb, ODESolver, InterpolationMethod, RemeshingMethod, device_type='gpu', dt=timeStep) - cTspPb.setSolver(solver) - cTspPb.setPrinter(pp.Printer(frequency=outputModulo, outputPrefix=outputFilePrefix, problem=cTspPb)) + ## Domain + box = pp.Box(3, length=[1., 1., 1.], origin=[0., 0., 0.]) + + ## Fields + scal = pp.ContinuousField(domain=box, name='Scalar') + velo = pp.ContinuousField(domain=box, name='Velocity', vector=True) + + ## Operator + advec = pp.Transport(velo, scal) + + ## Solver creation (discretisation of objects is done in solver initialisation) + topo3D = pp.CartesianTopology(domain=box, resolution=[4, 4, 4], dim=3) + + ##Problem + pb = pp.Problem(topo3D, [advec]) + + ## Setting solver to Problem + pb.setSolver(pp.ParticularSolver) + + # ## Setting io to Problem + # pb.setSolver(pp.Printer(...)) t1 = time.time() - cTspPb.solve(T=FinalTime, dt=timeStep) + # ## Solve problem + # pb.solve(T=FinalTime, dt=timeStep) - tf = time.time() + # tf = time.time() - print "Total time : ", tf - t0, "sec (CPU)" + print pb + print "\n TODO : -terminer l'initialisation du solver (opérateurs) \n\t -Ajouter les opérateurs annexes (timer et io) au probleme \n\t -Ecrire méthode de résolution" + + # print "Total time : ", tf - t0, "sec (CPU)" print "Init time : ", t1 - t0, "sec (CPU)" - print "Solving time : ", tf - t1, "sec (CPU)" + # print "Solving time : ", tf - t1, "sec (CPU)" + + + # InterpolationMethod = pp.Linear(grid=box.discreteDomain, gvalues=velo.discreteVariable) + # RemeshingMethod = pp.M6Prime(grid=box.discreteDomain, gvalues=scal.discreteVariable) + # ODESolver = pp.RK2(InterpolationMethod, box.discreteDomain.applyConditions) + # ## cpu + # #solver = pp.ParticularSolver(cTspPb, ODESolver, InterpolationMethod, RemeshingMethod) + # ## gpu + # solver = pp.GPUParticularSolver(cTspPb, + # ODESolver, InterpolationMethod, RemeshingMethod, + # device_type='gpu', src='./examples/mainJM_kernels.cl' + # ) + # #solver = pp.GPUParticularSolver_GLRender(cTspPb, ODESolver, InterpolationMethod, RemeshingMethod, device_type='gpu', dt=timeStep) + # cTspPb.setSolver(solver) + # cTspPb.setPrinter(pp.Printer(frequency=outputModulo, outputPrefix=outputFilePrefix, problem=cTspPb)) + + # t1 = time.time() + + # cTspPb.solve(T=FinalTime, dt=timeStep) + + # tf = time.time() + + # print "Total time : ", tf - t0, "sec (CPU)" + # print "Init time : ", t1 - t0, "sec (CPU)" + # print "Solving time : ", tf - t1, "sec (CPU)" if __name__ == "__main__": diff --git a/HySoP/hysop/ParticularSolvers/basic.py b/HySoP/hysop/ParticularSolvers/basic.py index 714c18f9e..d6f7189b1 100644 --- a/HySoP/hysop/ParticularSolvers/basic.py +++ b/HySoP/hysop/ParticularSolvers/basic.py @@ -3,16 +3,8 @@ @package Utils Particular solver description. """ -from ..Param import * -from Solver import Solver -from ..Domain.ParticleField import ParticleField -from ..Variable.DiscreteVariable import DiscreteVariable -from ..Operator.RemeshingDOp import RemeshingDOp -from ..Operator.TagDOp import TagDOp -from ..Operator.InterpolationDOp import InterpolationDOp -from ..Operator.RemeshingDOp import RemeshingDOp - -from Lambda2 import Lambda2 +from solver import Solver +from ..fields.continuous import ContinuousField class ParticularSolver(Solver): @@ -21,71 +13,75 @@ class ParticularSolver(Solver): Link with differents numericals methods used. """ def __init__(self, problem, - ODESolver, - InterpolationMethod, - RemeshingMethod, + ODESolver = None, + InterpolationMethod = None, + RemeshingMethod = None, splittingMethod='strang'): """ Constructor. Create a solver description. """ Solver.__init__(self, problem) - self.discreteProblem = problem.discreteProblem - ## ODE Solver method. - self.ODESolver = ODESolver - ## Interpolation method. - self.InterpolationMethod = InterpolationMethod - ## Remeshing method. - self.RemeshingMethod = RemeshingMethod - ## Splitting Method - self.splittingMethod = splittingMethod - self.dim = self.discreteProblem.domains[0].dimension - self.grid = self.discreteProblem.domains[0] - self.gvelo = self.discreteProblem.advecOp.velocity - self.gscal = self.discreteProblem.advecOp.scalar - self.parts = ParticleField(self.grid) - self.ppos = DiscreteVariable(domain=self.parts, dimension=self.gvelo.dimension, name="Particle position") - self.parts.setPositions(self.ppos) - self.pvelo = DiscreteVariable(domain=self.parts, dimension=self.gvelo.dimension, name="Particle velocity") - self.pscal = DiscreteVariable(domain=self.parts, dimension=self.gscal.dimension, scalar=True, name="Particle scalar") - #self.pbloc = DiscreteVariable(domain=self.parts, dimension=1, integer=True, initialValue=lambda x: 0, scalar=True) - #self.ptag = DiscreteVariable(domain=self.parts, dimension=1, integer=True, initialValue=lambda x: 0, scalar=True) - # Interpolation : particles positions, grid velocity -> particles velocity - interpol = InterpolationDOp(position=self.ppos, - velocityField=self.discreteProblem.advecOp.velocity, - resvelo=self.pvelo, - method=self.InterpolationMethod) - if isinstance(self.RemeshingMethod, Lambda2): - # Tag particles : particles positions, particles velocities -> particles bloc, particles Tag - tag = TagDOp(partPositions=self.ppos, - partVelocities=self.pvelo, - grid=self.discreteProblem.domains[0], - resbloc=self.pbloc, - restag=self.ptag) - # Remeshing : particles positions, particles scalar, particles tag, particles bloc -> grid scalar - self.remesh = RemeshingDOp(partPositions=self.ppos, - partScalar=self.pscal, - resscal=self.discreteProblem.advecOp.scalar, - method=self.RemeshingMethod) - # Advection : grid position, grid velocity, grid scalar -> particles position, particles scalar - self.discreteProblem.advecOp.setResultVariable(respos=self.ppos, resscal=self.pscal) - self.discreteProblem.advecOp.setMethod(self.ODESolver) - if isinstance(self.RemeshingMethod, Lambda2): - self.discreteProblem.addOperator([interpol, tag, self.remesh]) - else: - self.discreteProblem.addOperator([self.remesh]) + self.problem = problem + self.p_position = ContinuousField(domain=problem.domains[0], name='Particle Position') + self.p_scalar = ContinuousField(domain=problem.domains[0], name='Particle Scalar') + self.problem.addVariable([self.p_position, self.p_scalar]) + # self.discreteProblem = problem.discreteProblem + # ## ODE Solver method. + # self.ODESolver = ODESolver + # ## Interpolation method. + # self.InterpolationMethod = InterpolationMethod + # ## Remeshing method. + # self.RemeshingMethod = RemeshingMethod + # ## Splitting Method + # self.splittingMethod = splittingMethod + # self.dim = self.discreteProblem.domains[0].dimension + # self.grid = self.discreteProblem.domains[0] + # self.gvelo = self.discreteProblem.advecOp.velocity + # self.gscal = self.discreteProblem.advecOp.scalar + # self.parts = ParticleField(self.grid) + # self.ppos = DiscreteVariable(domain=self.parts, dimension=self.gvelo.dimension, name="Particle position") + # self.parts.setPositions(self.ppos) + # self.pvelo = DiscreteVariable(domain=self.parts, dimension=self.gvelo.dimension, name="Particle velocity") + # self.pscal = DiscreteVariable(domain=self.parts, dimension=self.gscal.dimension, scalar=True, name="Particle scalar") + # #self.pbloc = DiscreteVariable(domain=self.parts, dimension=1, integer=True, initialValue=lambda x: 0, scalar=True) + # #self.ptag = DiscreteVariable(domain=self.parts, dimension=1, integer=True, initialValue=lambda x: 0, scalar=True) + # # Interpolation : particles positions, grid velocity -> particles velocity + # interpol = InterpolationDOp(position=self.ppos, + # velocityField=self.discreteProblem.advecOp.velocity, + # resvelo=self.pvelo, + # method=self.InterpolationMethod) + # if isinstance(self.RemeshingMethod, Lambda2): + # # Tag particles : particles positions, particles velocities -> particles bloc, particles Tag + # tag = TagDOp(partPositions=self.ppos, + # partVelocities=self.pvelo, + # grid=self.discreteProblem.domains[0], + # resbloc=self.pbloc, + # restag=self.ptag) + # # Remeshing : particles positions, particles scalar, particles tag, particles bloc -> grid scalar + # self.remesh = RemeshingDOp(partPositions=self.ppos, + # partScalar=self.pscal, + # resscal=self.discreteProblem.advecOp.scalar, + # method=self.RemeshingMethod) + # # Advection : grid position, grid velocity, grid scalar -> particles position, particles scalar + # self.discreteProblem.advecOp.setResultVariable(respos=self.ppos, resscal=self.pscal) + # self.discreteProblem.advecOp.setMethod(self.ODESolver) + # if isinstance(self.RemeshingMethod, Lambda2): + # self.discreteProblem.addOperator([interpol, tag, self.remesh]) + # else: + # self.discreteProblem.addOperator([self.remesh]) - # Splitting Step TODO:Transformer le splitting en opérateur d'opérateur - splitting = [] - if self.splittingMethod == 'strang': - [splitting.append((i, 0.5)) for i in xrange(self.dim - 1)] - splitting.append((self.dim - 1, 1.)) - [splitting.append((self.dim - 2 - i, 0.5)) for i in xrange(self.dim - 1)] - else: - raise ValueError("Unknown splitting method : ", self.splittingMethod) - self.discreteProblem.operator_list = [] - [self.discreteProblem.operator_list.append((op, 1.0, 0)) for op in self.discreteProblem.operators if not op.is_splittable] - [[self.discreteProblem.operator_list.append((op, i[1], i[0])) for op in self.discreteProblem.operators if op.is_splittable] for i in splitting] + # # Splitting Step TODO:Transformer le splitting en opérateur d'opérateur + # splitting = [] + # if self.splittingMethod == 'strang': + # [splitting.append((i, 0.5)) for i in xrange(self.dim - 1)] + # splitting.append((self.dim - 1, 1.)) + # [splitting.append((self.dim - 2 - i, 0.5)) for i in xrange(self.dim - 1)] + # else: + # raise ValueError("Unknown splitting method : ", self.splittingMethod) + # self.discreteProblem.operator_list = [] + # [self.discreteProblem.operator_list.append((op, 1.0, 0)) for op in self.discreteProblem.operators if not op.is_splittable] + # [[self.discreteProblem.operator_list.append((op, i[1], i[0])) for op in self.discreteProblem.operators if op.is_splittable] for i in splitting] def initialize(self): """ @@ -93,32 +89,29 @@ class ParticularSolver(Solver): @param discreteProblem : Problem to initialize. """ - ### Create grid.points TODO: A enlever - self.grid.points = np.empty_like(self.gvelo.values, dtype=dtype_real) - if self.dim == 1: - for x in xrange(self.grid.elementNumber[0]): - self.grid.points[x] = self.grid.axes[0][x] - if self.dim == 2: - for x in xrange(self.grid.elementNumber[0]): - for y in xrange(self.grid.elementNumber[1]): - self.grid.points[x, y][0] = self.grid.axes[0][x] - self.grid.points[x, y][1] = self.grid.axes[1][y] - if self.dim == 3: - for x in xrange(self.grid.elementNumber[0]): - for y in xrange(self.grid.elementNumber[1]): - for z in xrange(self.grid.elementNumber[2]): - self.grid.points[x, y, z][0] = self.grid.axes[0][x] - self.grid.points[x, y, z][1] = self.grid.axes[1][y] - self.grid.points[x, y, z][2] = self.grid.axes[2][z] - self.gvelo.init() - self.gscal.init() + # ### Create grid.points TODO: A enlever + # self.grid.points = np.empty_like(self.gvelo.values, dtype=dtype_real) + # if self.dim == 1: + # for x in xrange(self.grid.elementNumber[0]): + # self.grid.points[x] = self.grid.axes[0][x] + # if self.dim == 2: + # for x in xrange(self.grid.elementNumber[0]): + # for y in xrange(self.grid.elementNumber[1]): + # self.grid.points[x, y][0] = self.grid.axes[0][x] + # self.grid.points[x, y][1] = self.grid.axes[1][y] + # if self.dim == 3: + # for x in xrange(self.grid.elementNumber[0]): + # for y in xrange(self.grid.elementNumber[1]): + # for z in xrange(self.grid.elementNumber[2]): + # self.grid.points[x, y, z][0] = self.grid.axes[0][x] + # self.grid.points[x, y, z][1] = self.grid.axes[1][y] + # self.grid.points[x, y, z][2] = self.grid.axes[2][z] + # self.gvelo.init() + # self.gscal.init() def __str__(self): """ToString method""" s = " Particular solver " - s += "\n - ODESolver : " + str(self.ODESolver) - s += "\n - Interpolation : " + str(self.InterpolationMethod) - s += "\n - Remeshing : " + str(self.RemeshingMethod) return s if __name__ == "__main__": diff --git a/HySoP/hysop/ParticularSolvers/solver.py b/HySoP/hysop/ParticularSolvers/solver.py index d0a2ddbd1..0f133c266 100644 --- a/HySoP/hysop/ParticularSolvers/solver.py +++ b/HySoP/hysop/ParticularSolvers/solver.py @@ -1,21 +1,26 @@ # -*- coding: utf-8 -*- """ -@package Utils +@package solver Solver interface. """ -from ..Param import * +from abc import ABCMeta, abstractmethod class Solver: """ Solver interface. """ + + __metaclass__ = ABCMeta + + @abstractmethod def __init__(self, problem): """ Constructor of an interface. Do nothing. """ pass + @abstractmethod def initialize(self): """ Solver initialisation for a given DiscreteProblem.DiscreteProblem. diff --git a/HySoP/hysop/domain/box.py b/HySoP/hysop/domain/box.py index 6ba485a0d..ed0528797 100644 --- a/HySoP/hysop/domain/box.py +++ b/HySoP/hysop/domain/box.py @@ -33,6 +33,7 @@ class Box(Domain): # Boundary conditions type : self.boundaries = np.zeros((self.dimension)) self.boundaries[:] = PERIODIC + self.discreteDomain = None def discretize(self, resolution): """Box discretization method. @@ -47,6 +48,8 @@ class Box(Domain): """ Doc display. """ s = str(self.dimension) + "D box (parallelepipedic or rectangular) domain : \n" s += " origin : " + str(self.origin) + ", maxPosition :" + str(self.max) + ", lengthes :" + str(self.length) + "." + if self.discreteDomain is not None: + s += "Discretised on : \n" + str(self.discreteDomain) return s if __name__ == "__main__": diff --git a/HySoP/hysop/fields/__init__.py b/HySoP/hysop/fields/__init__.py index e69de29bb..cee2ba565 100644 --- a/HySoP/hysop/fields/__init__.py +++ b/HySoP/hysop/fields/__init__.py @@ -0,0 +1,6 @@ +""" +@package parmepy.fields + +Everything concerning Fields. + +""" diff --git a/HySoP/hysop/fields/continuous.py b/HySoP/hysop/fields/continuous.py index 09d8b5909..284d4d404 100644 --- a/HySoP/hysop/fields/continuous.py +++ b/HySoP/hysop/fields/continuous.py @@ -4,12 +4,13 @@ Continuous variable description """ from .discrete import ScalarField +from .discrete import VectorField class ContinuousField(object): """ A variable defined on a physical domain """ - def __init__(self, domain, name="?"): + def __init__(self, domain, name="?", vector=False): """ Constructor. Create a ContinuousField. @@ -29,6 +30,8 @@ class ContinuousField(object): self.__fieldId = -1 ## list of the various discretizations of this field self.discreteField = [] + ## is field is a vector field + self.vector = vector def discretize(self, topology=None): """ @@ -36,18 +39,28 @@ class ContinuousField(object): """ self.__fieldId += 1 - self.discreteField.append(ScalarField(self, topology, name=self.name + "_D_" + str(self.__fieldId), - idFromParent=self.__fieldId)) + print "Discretisation de ", self.name, + if self.vector: + print "Vector Field" + self.discreteField.append(VectorField(self, topology, + name=self.name + "_D_" + str(self.__fieldId), + idFromParent=self.__fieldId)) + else: + print "Scalar Field" + self.discreteField.append(ScalarField(self, topology, + name=self.name + "_D_" + str(self.__fieldId), + idFromParent=self.__fieldId)) return self.discreteField[self.__fieldId], self.__fieldId def __str__(self): """ Field info display """ - s = self.name + ' , ' + str(self.dimension) + 'D field with the following discretizations:\n' + s = self.name + ' , ' + str(self.dimension) + 'D field ' if len(self.discreteField) != 0: + s += "with the following discretizations:\n" for i in range(len(self.discreteField)): s += self.discreteField[i].__str__() else: - s += "None." + s += ". Not discretised\n" return s diff --git a/HySoP/hysop/fields/discrete.py b/HySoP/hysop/fields/discrete.py index 408351a5e..8f05381c2 100644 --- a/HySoP/hysop/fields/discrete.py +++ b/HySoP/hysop/fields/discrete.py @@ -83,33 +83,63 @@ class ScalarField(object): class VectorField(object): """ A vector field, defined on each grid of each subdomain of the given topology. - + Vector field is composed of several ScalarFields """ - def __init__(self, topology, name=""): - self.topology = topology + def __init__(self, parent, topology=None, name="?", idFromParent=None): + if(topology is not None): + self.topology = topology + else: + raise NotImplementedError() + self.name = name self.dimension = topology.domain.dimension resolution = self.topology.mesh.resolution - self.data = np.zeros((resolution), dtype=PARMES_REAL, order=ORDER) + ## \todo is numpy array zeros or empty??? + self.data = [np.zeros((resolution), dtype=PARMES_REAL, order=ORDER) for d in xrange(self.dimension)] + self.__parentField = parent + self.__idFromParent = idFromParent def __str__(self): """ Display class information """ - s = self.name + ": " + str(self.dimension) + "D scalar field of shape " + str(self.data.shape) + s = '[' + str(self.topology.rank) + '] ' + s += " id " + str(self.__idFromParent) + ", " + s += self.name + " " + str(self.dimension) + 'D discrete vector field with resolution ' + s += str([data.shape for data in self.data]) + "." return s + "\n" def __getitem__(self, i): - """ Access to the content of the field """ - return self.data.__getitem__(i) + """ Access to the content of the field + Usage (3D): A = VectorField(...), We have 3 components len(a.data) == 3. + Following instructions access to index 2,1,1 of y component: + A[2,1,1,1] + A[1][2,1,1] + Access to whole vector of index 2,1,1: A[2,1,1] + """ + try: + if len(i) == len(self.data): + return [data.__getitem__(i) for data in self.data] + else: + return self.data[i[-1]].__getitem__(tuple(i[0:-1])) + except (TypeError): + return self.data[i] def __setitem__(self, i, value): """ - Set the content of the field component at position i + Set the content of the vector field component at position i Usage : - A = ScalarField(topo) - A[2,1,1] = 12.0 # Calls A.data[2,1,1] = 12.0 + A = VectorField(topo) + A[2,1,1] = 12.0 # Calls A.data[d][2,1,1] = 12.0 for all components d. + A[2,1,1] = [12.0, 13.0, 14.0] # Calls A.data[0][2,1,1] = 12.0, A.data[1][2,1,1] = 13.0 and A.data[2][2,1,1] = 14.0 + A[1][2,1,1] = 13.0 # Calls A.data[1][2,1,1] = 12.0 """ - self.data.__setitem__(i, value) + if len(i) == len(self.data): + try: + [data.__setitem__(i, v) for data, v in zip(self.data, value)] + except (TypeError): + [data.__setitem__(i, value) for data in self.data] + else: + self.data[i[-1]].__getitem__(tuple(i[0:-1])) def initialize(self, formula): """ diff --git a/HySoP/hysop/operator/Advection.py b/HySoP/hysop/operator/Transport.py similarity index 65% rename from HySoP/hysop/operator/Advection.py rename to HySoP/hysop/operator/Transport.py index 144b51902..00541de94 100644 --- a/HySoP/hysop/operator/Advection.py +++ b/HySoP/hysop/operator/Transport.py @@ -5,10 +5,10 @@ Advection operator representation """ from .continuous import ContinuousOperator -from .advection_d import AdvectionDOp +from .advection_d import Advection_P -class Advection(ContinuousOperator): +class Transport(ContinuousOperator): """ Advection operator representation """ @@ -26,8 +26,9 @@ class Advection(ContinuousOperator): self.velocity = velocity ## advected scalar variable self.scalar = scalar + self.addVariable([velocity, scalar]) - def discretize(self, spec=None): + def discretize(self, idVelocityD=0, idScalarD=0, result_position=None, result_scalar=None): """ Advection operator discretization method. Create an AdvectionDOp.AdvectionDOp from given specifications. @@ -35,11 +36,16 @@ class Advection(ContinuousOperator): @param spec : discretization specifications, not used. """ if self.discreteOperator is None: - self.discreteOperator = AdvectionDOp(self) + self.discreteOperator = Advection_P(self, idVelocityD, idScalarD, result_position, result_scalar) def __str__(self): """ToString method""" - s = " Advection operator (ContinuousOperator)" + s = "Advection operator (ContinuousOperator)" + if self.discreteOperator is not None: + s += "with the following discretization:\n" + s += str(self.discreteOperator) + else: + s += ". Not discretised" return s + "\n" if __name__ == "__main__": diff --git a/HySoP/hysop/operator/advection_d.py b/HySoP/hysop/operator/advection_d.py index 8aaab8ea2..94084acd9 100644 --- a/HySoP/hysop/operator/advection_d.py +++ b/HySoP/hysop/operator/advection_d.py @@ -8,122 +8,124 @@ import pyopencl as cl import time -class AdvectionDOp(DiscreteOperator): +class Advection_P(DiscreteOperator): """ - Advection operator representation. + Particle avection operator representation. DiscreteOperator.DiscreteOperator specialization. """ - def __init__(self, advec): + def __init__(self, advec, idVelocityD=0, idScalarD=0, result_position=None, result_scalar=None): """ Constructor. - Create a Transport operator on a given continuous domain. + Create a Advection operator on a given continuous domain. Work on a given scalar at a given velocity to produce scalar distribution at new positions. @param advec : Advection operator. """ DiscreteOperator.__init__(self) - ## Velocity. - self.velocity = advec.velocity.discreteVariable + ## Velocity. \todo utiliser un vectorField + self.velocity = advec.velocity.discreteField[idVelocityD] ## Transported scalar. - self.scalar = advec.scalar.discreteVariable - self.addVariable([self.velocity, self.scalar]) - self.gpu_kernel_name = "advection" - self.compute_time = [0., 0., 0.] - self.submit_time = 0. - self.queued_time = 0. - self.total_flop = 0 - self.total_bytes_accessed = 0 - - def setResultVariable(self, respos, resscal): - """ - Set results variables - - @param respos DiscreteVariable.DiscreteVariable : new positions - @param resscal DiscreteVariable.DiscreteVariable : scalar values - """ - ## Positions after advection - self.resultPosition = respos - ## Scalar value on new positions - self.resultScalar = resscal + self.scalar = advec.scalar.discreteField[idScalarD] + ## Result position + self.res_position = result_position + ## Result scalar + self.res_scalar = result_scalar + ## input fields + self.input = [self.velocity, self.scalar] + ## output fields + self.output = [self.res_position, self.res_scalar] + # self.gpu_kernel_name = "advection" + # self.compute_time = [0., 0., 0.] + # self.submit_time = 0. + # self.queued_time = 0. + # self.total_flop = 0 + # self.total_bytes_accessed = 0 def applyOperator(self, t, dt, splittingDirection): - """ - Apply advection operator. + raise NotImplementedMethod("Not yet implemented") + # """ + # Apply advection operator. - @param t : current time. - @param dt : time step for advection. - @param splittingDirection : direction to advect. - """ - if self.gpu_kernel is None: - c_time = time.time() - ## ------------------- NumPy Optimisation - # Particle update - self.resultScalar.values[...] = self.scalar.values[...] - self.resultPosition.values[...] = self.velocity.domain.points[...] - # Advection - self.numMethod.integrate(self.velocity.domain.points, self.velocity.values, self.resultPosition.values, t, dt, splittingDirection) - #print self.resultPosition.values - ## ------------------- - self.compute_time[splittingDirection] += (time.time() - c_time) - self.total_flop += self.numMethod.nb_flop * np.prod(self.resultPosition.values.shape[0:-1]) - else: - l = list(self.gpu_shape) - nb_ligne = l.pop(splittingDirection) - local_wg = list(l) - if len(l) == 2: - local_wg[0] = 1 - local_wg = tuple(local_wg) - else: - local_wg = None - if debug == 0: - evt_copy = cl.enqueue_copy(self.gpu_queue, self.resultScalar.gpu_mem_object, self.scalar.gpu_mem_object) - evt = self.gpu_kernel.advection(self.gpu_queue, tuple(l), local_wg, - self.velocity.gpu_mem_object, - self.scalar.gpu_mem_object, - self.resultPosition.gpu_mem_object, - self.resultScalar.gpu_mem_object, - dtype_real_gpu(t), dtype_real_gpu(dt), dtype_integer(splittingDirection), dtype_integer(nb_ligne), - self.velocity.domain.min.astype(dtype_real_gpu), - self.velocity.domain.length.astype(dtype_real_gpu), - self.velocity.domain.elementNumber.astype(dtype_integer), - self.velocity.domain.elementSize.astype(dtype_real_gpu) - ) - else: - evt = self.gpu_kernel.advection(self.gpu_queue, tuple(l), None, - self.velocity.gpu_mem_object, - self.scalar.gpu_mem_object, - self.resultPosition.gpu_mem_object, - self.resultScalar.gpu_mem_object, - self.debug_float_buffer, self.debug_integer_buffer, - dtype_real_gpu(t), dtype_real_gpu(dt), dtype_integer(splittingDirection), dtype_integer(nb_ligne), - self.velocity.domain.min.astype(dtype_real_gpu), - self.velocity.domain.length.astype(dtype_real_gpu), - self.velocity.domain.elementNumber.astype(dtype_integer), - self.velocity.domain.elementSize.astype(dtype_real_gpu) - ) - # self.gpu_queue.finish() - # cl.enqueue_copy(self.gpu_queue, self.resultPosition.values, self.resultPosition.gpu_mem_object) - # print self.resultPosition.values - self.gpu_queue.finish() - if debug > 0: - cl.enqueue_copy(self.gpu_queue, self.debug_float, self.debug_float_buffer) - cl.enqueue_copy(self.gpu_queue, self.debug_integer, self.debug_integer_buffer) - print " :: DEBUG :: ", self.debug_float - print " :: DEBUG :: ", self.debug_integer - self.queued_time += (evt.profile.submit - evt.profile.queued) * 1e-9 - self.submit_time += (evt.profile.start - evt.profile.submit) * 1e-9 - self.compute_time[splittingDirection] += (evt.profile.end - evt.profile.start) * 1e-9 - self.queued_time += (evt_copy.profile.submit - evt_copy.profile.queued) * 1e-9 - self.submit_time += (evt_copy.profile.start - evt_copy.profile.submit) * 1e-9 - self.compute_time[splittingDirection] += (evt_copy.profile.end - evt_copy.profile.start) * 1e-9 - self.total_flop += 20 * nb_ligne * np.prod(l) # 20 flop par particule d'une ligne - self.total_bytes_accessed += 2 * nb_ligne * np.prod(l) * 4 # 2 floats lus + 1+DIM float écrits - #print "Advection:", self.compute_time * 1e-9 + # @param t : current time. + # @param dt : time step for advection. + # @param splittingDirection : direction to advect. + # """ + # if self.gpu_kernel is None: + # c_time = time.time() + # ## ------------------- NumPy Optimisation + # # Particle update + # self.resultScalar.values[...] = self.scalar.values[...] + # self.resultPosition.values[...] = self.velocity.domain.points[...] + # # Advection + # self.numMethod.integrate(self.velocity.domain.points, self.velocity.values, self.resultPosition.values, t, dt, splittingDirection) + # #print self.resultPosition.values + # ## ------------------- + # self.compute_time[splittingDirection] += (time.time() - c_time) + # self.total_flop += self.numMethod.nb_flop * np.prod(self.resultPosition.values.shape[0:-1]) + # else: + # l = list(self.gpu_shape) + # nb_ligne = l.pop(splittingDirection) + # local_wg = list(l) + # if len(l) == 2: + # local_wg[0] = 1 + # local_wg = tuple(local_wg) + # else: + # local_wg = None + # if debug == 0: + # evt_copy = cl.enqueue_copy(self.gpu_queue, self.resultScalar.gpu_mem_object, self.scalar.gpu_mem_object) + # evt = self.gpu_kernel.advection(self.gpu_queue, tuple(l), local_wg, + # self.velocity.gpu_mem_object, + # self.scalar.gpu_mem_object, + # self.resultPosition.gpu_mem_object, + # self.resultScalar.gpu_mem_object, + # dtype_real_gpu(t), dtype_real_gpu(dt), dtype_integer(splittingDirection), dtype_integer(nb_ligne), + # self.velocity.domain.min.astype(dtype_real_gpu), + # self.velocity.domain.length.astype(dtype_real_gpu), + # self.velocity.domain.elementNumber.astype(dtype_integer), + # self.velocity.domain.elementSize.astype(dtype_real_gpu) + # ) + # else: + # evt = self.gpu_kernel.advection(self.gpu_queue, tuple(l), None, + # self.velocity.gpu_mem_object, + # self.scalar.gpu_mem_object, + # self.resultPosition.gpu_mem_object, + # self.resultScalar.gpu_mem_object, + # self.debug_float_buffer, self.debug_integer_buffer, + # dtype_real_gpu(t), dtype_real_gpu(dt), dtype_integer(splittingDirection), dtype_integer(nb_ligne), + # self.velocity.domain.min.astype(dtype_real_gpu), + # self.velocity.domain.length.astype(dtype_real_gpu), + # self.velocity.domain.elementNumber.astype(dtype_integer), + # self.velocity.domain.elementSize.astype(dtype_real_gpu) + # ) + # # self.gpu_queue.finish() + # # cl.enqueue_copy(self.gpu_queue, self.resultPosition.values, self.resultPosition.gpu_mem_object) + # # print self.resultPosition.values + # self.gpu_queue.finish() + # if debug > 0: + # cl.enqueue_copy(self.gpu_queue, self.debug_float, self.debug_float_buffer) + # cl.enqueue_copy(self.gpu_queue, self.debug_integer, self.debug_integer_buffer) + # print " :: DEBUG :: ", self.debug_float + # print " :: DEBUG :: ", self.debug_integer + # self.queued_time += (evt.profile.submit - evt.profile.queued) * 1e-9 + # self.submit_time += (evt.profile.start - evt.profile.submit) * 1e-9 + # self.compute_time[splittingDirection] += (evt.profile.end - evt.profile.start) * 1e-9 + # self.queued_time += (evt_copy.profile.submit - evt_copy.profile.queued) * 1e-9 + # self.submit_time += (evt_copy.profile.start - evt_copy.profile.submit) * 1e-9 + # self.compute_time[splittingDirection] += (evt_copy.profile.end - evt_copy.profile.start) * 1e-9 + # self.total_flop += 20 * nb_ligne * np.prod(l) # 20 flop par particule d'une ligne + # self.total_bytes_accessed += 2 * nb_ligne * np.prod(l) * 4 # 2 floats lus + 1+DIM float écrits + # #print "Advection:", self.compute_time * 1e-9 def __str__(self): """ToString method""" - return "AdvectionDOp (DiscreteOperator)" + s = "Advection_P (DiscreteOperator). Applying on following fields : \n Input fields : \n" + for f in self.input: + s += str(f) + "\n" + s += "Output fields : \n" + for f in self.output: + s += str(f) + "\n" + return s if __name__ == "__main__": print __doc__ diff --git a/HySoP/hysop/operator/continuous.py b/HySoP/hysop/operator/continuous.py index 6241f5617..ed2b750a0 100644 --- a/HySoP/hysop/operator/continuous.py +++ b/HySoP/hysop/operator/continuous.py @@ -20,11 +20,22 @@ class ContinuousOperator: Constructor. Create a ContinuousOperator. """ - ## Application domains of the variables. - self.domains = [] + ## Variables + self.variables = [] ## Operator discretization. self.discreteOperator = None + def addVariable(self, cVariable): + """ + Add an continuous variables to the operator. + Also add variables' domains to the operator. + + @param cVariable ContinuousVariable.ContinuousVariable : variables to add. + """ + for v in cVariable: + if self.variables.count(v) == 0: + self.variables.append(v) + @abstractmethod def discretize(self, spec=None): """ diff --git a/HySoP/hysop/operator/discrete.py b/HySoP/hysop/operator/discrete.py index d816c828b..2578a9755 100644 --- a/HySoP/hysop/operator/discrete.py +++ b/HySoP/hysop/operator/discrete.py @@ -35,14 +35,6 @@ class DiscreteOperator: """ self.numMethod = method - @abstractmethod - def setResultVariable(self): - """ - Abstract method, apply operaton on a variable. - Must be implemented by sub-class. - """ - raise NotImplementedError("Need to override method in a subclass of " + providedClass) - @abstractmethod def applyOperator(self): """ diff --git a/HySoP/hysop/problem/ContinuousProblem.py b/HySoP/hysop/problem/ContinuousProblem.py deleted file mode 100644 index cce6b951c..000000000 --- a/HySoP/hysop/problem/ContinuousProblem.py +++ /dev/null @@ -1,130 +0,0 @@ -# -*- coding: utf-8 -*- -""" -@package Problem -Problem representation -""" -from ..Param import * - - -class ContinuousProblem: - """ - Abstract description of continuous problem. - """ - - def __init__(self): - """ - Constructor. - Create an empty problem. - """ - ## Domains of the problem. - self.domains = [] - ## Variables of the problem. - self.variables = [] - ## Operators of the problem. - self.operators = [] - ## Discretization of the problem. - self.discreteProblem = None - - def addDomain(self, cDomain): - """ - Add a list of ContinuousDomain.ContinuousDomain to the problem. - - @param cDomain ContinuousDomain.ContinuousDomain : list of domains to add. - """ - for d in cDomain: - if self.domains.count(d) == 0: - self.domains.append(d) - - def addVariable(self, cVariable): - """ - Add a list of ContinuousVariable.ContinuousVariable to the problem. - Also add variables' domains to the problem. - - @param cVariable ContinuousVariable.ContinuousVariable : list of variables to add. - """ - for v in cVariable: - if self.variables.count(v) == 0: - self.variables.append(v) - if self.domains.count(v.domain) == 0: - self.domains.append(v.domain) - - def addOperator(self, cOperator, index=None): - """ - Add a list of ContinuousOperator.ContinuousOperator to the problem. - Also add operators' variables and variables' domains to the problem. - - @param cOperator ContinuousOperator.ContinuousOperator : list of operators to add. - """ - if isinstance(cOperator, list): - for i,o in enumerate(cOperator): - if self.operators.count(o) == 0: - try: - self.operators.insert(index[i],o) - except TypeError: - self.operators.append(o) - for v in o.variables: - if self.variables.count(v) == 0: - self.variables.append(v) - if self.domains.count(v.domain) == 0: - self.domains.append(v.domain) - else: - if self.operators.count(cOperator) == 0: - try: - self.operators.insert(index, cOperator) - except TypeError: - self.operators.append(cOperator) - for v in cOperator.variables: - if self.variables.count(v) == 0: - self.variables.append(v) - if self.domains.count(v.domain) == 0: - self.domains.append(v.domain) - - def discretize(self, dSpec=None, vSpec=None, oSpec=None): - """ - Abstract method. - Must be implemented by sub-class. - - @param dSpec list : ContinuousDomain.ContinuousDomain discretization specifications - @param vSpec list : ContinuousVariable.ContinuousVariable discretization specifications - @param oSpec list : ContinuousOperator.ContinuousOperator discretization specifications - """ - raise NotImplementedError("Need to override method in a subclass of " + providedClass) - - def setSolver(self, solver): - """ - Set solver for the discrete problem. - It must have a discrete Problem. - - @param solver : Solver.Solver to use. - """ - if self.discreteProblem == None: - raise NotImplementedError("Cannot set solver on a non discretized problem " + providedClass) - self.discreteProblem.setSolver(solver) - - def setPrinter(self, printer): - """ - Set result printer. - It must have a discrete Problem. - - @param printer : Printer.Printer to use. - """ - if self.discreteProblem == None: - raise NotImplementedError("Cannot set printer on a non discretized problem " + providedClass) - self.discreteProblem.setPrinter(printer) - - def solve(self, T, dt): - """ - Solving the problem. - It must have a discrete Problem to solve. - - @param T : Simulation final time - @param dt : Simulation time step - """ - if self.discreteProblem == None: - raise NotImplementedError("Cannot solve a non discretized problem " + providedClass) - self.discreteProblem.solve(T, dt) - -if __name__ == "__main__": - print __doc__ - print "- Provided class : ContinuousProblem" - print ContinuousProblem.__doc__ diff --git a/HySoP/hysop/problem/ContinuousTransportProblem.py b/HySoP/hysop/problem/ContinuousTransportProblem.py deleted file mode 100644 index 969a98eed..000000000 --- a/HySoP/hysop/problem/ContinuousTransportProblem.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -""" -@package Problem -Problem representation -""" -from ..Param import * -from ContinuousProblem import ContinuousProblem -from DiscreteTransportProblem import DiscreteTransportProblem - - -class ContinuousTransportProblem(ContinuousProblem): - """ - Continuous Transport problem representation. - """ - - def __init__(self, advection, domains=None, variables=None, operators=None): - """ - Constructor. - Create a transport problem instance. - - @param advection : Advection operator for transport problem. - @param domains : Additionals domains. - @param variables : Additionnals variables. - @param operators : Additionnals operators. - """ - ContinuousProblem.__init__(self) - ## Advection operator for the transport problem - self.advecOp = advection - self.addOperator([self.advecOp]) - if domains != None: - self.addDomain(domains) - if variables != None: - self.addVariable(variables) - if operators != None: - self.addOperator(operators) - - def discretize(self, dSpec=None, vSpec=None, oSpec=None): - """ - Transport operator discretization. - Create a DiscreteTransportProblem from given discretizations specifications. - - @param dSpec list : ContinuousDomain.ContinuousDomain discretization specifications, element number for each direction. - @param vSpec list : ContinuousVariable.ContinuousVariable discretization specifications, not used. - @param oSpec list : ContinuousOperator.ContinuousOperator discretization specifications, not used. - """ - if dSpec == None: - dSpec = [None for d in self.domains] - if vSpec == None: - vSpec = [None for d in self.variables] - if oSpec == None: - oSpec = [None for d in self.operators] - # Discretizations - for i in xrange(len(self.domains)): - self.domains[i].discretize(dSpec[i]) - for i in xrange(len(self.variables)): - self.variables[i].discretize(vSpec[i]) - for i in xrange(len(self.operators)): - self.operators[i].discretize(oSpec[i]) - self.discreteProblem = DiscreteTransportProblem(advection=self.advecOp.discreteOperator) - self.discreteProblem.addDomain([d.discreteDomain for d in self.domains]) - self.discreteProblem.addVariable([v.discreteVariable for v in self.variables]) - self.discreteProblem.addOperator([o.discreteOperator for o in self.operators], [self.operators.index(o) for o in self.operators]) - - def writeToFile(self, fileStr): - """ - Write the data to a file. - - @param fileStr String : file name - """ - self.discreteProblem.writeToFile(fileStr) - - def __str__(self): - """ToString method""" - s = "ContinuousTransportProblem (ContinuousProblem)\n" - s += " {0} domains, {1} variables and {2} operators.\n".format(len(self.domains), len(self.variables), len(self.operators)) - s += " Domains : \n" - for d in self.domains: - s += str(d) - s += "\n Variables : \n" - for v in self.variables: - s += str(v) - s += "\n Operators : \n" - for o in self.operators: - s += str(o) - return s - -if __name__ == "__main__": - print __doc__ - print "- Provided class : ContinuousTransportProblem" - print ContinuousTransportProblem.__doc__ diff --git a/HySoP/hysop/problem/__init__.py b/HySoP/hysop/problem/__init__.py index e6d9b54b3..1553dd6dc 100644 --- a/HySoP/hysop/problem/__init__.py +++ b/HySoP/hysop/problem/__init__.py @@ -1,4 +1,6 @@ -from ContinuousProblem import ContinuousProblem -from ContinuousTransportProblem import ContinuousTransportProblem -from DiscreteProblem import DiscreteProblem -from DiscreteTransportProblem import DiscreteTransportProblem +""" +@package parmepy.problem + +Everything concerning Problems. + +""" diff --git a/HySoP/hysop/problem/continuous.py b/HySoP/hysop/problem/continuous.py new file mode 100644 index 000000000..125e63005 --- /dev/null +++ b/HySoP/hysop/problem/continuous.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +""" +@package Problem +Problem representation +""" +from abc import ABCMeta, abstractmethod + + +class ContinuousProblem: + """ + Abstract description of continuous problem. + """ + + __metaclass__ = ABCMeta + + @abstractmethod + def __init__(self): + """ + Constructor. + Create an empty problem. + """ + ## Operators of the problem. + self.operators = [] + ## Discretization of the problem. + self.discreteProblem = None + + @abstractmethod + def discretize(self, spec=None): + """ + Abstract method. + Must be implemented by sub-class. + + @param dSpec list : ContinuousDomain.ContinuousDomain discretization specifications + @param vSpec list : ContinuousVariable.ContinuousVariable discretization specifications + @param oSpec list : ContinuousOperator.ContinuousOperator discretization specifications + """ + raise NotImplementedError("Need to override method in a subclass") + + @abstractmethod + def setSolver(self, solver): + """ + Set solver for the discrete problem. + It must have a discrete Problem. + + @param solver : Solver.Solver to use. + """ + if self.discreteProblem == None: + raise NotImplementedError("Cannot set solver on a non discretized problem ") + self.discreteProblem.setSolver(solver) + + @abstractmethod + def setPrinter(self, printer): + """ + Set result printer. + It must have a discrete Problem. + + @param printer : Printer.Printer to use. + """ + if self.discreteProblem == None: + raise NotImplementedError("Cannot set printer on a non discretized problem ") + self.discreteProblem.setPrinter(printer) + + @abstractmethod + def solve(self, T, dt): + """ + Solving the problem. + It must have a discrete Problem to solve. + + @param T : Simulation final time + @param dt : Simulation time step + """ + if self.discreteProblem == None: + raise NotImplementedError("Cannot solve a non discretized problem ") + self.discreteProblem.solve(T, dt) + +if __name__ == "__main__": + print __doc__ + print "- Provided class : ContinuousProblem" + print ContinuousProblem.__doc__ diff --git a/HySoP/hysop/problem/DiscreteProblem.py b/HySoP/hysop/problem/discrete.py similarity index 100% rename from HySoP/hysop/problem/DiscreteProblem.py rename to HySoP/hysop/problem/discrete.py diff --git a/HySoP/hysop/problem/problem.py b/HySoP/hysop/problem/problem.py new file mode 100644 index 000000000..2aa412159 --- /dev/null +++ b/HySoP/hysop/problem/problem.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +""" +@package Problem +Problem representation +""" + + +class Problem(): + """ + Problem representation. + """ + + def __init__(self, topology, operators, params=None): + """ + Constructor. + Create a transport problem instance. + + """ + self.domains = [] + self.variables = [] + self.operators = operators + for op in self.operators: + self.addVariable(op.variables) + for v in self.variables: + self.addDomain(v.domain) + self.topology = topology + self.solver = None + + def setSolver(self, solver): + self.solver = solver(self) + ## Discretise domains + for d in self.domains: + d.discretize(self.topology.resolution) + ## Discretise fields + for v in self.variables: + v.discretize(self.topology) + ## Discretise operators + for op in self.operators: + op.discretize(result_position=self.solver.p_position, result_scalar=self.solver.p_scalar) + + def addVariable(self, cVariable): + """ + Add an continuous variables to the operator. + Also add variables' domains to the operator. + + @param cVariable ContinuousVariable.ContinuousVariable : variables to add. + """ + try: + for v in cVariable: + if self.variables.count(v) == 0: + self.variables.append(v) + except (TypeError): + if self.variables.count(cVariable) == 0: + self.variables.append(cVariable) + + def addDomain(self, cDomain): + """ + Add an domain to the operator. + + @param cDomain : domain to add + """ + try: + for d in cDomain: + if self.domains.count(d) == 0: + self.domains.append(d) + except (TypeError): + if self.domains.count(cDomain) == 0: + self.domains.append(cDomain) + + def __str__(self): + """ToString method""" + s = "Problem based on\n" + s += str(self.topology) + s += "with following operators : \n" + for op in self.operators: + s += str(op) + s += "working on fields : \n" + for f in self.variables: + s += str(f) + s += "defined on domains : \n" + for d in self.domains: + s += str(d) + s += "\nsolved by : \n" + str(self.solver) + + return s + +if __name__ == "__main__": + print __doc__ + print "- Provided class : ContinuousTransportProblem" + print ContinuousTransportProblem.__doc__ -- GitLab