source: sasview/simview/perspectives/simulation/simulation.py @ 7e8601f

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 7e8601f was cb78690, checked in by Mathieu Doucet <doucetm@…>, 15 years ago

simview: continue consolidating functionality to central simulation panel

  • Property mode set to 100644
File size: 10.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 sys
12import os
13import numpy
14import time
15from copy import deepcopy
16import logging
17
18# Application imports
19import SimCanvas
20import ShapeParameters
21import ShapeAdapter
22from sans.guiframe.dataFitting import Data1D
23# Real-space simulation import
24import sans.realspace.VolumeCanvas as VolumeCanvas
25
26from data_util.calcthread import CalcThread
27from sans.guicomm.events import NewPlotEvent, StatusEvent   
28
29class Calc1D(CalcThread):
30    """
31        Thread object to simulate I(q)
32    """
33   
34    def __init__(self, x, model,
35                 completefn = None,
36                 updatefn   = None,
37                 yieldtime  = 0.01,
38                 worktime   = 0.01
39                 ):
40        CalcThread.__init__(self,completefn,
41                 updatefn,
42                 yieldtime,
43                 worktime)
44        self.x = x
45        self.model = model
46        self.starttime = 0
47       
48    def compute(self):
49        x = self.x
50        output = numpy.zeros(len(x))
51        error = numpy.zeros(len(x))
52       
53        self.starttime = time.time()
54       
55        for i_x in range(len(self.x)):
56            # Check whether we need to bail out
57            self.isquit()
58            self.update(output=output, error=error)
59           
60            value, err = self.model.getIqError(float(self.x[i_x]))
61            output[i_x] = value
62            error[i_x] = err
63           
64        elapsed = time.time()-self.starttime
65        self.complete(output=output, error=error, elapsed=elapsed)
66
67## Default minimum q-value for simulation output   
68DEFAULT_Q_MIN = 0.01
69## Default maximum q-value for simulation output
70DEFAULT_Q_MAX = 0.4
71## Default number of q point for simulation output
72DEFAULT_Q_NPTS = 10
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, style=wx.RAISED_BORDER)
108       
109        # Simulation
110        self.volCanvas = VolumeCanvas.VolumeCanvas()
111        self.volCanvas.setParam('lores_density', 0.1)
112        self.adapter = ShapeAdapter.ShapeVisitor()
113        self._data_1D = None
114        self.calc_thread_1D = None
115        self.speedCheck = False
116        self.speed = 3.0e-7
117       
118        # Q-values for plotting simulated I(Q)
119        step = (self.q_max-self.q_min)/(self.q_npts-1)
120        self.x = numpy.arange(self.q_min, self.q_max+step*0.01, step)       
121       
122        # Set the list of panels that are part of the simulation perspective
123        self.perspective = []
124        self.perspective.append(self.plotPanel.window_name)
125        self.perspective.append(self.paramPanel.window_name)
126       
127        # Bind state events
128        self.parent.Bind(ShapeParameters.EVT_ADD_SHAPE, self._onAddShape)
129        self.parent.Bind(ShapeParameters.EVT_DEL_SHAPE, self._onDelShape)
130
131        return [self.plotPanel, self.paramPanel]
132
133    def _onAddShape(self, evt):
134        """
135            Process a user event to add a newly created
136            or modified shape to the canvas
137        """
138        evt.Skip()
139       
140        # Give the new shape to the canvas
141        if evt.new:
142            shape = evt.shape.accept(self.adapter)
143            id = self.volCanvas.addObject(shape)
144            self.plotPanel.canvas.addShape(evt.shape, id)
145        else:
146            self.adapter.update(self.volCanvas, evt.shape)
147       
148        # Compute the simulated I(Q)
149        self._simulate_Iq()
150       
151        # Refresh the 3D viewer
152        self._refresh_3D_viewer()
153       
154    def _onDelShape(self, evt):
155        """
156            Remove a shape from the simulation
157        """
158        # Notify the simulation canvas
159        self.volCanvas.delete(evt.id)
160        # Notify the UI canvas
161        self.plotPanel.canvas.delShape(evt.id)
162       
163        # Compute the simulated I(Q)
164        self._simulate_Iq()
165       
166        # Refresh the 3D viewer
167        self._refresh_3D_viewer()   
168       
169    def _simulate_Iq(self):
170        """
171            Simulate I(q) using the current VolumeCanvas object.
172        """
173        # Check that the VolumeCanvas object exists
174        if not isinstance(self.volCanvas, VolumeCanvas.VolumeCanvas):
175            return
176       
177        # If a computation thread is running, stop it
178        if self.calc_thread_1D != None and self.calc_thread_1D.isrunning():
179            self.calc_thread_1D.stop()
180           
181        # Create a computation thread
182        self.calc_thread_1D = Calc1D(self.x, self.volCanvas, 
183                            completefn=self._simulation_completed_1D,
184                            updatefn=None)
185        self.calc_thread_1D.queue()
186        self.calc_thread_1D.ready(2.5)
187   
188        # Evaluate maximum number of points on the canvas and the
189        # maximum computation time
190       
191        # TODO: the getMaxVolume should be a call to the VolumeCanvas object.
192        # Since the VolumeCanvas doesn't currently have that functionality, and
193        # since the simulation panel holds the list of graphical representations
194        # for the shapes, we will take the information from there until VolumeCanvas
195        # is updated.
196        npts = self.plotPanel.canvas.getMaxVolume() * self.volCanvas.params['lores_density'] 
197       
198        est = self.speed * npts * npts
199        self.parent.SetStatusText("Calculation started: this might take a moment... [up to %d secs, %g points]" % (int(est), int(npts)))
200
201 
202    def _simulation_completed_1D(self, output, elapsed, error=None):
203        """
204            Called by the computation thread when the simulation is complete.
205            This method processes the simulation output, plots it, and updates
206            the simulation time estimate.
207           
208            @param output: simulated distribution I(q)
209            @param elapsed: simulation time, in seconds
210            @param error: standard deviation on the I(q) points
211        """
212        # Create the plotting event to pop up the I(Q) plot.
213        new_plot = Data1D(x=self.x, y=output, dy=error)
214        new_plot.name = "Simulation"
215        new_plot.group_id = "simulation_output"
216        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
217        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
218        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Simulation output"))
219       
220        # Notify the user of the simlation time and update the basic
221        # simulation estimate that will allow us to estimate simulation time
222        # next time we are asked to simulate.
223        msg = "Calculation completed in %g secs! [%g points]" % (elapsed, self.volCanvas.npts)
224        wx.PostEvent(self.parent, StatusEvent(status=msg))
225        if self.volCanvas.npts>0:
226            self.speed = elapsed/self.volCanvas.npts**2
227         
228   
229    def get_perspective(self):
230        """
231            Get the list of panel names for this perspective
232        """
233        return self.perspective
234   
235    def populate_menu(self, id, owner):
236        """
237            -- TEMPORARY --
238            Create a menu for the plug-in
239           
240            #TODO: remove this method once:
241                1- P(r) is plotted with the I(q) simulation result
242        """
243        # Shapes
244        shapes = wx.Menu()
245 
246        #TODO: Save P(r) should be replaced by plotting P(r) for each simulation
247        id = wx.NewId()
248        shapes.Append(id, '&Save P(r) output')
249        wx.EVT_MENU(self.parent, id, self._onSavePr)
250
251        return [(id+1, shapes, "Save P(r)")] 
252   
253    def _change_point_density(self, point_density):
254        """
255            Placeholder for changing the simulation point density
256            TODO: refactor this away by writing a single update method for the simulation parameters
257        """
258        self.volCanvas.setParam('lores_density', point_density)
259   
260    def _onSavePr(self, evt):
261        """
262            Save the current P(r) output to a file
263            TODO: refactor this away once P(r) is plotted
264        """     
265        path = None
266        dlg = wx.FileDialog(None, "Choose a file", self._default_save_location, "", "*.txt", wx.SAVE)
267        if dlg.ShowModal() == wx.ID_OK:
268            path = dlg.GetPath()
269            self._default_save_location = os.path.dirname(path)
270        dlg.Destroy()
271       
272        if not path == None:
273            self.volCanvas.write_pr(path)   
274   
275    def help(self, evt):
276        """
277            Provide help for the simulation
278        """
279        pass
280   
281    def on_perspective(self, event):
282        """
283            Call back function for the perspective menu item.
284            We notify the parent window that the perspective
285            has changed.
286        """
287        self.parent.set_perspective(self.perspective)
288   
289    def post_init(self):
290        """
291            Post initialization call back to close the loose ends
292            [Somehow openGL needs this call]
293        """
294        self.parent.set_perspective(self.perspective) 
295        self.parent._mgr.Update()
296
297    def _refresh_3D_viewer(self):
298        """
299            Refresh the 3D viewer window
300            #TODO: don't access data member directly
301        """
302        self.plotPanel.canvas.Refresh(False)
303        # Give focus back to 3D canvas so that the
304        # zooming works
305        #self.plotPanel.canvas.SetFocus()
306        #self.plotPanel.SetFocus()
Note: See TracBrowser for help on using the repository browser.