Salome HOME
Windows support
[modules/med.git] / src / MEDCalc / tui / fieldproxy.py
1 # Copyright (C) 2011-2016  CEA/DEN, EDF R&D
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19 # Author : Guillaume Boulant (EDF)
20
21 import medcalc
22 import MEDCALC
23 import SALOME
24
25 from medcalc.medcorba import factory
26 dataManager = factory.getDataManager()
27 calculator  = factory.getCalculator()
28
29 # Some helper functions to deal with the fields and meshes
30 import sys
31 if sys.platform == "win32":
32   import MEDCouplingCompat as MEDCoupling
33 else:
34   import MEDCoupling
35 __mapTypeOfFieldLabel = {
36   MEDCoupling.ON_CELLS:    "ON_CELLS",
37   MEDCoupling.ON_NODES:    "ON_NODES",
38   MEDCoupling.ON_GAUSS_PT: "ON_GAUSS_PT",
39   MEDCoupling.ON_GAUSS_NE: "ON_GAUSS_NE"
40   }
41 #
42 def _typeOfFieldLabel(typeOfField):
43   # A field name could identify several MEDCoupling fields, that
44   # differ by their spatial discretization on the mesh (values on
45   # cells, values on nodes, ...). This spatial discretization is
46   # specified (at least) by the TypeOfField that is an integer value
47   # in this list:
48   # 0 = ON_CELLS
49   # 1 = ON_NODES
50   # 2 = ON_GAUSS_PT
51   # 3 = ON_GAUSS_NE
52   try:
53     return __mapTypeOfFieldLabel[typeOfField]
54   except IndexError as e:
55     return "UNKNOWN"
56 #
57
58 def newFieldProxy(fieldHandlerId):
59   '''
60   This creates a new FieldProxy wrapping the field whose
61   fieldHandlerId is passed in argument. The function requests the
62   DataManager to get the fieldHandler from its id.
63   '''
64   fieldHandler = dataManager.getFieldHandler(fieldHandlerId)
65   return FieldProxy(fieldHandler)
66 #
67
68 # This define the map between attributes of a FieldProxy and those of
69 # the associated FieldHandler
70 PROXY_ATTRIBUTES_MAP = {"id":None,
71                         "fieldseriesId":None,
72                         "fieldname":"name",
73                         "meshname":None,
74                         "meshid":None,
75                         "type":None,
76                         "iteration":"iteration",
77                         "order":"order",
78                         "source":"source"}
79
80 class FieldProxy:
81   """
82   This object is a proxy to manipulate a remote MEDCoupling field
83   from within the SALOME python interpreter. Remote means that the
84   MEDCoupling field is in the SALOME container and not in the
85   client. See UserGuide class for detailed documentation of what can
86   be done with a field proxy.
87   """
88   def __init__( self, fieldHandler ):
89     """
90     This defines the wrapping on the field specified by its
91     fieldHandler id.
92     """
93     self.__fieldHandler = fieldHandler
94     self.__restriction  = None
95     print(self.__repr__())
96   #
97   def __getattr__(self, name ):
98     """
99     This method realizes the read proxy pattern toward the field
100     handler.
101     """
102     # WRN: Note that the modification of this function can lead to
103     # coercion problem. Modify this function with extrem care.
104     return getattr( self.__fieldHandler, name )
105   #
106   def __setattr__(self, name, value):
107     """
108     This method realizes the write proxy pattern toward the field
109     handler. Only some attributes are writable. The list is
110     specified in the PROXY_ATTRIBUTES_MAP table.
111     """
112     if name in list(PROXY_ATTRIBUTES_MAP.keys()):
113       if PROXY_ATTRIBUTES_MAP[name] is not None:
114         medcalc.wrn("The modification of this attribute can't be done that way")
115         msg="Use f.update(%s=\"%s\") instead to ensure synchronisation of data."
116         medcalc.inf(msg%(PROXY_ATTRIBUTES_MAP[name],value))
117       else:
118         medcalc.err("The modification of the attribute %s is not possible"%name)
119     else:
120       self.__dict__[name] = value
121   #
122   def __repr__(self):
123     """
124     Return a string containing a printable representation of this
125     object (what is displayed when typing the variable name and
126     carriage return).
127     """
128     # We need first to update the handler because some data can
129     # have changed (the meshid for example in case of change of
130     # underlying mesh).
131     # __GBO__ : TODO check the performance
132     self.__fieldHandler = dataManager.getFieldHandler(self.id)
133     text = "field name (id)\t = %s (%s)\n"%(self.fieldname, self.id)
134     text+= "mesh name (id) \t = %s (%s)\n"%(self.meshname,self.meshid)
135     text+= "discretization \t = %s\n"%_typeOfFieldLabel(self.type)
136     text+= "(iter, order)  \t = (%s,%s)\n"%(str(self.iteration),str(self.order))
137     text+= "data source    \t = %s"%self.source
138     return text
139   #
140   def __str__(self):
141     """
142     This is what is displayed when you type 'print(myField)'. Note
143     that this function prints the values of the field and then you
144     must be aware that a huge amount of data could be
145     displayed. Moreover, it means that this operation triggers the
146     loading of the associated MEDCouplingFied data in the SALOME
147     container.
148     """
149     text = dataManager.getFieldRepresentation(self.id)
150     return text
151   #
152   def __add__(self, operande):
153     """
154     This makes the addition of two fields or the addition of a
155     scalar to a field. It depends weither the operande is a
156     FieldProxy or a simple scalar numerical value.
157     """
158     # The medcalc calculator could raise exceptions coming from
159     # MEDCoupling. Note that the fieldproxy instances are used
160     # from within the python console, and for ergonomic reason, we
161     # choose to not raise the possible exceptions to the console
162     # by a clear message. Keep this in mind for unit test. You
163     # have to test the return value, which should not be
164     # null. This principle is applyed for all operations.
165     try:
166       if isinstance(operande, FieldProxy):
167         # The operande is an other field
168         medcalc.inf("Addition of  %s and %s"%(self.fieldname, operande.fieldname))
169         rfieldHandler = calculator.add(self.__fieldHandler, operande.__fieldHandler)
170       else:
171         # The operande is a scalar numerical value that must be
172         # considered as an offset in a linear transformation
173         factor = 1
174         offset = operande
175         medcalc.inf("Application of the offset %s to %s" % (offset, self.fieldname))
176         rfieldHandler = calculator.lin(self.__fieldHandler, factor, offset)
177     except SALOME.SALOME_Exception as ex:
178       medcalc.err(ex.details.text)
179       return None
180
181     return FieldProxy(rfieldHandler)
182   #
183   def __radd__(self, operande):
184     """
185     The user typed 'operande+self', we replace by 'self+operande'
186     to automatically activate the __add__ method of fieldpoxy.
187     """
188     return self+operande
189   #
190   def __iadd__(self, operande):
191     """
192     These methods implements the augmented arithmetic assignments (+=)
193     """
194     medcalc.wrn("NOT IMPLEMENTED YET")
195   #
196   def __sub__(self,operande):
197     """
198     This makes the substraction of two fields or the substraction
199     of a scalar to a field. It depends weither the operande is a
200     FieldProxy or a simple scalar numerical value.
201     """
202     try:
203       if isinstance(operande, FieldProxy):
204         # The operande is an other field
205         medcalc.inf("Substraction of %s by %s"%(self.fieldname, operande.fieldname))
206         rfieldHandler = calculator.sub(self.__fieldHandler, operande.__fieldHandler)
207       else:
208         # The operande is a scalar numerical value that must be
209         # considered as an offset in a linear transformation
210         factor = 1
211         offset = -operande
212         medcalc.inf("Application of the offset %s to %s" % (offset, self.fieldname))
213         rfieldHandler = calculator.lin(self.__fieldHandler, factor, offset)
214     except SALOME.SALOME_Exception as ex:
215       medcalc.err(ex.details.text)
216       return None
217
218     return FieldProxy(rfieldHandler)
219   #
220   def __rsub__(self, operande):
221     """
222     The user typed 'operande-self' where operande is not a field
223     proxy. This function process the situation.
224     """
225     # The operande is a numerical value (because otherwise, the
226     # "sub" method would have been called instead). We may apply
227     # the command '(self-operande)*(-1)' to activate the __sub__
228     # method of fieldpoxy.
229     #
230     #return (self-operande)*(-1)
231     #
232     # We prefer to apply a linear transformation because it can be
233     # done in one single request to the med calculator.
234
235     factor = -1
236     offset = operande
237     medcalc.inf("Linear transformation %s%s*%s" % (offset, factor, self.fieldname))
238     try:
239       rfieldHandler = calculator.lin(self.__fieldHandler, factor, offset)
240     except SALOME.SALOME_Exception as ex:
241       medcalc.err(ex.details.text)
242       return None
243
244     return FieldProxy(rfieldHandler)
245   #
246   def __mul__(self, operande):
247     """
248     This makes the multiplication of two fields or the
249     multiplication of a scalar to a field. It depends weither the
250     operande is a FieldProxy or a simple scalar numerical value.
251     """
252     try:
253       if isinstance(operande, FieldProxy):
254         # The operande is an other field
255         medcalc.inf("Multiplication of %s by %s"%(self.fieldname, operande.fieldname))
256         rfieldHandler = calculator.mul(self.__fieldHandler, operande.__fieldHandler)
257       else:
258         # The operande is a scalar numerical value that must be
259         # considered as an offset in a linear transformation
260         factor = operande
261         offset = 0
262         medcalc.inf("Scaling %s by factor %s" % (self.fieldname, factor))
263         rfieldHandler = calculator.lin(self.__fieldHandler, factor, offset)
264     except SALOME.SALOME_Exception as ex:
265       medcalc.err(ex.details.text)
266       return None
267
268     return FieldProxy(rfieldHandler)
269   #
270   def __rmul__(self, operande):
271     """
272     The user typed 'operande*self', we want to execute
273     'self*operande' to activate the __mul__ method of fieldpoxy.
274     """
275     return self*operande
276   #
277   def __div__(self, operande):
278     """
279     This makes the division of two fields or the division of field
280     by a scalar. It depends weither the operande is a FieldProxy
281     or a simple scalar numerical value.
282     """
283     try:
284       if isinstance(operande, FieldProxy):
285         # The operande is an other field
286         medcalc.inf("Division of %s by %s"%(self.fieldname, operande.fieldname))
287         rfieldHandler = calculator.div(self.__fieldHandler, operande.__fieldHandler)
288       else:
289         # The operande is a scalar numerical value that must be
290         # considered as an offset in a linear transformation
291         factor = 1./operande
292         offset = 0
293         medcalc.inf("Scaling %s by factor 1/%s" % (self.fieldname, operande))
294         rfieldHandler = calculator.lin(self.__fieldHandler, factor, offset)
295     except SALOME.SALOME_Exception as ex:
296       medcalc.err(ex.details.text)
297       return None
298
299     return FieldProxy(rfieldHandler)
300   #
301   def __rdiv__(self, operande):
302     """
303     The user typed 'operande/self', we want to execute for each
304     value of the field the operation 'operande/value'.
305     """
306     medcalc.inf("Division of %s by %s" % (operande, self.fieldname))
307     function  = "%s/u"%operande
308     nbResComp = MEDCALC.NBCOMP_DEFAULT
309     try:
310       rfieldHandler = calculator.fct(self.__fieldHandler,function,nbResComp)
311     except SALOME.SALOME_Exception as ex:
312       medcalc.err(ex.details.text)
313       return None
314
315     return FieldProxy(rfieldHandler)
316   #
317   def __pow__(self, power):
318     """
319     This compute the power of the field to the specified value.
320     """
321     function  = "abs(u)^%s"%power
322     return self.ope(function,duplicate=True)
323   #
324   def __abs__(self):
325     """
326     This compute the absolute value of the field. We use here
327     """
328     return self.ope(function="abs(u)",duplicate=True)
329   #
330   def __neg__(self):
331     """
332     This computes the negative of this field (when you type -f)
333     """
334     return -1*self
335   #
336   def dup(self):
337     """
338     This creates a duplicate of the field. The whole data are
339     duplicated.
340     """
341     medcalc.inf("Duplication of %s"%self.fieldname)
342     try:
343       rfieldHandler = calculator.dup(self.__fieldHandler)
344     except SALOME.SALOME_Exception as ex:
345       medcalc.err(ex.details.text)
346       return None
347
348     return FieldProxy(rfieldHandler)
349   #
350   def ope(self, function, duplicate=True):
351     """
352     This can be used to apply a transformation function to this
353     field. The transformation is specified using a literal
354     equation given as a string where u stands for the field.
355     """
356     # _GBO_ TO BE IMPLEMENTED: the case where duplicate = False
357     # must modify the object itself and not create a new field
358     medcalc.inf("Operate the equation \"%s\" to %s"%(function,self.fieldname))
359     try:
360       rfieldHandler = calculator.fct(self.__fieldHandler,
361                                      function,
362                                      MEDCALC.NBCOMP_DEFAULT)
363     except SALOME.SALOME_Exception as ex:
364       medcalc.err(ex.details.text)
365       return None
366
367     return FieldProxy(rfieldHandler)
368   #
369   def __call__(self, restriction=None):
370     """
371     This could be used to return a fieldproxy binded on the same
372     fieldHandler than self, but with options that restrict the
373     usage to a domain specified by the given arguments (restricted
374     to a component, to a part of the mesh, ...).
375     """
376     medcalc.wrn("Not implemented yet. Return the field itself")
377     self.__restriction = restriction
378     return self
379   #
380   def update(self,name=None,iteration=None,order=None,source=None):
381     """
382     This function can be used to update the meta-data associated
383     to this field. It can modify the name, the iteration, the
384     order and the source.
385     """
386     if name is None:
387       name = self.fieldname
388     if iteration is None:
389       iteration = self.iteration
390     if order is None:
391       order = self.order
392     if source is None:
393       source = self.source
394
395     dataManager.updateFieldMetadata(self.id,name,iteration,order,source)
396     self.__fieldHandler.fieldname = name
397     self.__fieldHandler.iteration = iteration
398     self.__fieldHandler.order     = order
399     self.__fieldHandler.source    = source
400     # WARN: Note that you have to update directly the fieldHandler
401     # object because of the presence of the method __setattr__
402     # that make the proxy to this update method
403
404     # Finally, we have to notify the GUI for update of field prestations
405     #self.__notifyGui_update()
406     from medcalc.medevents import notifyGui_updateField
407     notifyGui_updateField(self.id)
408
409     # Print for visual control
410     print(self.__repr__())
411   #
412 #
413
414 #
415 # ===================================================================
416 # unit test functions
417 # ===================================================================
418 #
419
420 def TEST_typeOfFieldLabel():
421   print(typeOfFieldLabel(0))
422   print(typeOfFieldLabel(5))
423 #
424
425 # ===================================================================
426 if __name__ == "__main__":
427   TEST_typeOfFieldLabel()
428 #