source: sasview/src/sans/perspectives/simulation/simulation.py @ a855fec

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since a855fec was a855fec, checked in by Mathieu Doucet <doucetm@…>, 10 years ago

Update perspective imports for latest move

  • Property mode set to 100644
File size: 11.6 KB
Line 
1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2009, University of Tennessee
9"""
10import wx
11import os
12import numpy
13import time
14import logging
15
16# Application imports
17import SimCanvas
18import ShapeParameters
19import ShapeAdapter
20from sans.guiframe.dataFitting import Data1D
21# Real-space simulation import
22import sans.realspace.VolumeCanvas as VolumeCanvas
23
24from sans.data_util.calcthread import CalcThread
25from sans.guicomm.events import NewPlotEvent, StatusEvent   
26
27class Calc1D(CalcThread):
28    """
29        Thread object to simulate I(q)
30    """
31   
32    def __init__(self, x, model,
33                 completefn = None,
34                 updatefn   = None,
35                 yieldtime  = 0.01,
36                 worktime   = 0.01
37                 ):
38        CalcThread.__init__(self,completefn,
39                 updatefn,
40                 yieldtime,
41                 worktime)
42        self.x = x
43        self.model = model
44        self.starttime = 0
45       
46    def compute(self):
47        x = self.x
48        output = numpy.zeros(len(x))
49        error = numpy.zeros(len(x))
50       
51        self.starttime = time.time()
52       
53        for i_x in range(len(self.x)):
54            # Check whether we need to bail out
55            self.isquit()
56            self.update(output=output, error=error)
57           
58            value, err = self.model.getIqError(float(self.x[i_x]))
59            output[i_x] = value
60            error[i_x] = err
61           
62        elapsed = time.time()-self.starttime
63        self.complete(output=output, error=error, elapsed=elapsed)
64
65## Default minimum q-value for simulation output   
66DEFAULT_Q_MIN = 0.01
67## Default maximum q-value for simulation output
68DEFAULT_Q_MAX = 0.4
69## Default number of q point for simulation output
70DEFAULT_Q_NPTS = 10
71## Default number of real-space points per Angstrom cube
72DEFAULT_PT_DENSITY = 0.1
73   
74class Plugin:
75    """
76        Real-space simulation plug-in for guiframe
77    """
78    ## Minimum q-value for simulation output   
79    q_min = DEFAULT_Q_MIN
80    ## Maximum q-value for simulation output
81    q_max = DEFAULT_Q_MAX
82    ## Number of q point for simulation output
83    q_npts = DEFAULT_Q_NPTS   
84   
85    def __init__(self):
86        ## Plug-in name
87        self.sub_menu = "Simulation"
88        ## Reference to the parent window
89        self.parent = None
90        ## List of panels for the simulation perspective (names)
91        self.perspective = []
92        # Default save location
93        self._default_save_location = os.getcwd()
94        # Log startup
95        logging.info("Simulation plug-in started")
96       
97    def get_panels(self, parent):
98        """
99            Create and return a list of panel objects
100        """
101        self.parent = parent
102       
103        # 3D viewer
104        self.plotPanel  = SimCanvas.SimPanel(self.parent, -1, style=wx.RAISED_BORDER)
105
106        # Central simulation panel
107        self.paramPanel = ShapeParameters.ShapeParameterPanel(self.parent, 
108                                                              q_min = self.q_min,
109                                                              q_max = self.q_max,
110                                                              q_npts = self.q_npts,
111                                                              pt_density = DEFAULT_PT_DENSITY,
112                                                              style=wx.RAISED_BORDER)
113       
114        # Simulation
115        self.volCanvas = VolumeCanvas.VolumeCanvas()
116        self.volCanvas.setParam('lores_density', DEFAULT_PT_DENSITY)
117        self.adapter = ShapeAdapter.ShapeVisitor()
118        self._data_1D = None
119        self.calc_thread_1D = None
120        self.speedCheck = False
121        self.speed = 3.0e-7
122       
123        # Q-values for plotting simulated I(Q)
124        step = (self.q_max-self.q_min)/(self.q_npts-1)
125        self.x = numpy.arange(self.q_min, self.q_max+step*0.01, step)       
126       
127        # Set the list of panels that are part of the simulation perspective
128        self.perspective = []
129        self.perspective.append(self.plotPanel.window_name)
130        self.perspective.append(self.paramPanel.window_name)
131       
132        # Bind state events
133        self.parent.Bind(ShapeParameters.EVT_ADD_SHAPE, self._onAddShape)
134        self.parent.Bind(ShapeParameters.EVT_DEL_SHAPE, self._onDelShape)
135        self.parent.Bind(ShapeParameters.EVT_Q_RANGE, self._on_q_range_changed)
136        self.parent.Bind(ShapeParameters.EVT_PT_DENSITY, self._on_pt_density_changed)
137
138        return [self.plotPanel, self.paramPanel]
139
140    def _onAddShape(self, evt):
141        """
142            Process a user event to add a newly created
143            or modified shape to the canvas
144        """
145        evt.Skip()
146       
147        # Give the new shape to the canvas
148        if evt.new:
149            shape = evt.shape.accept(self.adapter)
150            id = self.volCanvas.addObject(shape)
151            self.plotPanel.canvas.addShape(evt.shape, id)
152        else:
153            self.adapter.update(self.volCanvas, evt.shape)
154       
155        # Compute the simulated I(Q)
156        self._simulate_Iq()
157       
158        # Refresh the 3D viewer
159        self._refresh_3D_viewer()
160       
161    def _onDelShape(self, evt):
162        """
163            Remove a shape from the simulation
164        """
165        # Notify the simulation canvas
166        self.volCanvas.delete(evt.id)
167        # Notify the UI canvas
168        self.plotPanel.canvas.delShape(evt.id)
169       
170        # Compute the simulated I(Q)
171        self._simulate_Iq()
172       
173        # Refresh the 3D viewer
174        self._refresh_3D_viewer()   
175       
176    def _on_q_range_changed(self, evt):
177        """
178            Modify the Q range of the simulation output
179        """
180        if evt.q_min is not None:
181            self.q_min = evt.q_min
182        if evt.q_max is not None:
183            self.q_max = evt.q_max
184        if evt.npts is not None:
185            self.q_npts = evt.npts
186       
187        # Q-values for plotting simulated I(Q)
188        step = (self.q_max-self.q_min)/(self.q_npts-1)
189        self.x = numpy.arange(self.q_min, self.q_max+step*0.01, step)   
190         
191        # Compute the simulated I(Q)
192        self._simulate_Iq()
193       
194    def _on_pt_density_changed(self, evt):
195        """
196            Modify the Q range of the simulation output
197        """
198        if evt.npts is not None:
199            self.volCanvas.setParam('lores_density', evt.npts)
200         
201        # Compute the simulated I(Q)
202        self._simulate_Iq()
203       
204    def _simulate_Iq(self):
205        """
206            Simulate I(q) using the current VolumeCanvas object.
207        """
208        # Check that the VolumeCanvas object exists
209        if not isinstance(self.volCanvas, VolumeCanvas.VolumeCanvas):
210            return
211       
212        # If a computation thread is running, stop it
213        if self.calc_thread_1D != None and self.calc_thread_1D.isrunning():
214            self.calc_thread_1D.stop()
215           
216        # Create a computation thread
217        self.calc_thread_1D = Calc1D(self.x, self.volCanvas, 
218                            completefn=self._simulation_completed_1D,
219                            updatefn=None)
220        self.calc_thread_1D.queue()
221        self.calc_thread_1D.ready(2.5)
222   
223        # Evaluate maximum number of points on the canvas and the
224        # maximum computation time
225       
226        # TODO: the getMaxVolume should be a call to the VolumeCanvas object.
227        # Since the VolumeCanvas doesn't currently have that functionality, and
228        # since the simulation panel holds the list of graphical representations
229        # for the shapes, we will take the information from there until VolumeCanvas
230        # is updated.
231        npts = self.plotPanel.canvas.getMaxVolume() * self.volCanvas.params['lores_density'] 
232       
233        est = self.speed * npts * npts
234        self.parent.SetStatusText("Calculation started: this might take a moment... [up to %d secs, %g points]" % (int(est), int(npts)))
235
236 
237    def _simulation_completed_1D(self, output, elapsed, error=None):
238        """
239            Called by the computation thread when the simulation is complete.
240            This method processes the simulation output, plots it, and updates
241            the simulation time estimate.
242           
243            @param output: simulated distribution I(q)
244            @param elapsed: simulation time, in seconds
245            @param error: standard deviation on the I(q) points
246        """
247        # Create the plotting event to pop up the I(Q) plot.
248        new_plot = Data1D(x=self.x, y=output, dy=error)
249        new_plot.name = "I(Q) Simulation"
250        new_plot.group_id = "simulation_output"
251        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
252        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
253        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Simulation I(Q)"))
254       
255        # Create the plotting event to pop up the P(r) plot.
256        r, pr = self.volCanvas.getPrData()
257        new_plot = Data1D(x=r, y=pr, dy=[0]*len(r))
258        new_plot.name = "P(r) Simulation"
259        new_plot.group_id = "simulated_pr"
260        new_plot.xaxis("\\rm{r}", 'A')
261        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}") 
262       
263        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Simulated P(r)"))       
264       
265       
266        # Notify the user of the simlation time and update the basic
267        # simulation estimate that will allow us to estimate simulation time
268        # next time we are asked to simulate.
269        msg = "Calculation completed in %g secs! [%g points]" % (elapsed, self.volCanvas.npts)
270        wx.PostEvent(self.parent, StatusEvent(status=msg))
271        if self.volCanvas.npts>0:
272            self.speed = elapsed/self.volCanvas.npts**2
273         
274   
275    def get_perspective(self):
276        """
277            Get the list of panel names for this perspective
278        """
279        return self.perspective
280   
281    def populate_menu(self, id, owner):
282        """
283            Create a menu for the plug-in
284        """
285        return [] 
286   
287    def _change_point_density(self, point_density):
288        """
289            Placeholder for changing the simulation point density
290            TODO: refactor this away by writing a single update method for the simulation parameters
291        """
292        self.volCanvas.setParam('lores_density', point_density)
293   
294    def help(self, evt):
295        """
296            Provide help for the simulation
297        """
298        pass
299   
300    def on_perspective(self, event):
301        """
302            Call back function for the perspective menu item.
303            We notify the parent window that the perspective
304            has changed.
305        """
306        self.parent.set_perspective(self.perspective)
307   
308    def post_init(self):
309        """
310            Post initialization call back to close the loose ends
311            [Somehow openGL needs this call]
312        """
313        self.parent.set_perspective(self.perspective) 
314        self.parent._mgr.Update()
315
316    def _refresh_3D_viewer(self):
317        """
318            Refresh the 3D viewer window
319            #TODO: don't access data member directly
320        """
321        self.plotPanel.canvas.Refresh(False)
322        # Give focus back to 3D canvas so that the
323        # zooming works
324        #self.plotPanel.canvas.SetFocus()
325        #self.plotPanel.SetFocus()
Note: See TracBrowser for help on using the repository browser.