From cfc5f1cfe2b8e37f24747a8ab27f21ba3dfd3aa9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Franck=20P=C3=A9rignon?= <franck.perignon@imag.fr>
Date: Mon, 13 May 2013 14:07:40 +0000
Subject: [PATCH] Update analytical fields and operators

---
 HySoP/hysop/fields/__init__.py            | 22 ++++++++-
 HySoP/hysop/fields/analytical.py          | 56 -----------------------
 HySoP/hysop/fields/continuous.py          | 43 +++++++++++++++--
 HySoP/hysop/fields/discrete.py            |  2 +-
 HySoP/hysop/fields/scalar.py              | 18 ++++----
 HySoP/hysop/fields/vector.py              | 18 ++++----
 HySoP/hysop/operator/analytic.py          | 23 ++++++++--
 HySoP/hysop/operator/discrete/analytic.py | 13 ++----
 8 files changed, 98 insertions(+), 97 deletions(-)
 delete mode 100644 HySoP/hysop/fields/analytical.py

diff --git a/HySoP/hysop/fields/__init__.py b/HySoP/hysop/fields/__init__.py
index 16ef1de56..d709fa847 100644
--- a/HySoP/hysop/fields/__init__.py
+++ b/HySoP/hysop/fields/__init__.py
@@ -19,7 +19,6 @@
 # \code
 # from parmepy.domain.box import Box
 # from parmepy.fields.continuous import Field
-# from parmepy.analytical import AnalyticalField
 #
 # # First, the domain : a 3D box
 # dom = Box()
@@ -35,11 +34,30 @@
 #     valz = sin(z)
 #     return valx, valy, valz
 #
-# vorticity = AnalyticalField(dom, isVector=True, name='reference,
+# vorticity = Field(dom, isVector=True, name='reference,
 #     formula=compute_ref)
 #
 # # Compute the value of vorticity at point of coordinates (1,2,0) :
 # wx, wy, wz = vorticity.value(1.,2.,0.)
+#
+# # Modify the formula and add some extra arguments:
+# 
+# def compute_ref2(x,y,z,t,dt):
+#     valx = sin(x)*t
+#     valy = sin(y) + dt
+#     valz = sin(z)
+#     return valx, valy, valz
+#
+# vorticity.setFormula(compute_ref2)
+#
+# t = 4
+# dt = 8.3
+# # Compute vorticity using compute_ref2 for all the available
+# # discretizations.
+# # Warning : space coordinates are implicit but all extra-parameters
+# # must be explicitly given, in the same order as defined in compute_ref2.
+# # No keywords arguments allowed.
+# vorticity.initialize(t,dt)
 # \endcode
 #
 # Up to now, these are just descriptions of the fields. No discretisations, no
diff --git a/HySoP/hysop/fields/analytical.py b/HySoP/hysop/fields/analytical.py
deleted file mode 100644
index 98a09a4b9..000000000
--- a/HySoP/hysop/fields/analytical.py
+++ /dev/null
@@ -1,56 +0,0 @@
-"""
-@file fields/analytical.py
-
-Continuous variable description defined with an analytic formula.
-"""
-from parmepy.constants import debug
-from parmepy.fields.continuous import Field
-
-
-class AnalyticalField(Field):
-    """
-    A continuous variable defined with an analytic formula.
-
-    """
-
-    @debug
-    def __init__(self, domain, formula, name=None, isVector=False):
-        """
-        Create an AnalyticalField from a formula.
-
-        @param domain : variable application domain.
-        @param formula : a user-defined python function
-        @param name : name of the variable (may be used for vtk output).
-        @param isVector : true if the field is a vector.
-
-        @note formula is used in ScalarField or VectorField as a vectorized
-        function by numpy.
-        """
-        Field.__init__(self, domain, name, isVector)
-        ## Analytic formula.
-        self.formula = formula
-
-    def value(self, *pos):
-        """
-        Evaluation of the variable at a given position.
-
-        @param pos : Position to evaluate.
-        @return formula(pos): value of the formula at the given position.
-        """
-        return self.formula(*pos)
-
-    @debug
-    def initialize(self):
-        for dF in self.discreteFields.values():
-            dF.initialize(self.formula)
-
-    def __str__(self):
-        """ToString method"""
-        s = " --- Analytical variable --- \n"
-        s += Field.__str__(self)
-        return s
-
-if __name__ == "__main__":
-    print __doc__
-    print "- Provided class : AnalyticalField"
-    print AnalyticalField.__doc__
diff --git a/HySoP/hysop/fields/continuous.py b/HySoP/hysop/fields/continuous.py
index d51ef693f..1da1a1f79 100644
--- a/HySoP/hysop/fields/continuous.py
+++ b/HySoP/hysop/fields/continuous.py
@@ -38,12 +38,14 @@ class Field(object):
         return object.__new__(cls, *args, **kw)
 
     @debug
-    def __init__(self, domain, name=None, isVector=False):
+    def __init__(self, domain, formula=None, name=None, isVector=False):
         """
         Create a scalar of vector field which dimension is determined
         by its domain of definition.
 
         @param domain : physical domain where this field is defined.
+        @param formula : a user-defined python function
+        (default = 0 all over the domain)
         @param name : name of the variable.
         @param isVector : true if the field is a vector.
         """
@@ -63,6 +65,8 @@ class Field(object):
         self.discreteFields = {}
         ## Is this field a vector field?
         self.isVector = isVector
+        ## Analytic formula.
+        self._formula = formula
 
     @debug
     def discretize(self, topo):
@@ -88,22 +92,51 @@ class Field(object):
 
         return self.discreteFields[topo]
 
+    def setFormula(self, formula):
+        """
+        Set (or reset) a user-defined function to compute field
+        values.
+        @param formula : a user-defined python function
+        Warning: if set, this formula will
+        overwrite the (optional) formula given during
+        construction of this field.
+
+        """
+        self._formula = formula
+
     @debug
-    def initialize(self):
+    def initialize(self, *args):
         """
         Initialize all the discrete fields associated to this continuous field.
+        using the formula set during construction or with setFormula method.
+        If formula is not set, field values are set to zero.
 
-        @see VectorField.initialize
-        @see ScalarField.initialize
+        @param *args : see parmepy.fields
         """
         for dF in self.discreteFields.values():
-            dF.initialize()
+            dF.initialize(self._formula, *args)
+
+    def value(self, *pos):
+        """
+        Evaluation of the field at a given position, according
+        to the analytic formula given during construction.
+
+        @param pos : Position to evaluate.
+        @return formula(pos): value of the formula at the given position.
+        """
+        if self._formula is not None:
+            return self._formula(*pos)
+        else:
+            return 0.0
 
     def __str__(self):
         """ Field info display """
 
         s = '[' + str(main_rank) + '] " ' + self.name + ' ", '
         s += str(self.dimension) + 'D '
+        if self._formula is not None:
+            s += 'an analytic formula is associated to this field:'
+            s += str(self._formula.func_name)
         if self.isVector:
             s += 'vector field '
         else:
diff --git a/HySoP/hysop/fields/discrete.py b/HySoP/hysop/fields/discrete.py
index a7884643d..1ff8f84cb 100644
--- a/HySoP/hysop/fields/discrete.py
+++ b/HySoP/hysop/fields/discrete.py
@@ -103,7 +103,7 @@ class DiscreteField(object):
 
     @debug
     @abstractmethod
-    def initialize(self, formula=None):
+    def initialize(self, formula=None, *args, **kwargs):
         """Initialize values with given formula."""
 
     def get_data_method(self):
diff --git a/HySoP/hysop/fields/scalar.py b/HySoP/hysop/fields/scalar.py
index 0383f4755..4be9aaf69 100644
--- a/HySoP/hysop/fields/scalar.py
+++ b/HySoP/hysop/fields/scalar.py
@@ -46,7 +46,7 @@ class ScalarField(DiscreteField):
         self.data.__setitem__(i, value)
 
     @debug
-    def initialize(self, formula=None):
+    def initialize(self, formula=None, *args):
         """
         Initialize values with given formula.
 
@@ -58,22 +58,20 @@ class ScalarField(DiscreteField):
         @li 2D : float formula(float x, float y)
         where x,y,z are point coordinates.
         """
-        if formula is not None and not self.contains_data:
-            if __VERBOSE__:
-                print "...",
+        if formula is not None:
             v_formula = np.vectorize(formula)
             if self.dimension == 3:
-                self.data[...] = v_formula(*self.topology.mesh.coords)
+                self.data[...] = v_formula(*(self.topology.mesh.coords + args))
             elif self.dimension == 2:
-                self.data[...] = v_formula(*self.topology.mesh.coords)
+                self.data[...] = v_formula(*(self.topology.mesh.coords + args))
             else:
-                self.data[...] = formula(self.topology.mesh.coords)
+                self.data[...] = formula(*(self.topology.mesh.coords + args))
             self.data[...] = np.asarray(self.data,
                                         dtype=PARMES_REAL, order=ORDER)
-            self.contains_data = True
         else:
-            if __VERBOSE__:
-                print "No formula",
+            self.data[...] = 0.0
+
+        self.contains_data = True
 
     def norm(self):
         """
diff --git a/HySoP/hysop/fields/vector.py b/HySoP/hysop/fields/vector.py
index 8c4fd90f6..e4b4c7210 100644
--- a/HySoP/hysop/fields/vector.py
+++ b/HySoP/hysop/fields/vector.py
@@ -3,7 +3,6 @@
 
 Discrete vector field.
 """
-from parmepy import __VERBOSE__
 from parmepy.constants import np, ORDER, PARMES_REAL, debug
 from parmepy.fields.discrete import DiscreteField
 from numpy import linalg as la
@@ -77,7 +76,7 @@ class VectorField(DiscreteField):
             self.data[i[0]].__setitem__(tuple(i[1:]), value)
 
     @debug
-    def initialize(self, formula=None):
+    def initialize(self, formula=None, *args):
         """
         Initialize values with given formula.
 
@@ -89,13 +88,11 @@ class VectorField(DiscreteField):
         @li 2D : float, float formula(float x, float y)
         where x,y,z are point coordinates.
         """
-        if formula is not None and not self.contains_data:
-            if __VERBOSE__:
-                print "...",
+        if formula is not None:
             v_formula = np.vectorize(formula)
             if self.dimension == 3:
                 self.data[0][...], self.data[1][...], self.data[2][...] = \
-                    v_formula(*self.topology.mesh.coords)
+                    v_formula(*(self.topology.mesh.coords + args))
                 self.data[0][...] = np.asarray(self.data[0],
                                                dtype=PARMES_REAL, order=ORDER)
                 self.data[1][...] = np.asarray(self.data[1],
@@ -104,15 +101,16 @@ class VectorField(DiscreteField):
                                                dtype=PARMES_REAL, order=ORDER)
             elif self.dimension == 2:
                 self.data[0][...], self.data[1][...] = \
-                    v_formula(*self.topology.mesh.coords)
+                    v_formula(*(self.topology.mesh.coords + args))
                 self.data[0][...] = np.asarray(self.data[0],
                                                dtype=PARMES_REAL, order=ORDER)
                 self.data[1][...] = np.asarray(self.data[1],
                                                dtype=PARMES_REAL, order=ORDER)
-            self.contains_data = True
         else:
-            if __VERBOSE__:
-                print "No formula",
+            for d in xrange(self.dimension):
+                self.data[d][...] = 0.0
+
+        self.contains_data = True
 
     def norm(self):
         """
diff --git a/HySoP/hysop/operator/analytic.py b/HySoP/hysop/operator/analytic.py
index d5a0b3ccf..e7c23ea0b 100644
--- a/HySoP/hysop/operator/analytic.py
+++ b/HySoP/hysop/operator/analytic.py
@@ -14,22 +14,35 @@ class Analytic(Operator):
     """
 
     @debug
-    def __init__(self, variables, formula, resolutions, method='',
+    def __init__(self, variables, resolutions, formula=None, method=None,
                  **other_config):
         """
         Create an operator using an analytic formula to compute field(s).
         @param formula : the formula to be applied
         @param resolutions : list of resolutions (one per variable)
         """
-        if not callable(formula) and method.find('gpu') == -1:
-            raise ValueError("Analytic formula must be a function")
+
         if isinstance(variables, list):
             Operator.__init__(self, variables, method)
         else:
             Operator.__init__(self, [variables], method)
+
+        if formula is not None:
+            ## A formula applied to all variables of this operator
+            self.formula = formula
+            for v in self.variables:
+                v.setFormula(formula)
+        else:
+            self.formula = self.variables[0]._formula
+            for v in self.variables:
+                assert v._formula is self.formula
+
+        if self.formula is None and self.method.find('gpu') == -1:
+            raise ValueError("A formula must be set.")
+
+        ## Dict of resolutions (one per variable)
         self.resolutions = resolutions
         self.config = other_config
-        self.formula = formula
 
     @debug
     def setUp(self):
@@ -46,7 +59,7 @@ class Analytic(Operator):
             self.discreteFields[v] = v.discretize(topo)
 
         # Select and create discrete operator
-        if self.method.find('gpu') >= 0:
+        if self.method is not None and self.method.find('gpu') >= 0:
             from parmepy.gpu.gpu_analytic import GPUAnalytic
             self.discreteOperator = GPUAnalytic(self.discreteFields.values(),
                                                 method=self.method,
diff --git a/HySoP/hysop/operator/discrete/analytic.py b/HySoP/hysop/operator/discrete/analytic.py
index d663652b8..4d729d98c 100644
--- a/HySoP/hysop/operator/discrete/analytic.py
+++ b/HySoP/hysop/operator/discrete/analytic.py
@@ -31,18 +31,14 @@ class Analytic_D(DiscreteOperator):
         self.vformula = np.vectorize(self.formula)
 
     @debug
-    def apply(self, t=None, dt=None, ite=None):
+    def apply(self, *args):
         cTime = time.time()
         for df in self.variables:
-            args = list(df.topology.mesh.coords)
-            args.append(t)
-            args.append(dt)
-            args.append(ite)
             datatype = df.data[0].dtype
             if df.isVector:
                 if df.topology.dim == 3:
                     df.data[0][...], df.data[1][...], df.data[2][...] = \
-                        self.vformula(*args)
+                        self.vformula(*(df.topology.mesh.coords + args))
                     df.data[0][...] = np.asarray(df.data[0],
                                                  dtype=datatype, order=ORDER)
                     df.data[1][...] = np.asarray(df.data[1],
@@ -50,13 +46,14 @@ class Analytic_D(DiscreteOperator):
                     df.data[2][...] = np.asarray(df.data[2],
                                                  dtype=datatype, order=ORDER)
                 elif df.topology.dim == 2:
-                    df.data[0][...], df.data[1][...] = self.vformula(*args)
+                    df.data[0][...], df.data[1][...] = \
+                        self.vformula(*(df.topology.mesh.coords + args))
                     df.data[0][...] = np.asarray(df.data[0],
                                                  dtype=datatype, order=ORDER)
                     df.data[1][...] = np.asarray(df.data[1],
                                                  dtype=datatype, order=ORDER)
             else:
-                df.data[...] = self.vformula(*args)
+                df.data[...] = self.vformula(*(df.topology.mesh.coords + args))
                 df.data[...] = np.asarray(df.data,
                                           dtype=datatype, order=ORDER)
         for df in self.output:
-- 
GitLab