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