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