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