source: sasview/src/sans/perspectives/simulation/simulation.py @ 5ba88d3

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 5ba88d3 was f4167637, checked in by butler, 10 years ago

update Pr and simulations to fix thread problem. note: I don't think simulations is used at this point but figure it should be fixed if in repo.

  • Property mode set to 100644
File size: 12.1 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            ## stop just raises the flag -- the thread is supposed to
216            ## then kill itself but cannot.  Paul Kienzle came up with
217            ## this fix to prevent threads from stepping on each other
218            ## in Calc1D of fitting.py which was causing a simple custom model
219            ## to crash Sasview.  See rest of notes under Calc1D.
220            ##
221            ##    -PDB August 13, 2014                 
222            while self.calc_thread_1D.isrunning():
223                time.sleep(0.1)
224           
225        # Create a computation thread
226        self.calc_thread_1D = Calc1D(self.x, self.volCanvas, 
227                            completefn=self._simulation_completed_1D,
228                            updatefn=None)
229        self.calc_thread_1D.queue()
230        self.calc_thread_1D.ready(2.5)
231   
232        # Evaluate maximum number of points on the canvas and the
233        # maximum computation time
234       
235        # TODO: the getMaxVolume should be a call to the VolumeCanvas object.
236        # Since the VolumeCanvas doesn't currently have that functionality, and
237        # since the simulation panel holds the list of graphical representations
238        # for the shapes, we will take the information from there until VolumeCanvas
239        # is updated.
240        npts = self.plotPanel.canvas.getMaxVolume() * self.volCanvas.params['lores_density'] 
241       
242        est = self.speed * npts * npts
243        self.parent.SetStatusText("Calculation started: this might take a moment... [up to %d secs, %g points]" % (int(est), int(npts)))
244
245 
246    def _simulation_completed_1D(self, output, elapsed, error=None):
247        """
248            Called by the computation thread when the simulation is complete.
249            This method processes the simulation output, plots it, and updates
250            the simulation time estimate.
251           
252            @param output: simulated distribution I(q)
253            @param elapsed: simulation time, in seconds
254            @param error: standard deviation on the I(q) points
255        """
256        # Create the plotting event to pop up the I(Q) plot.
257        new_plot = Data1D(x=self.x, y=output, dy=error)
258        new_plot.name = "I(Q) Simulation"
259        new_plot.group_id = "simulation_output"
260        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
261        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
262        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Simulation I(Q)"))
263       
264        # Create the plotting event to pop up the P(r) plot.
265        r, pr = self.volCanvas.getPrData()
266        new_plot = Data1D(x=r, y=pr, dy=[0]*len(r))
267        new_plot.name = "P(r) Simulation"
268        new_plot.group_id = "simulated_pr"
269        new_plot.xaxis("\\rm{r}", 'A')
270        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}") 
271       
272        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Simulated P(r)"))       
273       
274       
275        # Notify the user of the simlation time and update the basic
276        # simulation estimate that will allow us to estimate simulation time
277        # next time we are asked to simulate.
278        msg = "Calculation completed in %g secs! [%g points]" % (elapsed, self.volCanvas.npts)
279        wx.PostEvent(self.parent, StatusEvent(status=msg))
280        if self.volCanvas.npts>0:
281            self.speed = elapsed/self.volCanvas.npts**2
282         
283   
284    def get_perspective(self):
285        """
286            Get the list of panel names for this perspective
287        """
288        return self.perspective
289   
290    def populate_menu(self, id, owner):
291        """
292            Create a menu for the plug-in
293        """
294        return [] 
295   
296    def _change_point_density(self, point_density):
297        """
298            Placeholder for changing the simulation point density
299            TODO: refactor this away by writing a single update method for the simulation parameters
300        """
301        self.volCanvas.setParam('lores_density', point_density)
302   
303    def help(self, evt):
304        """
305            Provide help for the simulation
306        """
307        pass
308   
309    def on_perspective(self, event):
310        """
311            Call back function for the perspective menu item.
312            We notify the parent window that the perspective
313            has changed.
314        """
315        self.parent.set_perspective(self.perspective)
316   
317    def post_init(self):
318        """
319            Post initialization call back to close the loose ends
320            [Somehow openGL needs this call]
321        """
322        self.parent.set_perspective(self.perspective) 
323        self.parent._mgr.Update()
324
325    def _refresh_3D_viewer(self):
326        """
327            Refresh the 3D viewer window
328            #TODO: don't access data member directly
329        """
330        self.plotPanel.canvas.Refresh(False)
331        # Give focus back to 3D canvas so that the
332        # zooming works
333        #self.plotPanel.canvas.SetFocus()
334        #self.plotPanel.SetFocus()
Note: See TracBrowser for help on using the repository browser.