root / ase / gui / scaling.py @ 19
Historique | Voir | Annoter | Télécharger (14,54 ko)
1 | 1 | tkerber | # encoding: utf-8
|
---|---|---|---|
2 | 1 | tkerber | |
3 | 1 | tkerber | "Module for homogeneous deformation and calculations of elastic constants."
|
4 | 1 | tkerber | |
5 | 1 | tkerber | import gtk |
6 | 1 | tkerber | from ase.gui.simulation import Simulation |
7 | 1 | tkerber | from ase.gui.minimize import MinimizeMixin |
8 | 1 | tkerber | from ase.gui.energyforces import OutputFieldMixin |
9 | 1 | tkerber | from ase.gui.widgets import oops, pack, AseGuiCancelException |
10 | 1 | tkerber | import ase |
11 | 1 | tkerber | import numpy as np |
12 | 1 | tkerber | |
13 | 1 | tkerber | scaling_txt = """\
|
14 | 1 | tkerber | This module is intended for calculating elastic constants by homogeneously
|
15 | 1 | tkerber | deforming a system."""
|
16 | 1 | tkerber | |
17 | 1 | tkerber | class HomogeneousDeformation(Simulation, MinimizeMixin, OutputFieldMixin): |
18 | 1 | tkerber | "Window for homogeneous deformation and elastic constants."
|
19 | 1 | tkerber | |
20 | 1 | tkerber | def __init__(self, gui): |
21 | 1 | tkerber | Simulation.__init__(self, gui)
|
22 | 1 | tkerber | self.set_title("Homogeneous scaling") |
23 | 1 | tkerber | vbox = gtk.VBox() |
24 | 1 | tkerber | self.packtext(vbox, scaling_txt)
|
25 | 1 | tkerber | self.packimageselection(vbox, txt1="", txt2="") |
26 | 1 | tkerber | self.start_radio_nth.set_active(True) |
27 | 1 | tkerber | pack(vbox, gtk.Label(""))
|
28 | 1 | tkerber | |
29 | 1 | tkerber | # Radio buttons for choosing deformation mode.
|
30 | 1 | tkerber | tbl = gtk.Table(4,3) |
31 | 1 | tkerber | for i, l in enumerate(('3D', '2D', '1D')): |
32 | 1 | tkerber | l = l + " deformation "
|
33 | 1 | tkerber | lbl = gtk.Label(l) |
34 | 1 | tkerber | tbl.attach(lbl, i, i+1, 0, 1) |
35 | 1 | tkerber | self.radio_bulk = gtk.RadioButton(None, "Bulk") |
36 | 1 | tkerber | tbl.attach(self.radio_bulk, 0, 1, 1, 2) |
37 | 1 | tkerber | self.radio_xy = gtk.RadioButton(self.radio_bulk, "xy-plane") |
38 | 1 | tkerber | tbl.attach(self.radio_xy, 1, 2, 1, 2) |
39 | 1 | tkerber | self.radio_xz = gtk.RadioButton(self.radio_bulk, "xz-plane") |
40 | 1 | tkerber | tbl.attach(self.radio_xz, 1, 2, 2, 3) |
41 | 1 | tkerber | self.radio_yz = gtk.RadioButton(self.radio_bulk, "yz-plane") |
42 | 1 | tkerber | tbl.attach(self.radio_yz, 1, 2, 3, 4) |
43 | 1 | tkerber | self.radio_x = gtk.RadioButton(self.radio_bulk, "x-axis") |
44 | 1 | tkerber | tbl.attach(self.radio_x, 2, 3, 1, 2) |
45 | 1 | tkerber | self.radio_y = gtk.RadioButton(self.radio_bulk, "y-axis") |
46 | 1 | tkerber | tbl.attach(self.radio_y, 2, 3, 2, 3) |
47 | 1 | tkerber | self.radio_z = gtk.RadioButton(self.radio_bulk, "z-axis") |
48 | 1 | tkerber | tbl.attach(self.radio_z, 2, 3, 3, 4) |
49 | 1 | tkerber | tbl.show_all() |
50 | 1 | tkerber | pack(vbox, [tbl]) |
51 | 1 | tkerber | self.deformtable = [
|
52 | 1 | tkerber | (self.radio_bulk, (1,1,1)), |
53 | 1 | tkerber | (self.radio_xy, (1,1,0)), |
54 | 1 | tkerber | (self.radio_xz, (1,0,1)), |
55 | 1 | tkerber | (self.radio_yz, (0,1,1)), |
56 | 1 | tkerber | (self.radio_x, (1,0,0)), |
57 | 1 | tkerber | (self.radio_y, (0,1,0)), |
58 | 1 | tkerber | (self.radio_z, (0,0,1))] |
59 | 1 | tkerber | self.deform_label = gtk.Label("") |
60 | 1 | tkerber | pack(vbox, [self.deform_label])
|
61 | 1 | tkerber | self.choose_possible_deformations(first=True) |
62 | 1 | tkerber | |
63 | 1 | tkerber | # Parameters for the deformation
|
64 | 1 | tkerber | framedef = gtk.Frame("Deformation:")
|
65 | 1 | tkerber | vbox2 = gtk.VBox() |
66 | 1 | tkerber | vbox2.show() |
67 | 1 | tkerber | framedef.add(vbox2) |
68 | 1 | tkerber | self.max_scale = gtk.Adjustment(0.010, 0.001, 10.0, 0.001) |
69 | 1 | tkerber | max_scale_spin = gtk.SpinButton(self.max_scale, 10.0, 3) |
70 | 1 | tkerber | pack(vbox2, [gtk.Label("Maximal scale factor: "), max_scale_spin])
|
71 | 1 | tkerber | self.scale_offset = gtk.Adjustment(0.0, -10.0, 10.0, 0.001) |
72 | 1 | tkerber | scale_offset_spin = gtk.SpinButton(self.scale_offset, 10.0, 3) |
73 | 1 | tkerber | pack(vbox2, [gtk.Label("Scale offset: "), scale_offset_spin])
|
74 | 1 | tkerber | self.nsteps = gtk.Adjustment(5, 3, 100, 1) |
75 | 1 | tkerber | nsteps_spin = gtk.SpinButton(self.nsteps, 1, 0) |
76 | 1 | tkerber | pack(vbox2, [gtk.Label("Number of steps: "), nsteps_spin])
|
77 | 1 | tkerber | |
78 | 1 | tkerber | # Atomic relaxations
|
79 | 1 | tkerber | framerel = gtk.Frame("Atomic relaxations:")
|
80 | 1 | tkerber | vbox2 = gtk.VBox() |
81 | 1 | tkerber | vbox2.show() |
82 | 1 | tkerber | framerel.add(vbox2) |
83 | 1 | tkerber | self.radio_relax_on = gtk.RadioButton(None, "On ") |
84 | 1 | tkerber | self.radio_relax_off = gtk.RadioButton(self.radio_relax_on, "Off") |
85 | 1 | tkerber | self.radio_relax_off.set_active(True) |
86 | 1 | tkerber | pack(vbox2, [self.radio_relax_on, self.radio_relax_off]) |
87 | 1 | tkerber | self.make_minimize_gui(vbox2)
|
88 | 1 | tkerber | for r in (self.radio_relax_on, self.radio_relax_off): |
89 | 1 | tkerber | r.connect("toggled", self.relax_toggled) |
90 | 1 | tkerber | self.relax_toggled()
|
91 | 1 | tkerber | pack(vbox, [framedef, gtk.Label(" "), framerel])
|
92 | 1 | tkerber | pack(vbox, gtk.Label(""))
|
93 | 1 | tkerber | |
94 | 1 | tkerber | # Results
|
95 | 1 | tkerber | pack(vbox, [gtk.Label("Results:")])
|
96 | 1 | tkerber | self.radio_results_optimal = gtk.RadioButton(
|
97 | 1 | tkerber | None, "Load optimal configuration") |
98 | 1 | tkerber | self.radio_results_all = gtk.RadioButton(
|
99 | 1 | tkerber | self.radio_results_optimal, "Load all configurations") |
100 | 1 | tkerber | self.radio_results_optimal.set_active(True) |
101 | 1 | tkerber | pack(vbox, [self.radio_results_optimal])
|
102 | 1 | tkerber | pack(vbox, [self.radio_results_all])
|
103 | 1 | tkerber | |
104 | 1 | tkerber | # Output field
|
105 | 1 | tkerber | outframe = self.makeoutputfield(None) |
106 | 1 | tkerber | fitframe = gtk.Frame("Fit:")
|
107 | 1 | tkerber | vbox2 = gtk.VBox() |
108 | 1 | tkerber | vbox2.show() |
109 | 1 | tkerber | fitframe.add(vbox2) |
110 | 1 | tkerber | self.radio_fit_2 = gtk.RadioButton(None, "2nd") |
111 | 1 | tkerber | self.radio_fit_3 = gtk.RadioButton(self.radio_fit_2, "3rd") |
112 | 1 | tkerber | self.radio_fit_2.connect("toggled", self.change_fit) |
113 | 1 | tkerber | self.radio_fit_3.connect("toggled", self.change_fit) |
114 | 1 | tkerber | self.radio_fit_3.set_active(True) |
115 | 1 | tkerber | pack(vbox2, [gtk.Label("Order of fit: "), self.radio_fit_2, |
116 | 1 | tkerber | self.radio_fit_3])
|
117 | 1 | tkerber | pack(vbox2, [gtk.Label("")])
|
118 | 1 | tkerber | scrwin = gtk.ScrolledWindow() |
119 | 1 | tkerber | scrwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
120 | 1 | tkerber | self.fit_output = gtk.TextBuffer()
|
121 | 1 | tkerber | txtview = gtk.TextView(self.fit_output)
|
122 | 1 | tkerber | txtview.set_editable(False)
|
123 | 1 | tkerber | scrwin.add(txtview) |
124 | 1 | tkerber | scrwin.show_all() |
125 | 1 | tkerber | self.fit_win = scrwin
|
126 | 1 | tkerber | print "SIZE RQ:", scrwin.size_request() |
127 | 1 | tkerber | vbox2.pack_start(scrwin, True, True, 0) |
128 | 1 | tkerber | hbox = gtk.HBox(homogeneous=True)
|
129 | 1 | tkerber | for w in [outframe, fitframe]: |
130 | 1 | tkerber | hbox.pack_start(w) |
131 | 1 | tkerber | w.show() |
132 | 1 | tkerber | pack(vbox, hbox) |
133 | 1 | tkerber | pack(vbox, gtk.Label(""))
|
134 | 1 | tkerber | |
135 | 1 | tkerber | # Status field
|
136 | 1 | tkerber | self.status_label = gtk.Label("") |
137 | 1 | tkerber | pack(vbox, [self.status_label])
|
138 | 1 | tkerber | |
139 | 1 | tkerber | # Run buttons etc.
|
140 | 1 | tkerber | self.makebutbox(vbox)
|
141 | 1 | tkerber | vbox.show() |
142 | 1 | tkerber | self.add(vbox)
|
143 | 1 | tkerber | self.show()
|
144 | 1 | tkerber | self.gui.register_vulnerable(self) |
145 | 1 | tkerber | |
146 | 1 | tkerber | def choose_possible_deformations(self, first=False): |
147 | 1 | tkerber | """Turn on sensible radio buttons.
|
148 | 1 | tkerber |
|
149 | 1 | tkerber | Only radio buttons corresponding to deformations in directions
|
150 | 1 | tkerber | with periodic boundary conditions should be turned on.
|
151 | 1 | tkerber | """
|
152 | 1 | tkerber | if self.setup_atoms(): |
153 | 1 | tkerber | pbc = self.atoms.get_pbc()
|
154 | 1 | tkerber | else:
|
155 | 1 | tkerber | pbc = [False, False, False] |
156 | 1 | tkerber | for radio, requirement in self.deformtable: |
157 | 1 | tkerber | ok = True
|
158 | 1 | tkerber | for i in range(3): |
159 | 1 | tkerber | if requirement[i] and not pbc[i]: |
160 | 1 | tkerber | ok = False
|
161 | 1 | tkerber | radio.set_sensitive(ok) |
162 | 1 | tkerber | if first and ok: |
163 | 1 | tkerber | # The first acceptable choice, choose it to prevent
|
164 | 1 | tkerber | # inconsistent state.
|
165 | 1 | tkerber | radio.set_active(True)
|
166 | 1 | tkerber | first = False
|
167 | 1 | tkerber | |
168 | 1 | tkerber | def relax_toggled(self, *args): |
169 | 1 | tkerber | "Turn minimization widgets on or off."
|
170 | 1 | tkerber | state = self.radio_relax_on.get_active()
|
171 | 1 | tkerber | for widget in (self.algo, self.fmax_spin, self.steps_spin): |
172 | 1 | tkerber | widget.set_sensitive(state) |
173 | 1 | tkerber | |
174 | 1 | tkerber | def notify_atoms_changed(self): |
175 | 1 | tkerber | "When atoms have changed, check for the number of images."
|
176 | 1 | tkerber | self.setupimageselection()
|
177 | 1 | tkerber | self.choose_possible_deformations()
|
178 | 1 | tkerber | |
179 | 1 | tkerber | def get_deformation_axes(self): |
180 | 1 | tkerber | """Return which axes the user wants to deform along."""
|
181 | 1 | tkerber | for but, deform in self.deformtable: |
182 | 1 | tkerber | if but.get_active():
|
183 | 1 | tkerber | return np.array(deform)
|
184 | 1 | tkerber | # No deformation chosen!
|
185 | 1 | tkerber | oops("No deformation chosen: Please choose a deformation mode.")
|
186 | 1 | tkerber | return False |
187 | 1 | tkerber | |
188 | 1 | tkerber | def run(self, *args): |
189 | 1 | tkerber | """Make the deformation."""
|
190 | 1 | tkerber | self.output.set_text("") |
191 | 1 | tkerber | if not self.setup_atoms(): |
192 | 1 | tkerber | return
|
193 | 1 | tkerber | deform_axes = self.get_deformation_axes()
|
194 | 1 | tkerber | if deform_axes is False: |
195 | 1 | tkerber | return #Nothing to do! |
196 | 1 | tkerber | |
197 | 1 | tkerber | # Prepare progress bar
|
198 | 1 | tkerber | if self.radio_relax_on.get_active(): |
199 | 1 | tkerber | fmax = self.fmax.value
|
200 | 1 | tkerber | mininame = self.minimizers[self.algo.get_active()] |
201 | 1 | tkerber | self.begin(mode="scale/min", algo=mininame, fmax=fmax, |
202 | 1 | tkerber | steps=self.steps.value, scalesteps=self.nsteps.value) |
203 | 1 | tkerber | else:
|
204 | 1 | tkerber | self.begin(mode="scale", scalesteps=self.nsteps.value) |
205 | 1 | tkerber | try:
|
206 | 1 | tkerber | logger_func = self.gui.simulation['progress'].get_logger_stream |
207 | 1 | tkerber | except (KeyError, AttributeError): |
208 | 1 | tkerber | logger = None
|
209 | 1 | tkerber | else:
|
210 | 1 | tkerber | logger = logger_func() # Don't catch errors in the function.
|
211 | 1 | tkerber | |
212 | 1 | tkerber | # Display status message
|
213 | 1 | tkerber | self.status_label.set_text("Running ...") |
214 | 1 | tkerber | self.status_label.modify_fg(gtk.STATE_NORMAL,
|
215 | 1 | tkerber | gtk.gdk.color_parse('#AA0000'))
|
216 | 1 | tkerber | while gtk.events_pending():
|
217 | 1 | tkerber | gtk.main_iteration() |
218 | 1 | tkerber | |
219 | 1 | tkerber | # Do the scaling
|
220 | 1 | tkerber | scale = self.max_scale.value
|
221 | 1 | tkerber | steps = np.linspace(-scale, scale, self.nsteps.value)
|
222 | 1 | tkerber | steps += self.scale_offset.value
|
223 | 1 | tkerber | undef_cell = self.atoms.get_cell()
|
224 | 1 | tkerber | results = [] |
225 | 1 | tkerber | txt = "Strain\t\tEnergy [eV]\n"
|
226 | 1 | tkerber | # If we load all configurations, prepare it.
|
227 | 1 | tkerber | if self.radio_results_all.get_active(): |
228 | 1 | tkerber | self.prepare_store_atoms()
|
229 | 1 | tkerber | |
230 | 1 | tkerber | try:
|
231 | 1 | tkerber | # Now, do the deformation
|
232 | 1 | tkerber | for i, d in enumerate(steps): |
233 | 1 | tkerber | deformation = np.diag(1.0 + d * deform_axes)
|
234 | 1 | tkerber | self.atoms.set_cell(np.dot(undef_cell, deformation),
|
235 | 1 | tkerber | scale_atoms=True)
|
236 | 1 | tkerber | if self.gui.simulation.has_key('progress'): |
237 | 1 | tkerber | self.gui.simulation['progress'].set_scale_progress(i) |
238 | 1 | tkerber | if self.radio_relax_on.get_active(): |
239 | 1 | tkerber | algo = getattr(ase.optimize, mininame)
|
240 | 1 | tkerber | minimizer = algo(self.atoms, logfile=logger)
|
241 | 1 | tkerber | minimizer.run(fmax=fmax, steps=self.steps.value)
|
242 | 1 | tkerber | e = self.atoms.get_potential_energy()
|
243 | 1 | tkerber | results.append((d, e)) |
244 | 1 | tkerber | txt = txt + ("%.3f\t\t%.3f\n" % (d, e))
|
245 | 1 | tkerber | self.output.set_text(txt)
|
246 | 1 | tkerber | if self.radio_results_all.get_active(): |
247 | 1 | tkerber | self.store_atoms()
|
248 | 1 | tkerber | except AseGuiCancelException:
|
249 | 1 | tkerber | # Update display to reflect cancellation of simulation.
|
250 | 1 | tkerber | self.status_label.set_text("Calculation CANCELLED.") |
251 | 1 | tkerber | self.status_label.modify_fg(gtk.STATE_NORMAL,
|
252 | 1 | tkerber | gtk.gdk.color_parse('#AA4000'))
|
253 | 1 | tkerber | except MemoryError: |
254 | 1 | tkerber | self.status_label.set_text("Out of memory, consider using LBFGS instead") |
255 | 1 | tkerber | self.status_label.modify_fg(gtk.STATE_NORMAL,
|
256 | 1 | tkerber | gtk.gdk.color_parse('#AA4000'))
|
257 | 1 | tkerber | |
258 | 1 | tkerber | else:
|
259 | 1 | tkerber | # Update display to reflect succesful end of simulation.
|
260 | 1 | tkerber | self.status_label.set_text("Calculation completed.") |
261 | 1 | tkerber | self.status_label.modify_fg(gtk.STATE_NORMAL,
|
262 | 1 | tkerber | gtk.gdk.color_parse('#007700'))
|
263 | 1 | tkerber | |
264 | 1 | tkerber | if results:
|
265 | 1 | tkerber | self.do_fit(np.array(results))
|
266 | 1 | tkerber | if self.radio_results_optimal.get_active(): |
267 | 1 | tkerber | if self.minimum_ok: |
268 | 1 | tkerber | deformation = np.diag(1.0 + self.x0 * deform_axes) |
269 | 1 | tkerber | self.atoms.set_cell(np.dot(undef_cell, deformation),
|
270 | 1 | tkerber | scale_atoms=True)
|
271 | 1 | tkerber | if self.radio_relax_on.get_active(): |
272 | 1 | tkerber | if self.gui.simulation.has_key('progress'): |
273 | 1 | tkerber | self.gui.simulation['progress'].set_scale_progress( |
274 | 1 | tkerber | len(steps))
|
275 | 1 | tkerber | algo = getattr(ase.optimize, mininame)
|
276 | 1 | tkerber | minimizer = algo(self.atoms, logfile=logger)
|
277 | 1 | tkerber | minimizer.run(fmax=fmax, steps=self.steps.value)
|
278 | 1 | tkerber | # Store the optimal configuration.
|
279 | 1 | tkerber | self.prepare_store_atoms()
|
280 | 1 | tkerber | self.store_atoms()
|
281 | 1 | tkerber | else:
|
282 | 1 | tkerber | oops("No trustworthy minimum: Old configuration kept.")
|
283 | 1 | tkerber | self.activate_output()
|
284 | 1 | tkerber | self.gui.notify_vulnerable()
|
285 | 1 | tkerber | self.end()
|
286 | 1 | tkerber | |
287 | 1 | tkerber | # If we store all configurations: Open movie window and energy graph
|
288 | 1 | tkerber | if self.gui.images.nimages > 1: |
289 | 1 | tkerber | self.gui.movie()
|
290 | 1 | tkerber | assert not np.isnan(self.gui.images.E[0]) |
291 | 1 | tkerber | if not self.gui.plot_graphs_newatoms(): |
292 | 1 | tkerber | expr = 'i, e - E[-1]'
|
293 | 1 | tkerber | self.gui.plot_graphs(expr=expr)
|
294 | 1 | tkerber | # Continuations should use the best image
|
295 | 1 | tkerber | nbest = np.argmin(np.array(results)[:,1])
|
296 | 1 | tkerber | self.start_nth_adj.value = nbest
|
297 | 1 | tkerber | |
298 | 1 | tkerber | |
299 | 1 | tkerber | def change_fit(self, widget): |
300 | 1 | tkerber | "Repeat the fitting if the order is changed."
|
301 | 1 | tkerber | # It may be called both for the button being turned on and the
|
302 | 1 | tkerber | # one being turned off. But we only want to call do_fit once.
|
303 | 1 | tkerber | # And only if there are already cached results (ie. if the
|
304 | 1 | tkerber | # order is changed AFTER the calculation is done).
|
305 | 1 | tkerber | if widget.get_active() and getattr(self, "results", None) is not None: |
306 | 1 | tkerber | self.do_fit(None) |
307 | 1 | tkerber | |
308 | 1 | tkerber | def do_fit(self, results): |
309 | 1 | tkerber | "Fit the results to a polynomial"
|
310 | 1 | tkerber | if results is None: |
311 | 1 | tkerber | results = self.results # Use cached results |
312 | 1 | tkerber | else:
|
313 | 1 | tkerber | self.results = results # Keep for next time |
314 | 1 | tkerber | self.minimum_ok = False |
315 | 1 | tkerber | if self.radio_fit_3.get_active(): |
316 | 1 | tkerber | order = 3
|
317 | 1 | tkerber | else:
|
318 | 1 | tkerber | order = 2
|
319 | 1 | tkerber | |
320 | 1 | tkerber | if len(results) < 3: |
321 | 1 | tkerber | txt = ("Insufficent data for a fit\n(only %i data points)\n"
|
322 | 1 | tkerber | % (len(results),) )
|
323 | 1 | tkerber | order = 0
|
324 | 1 | tkerber | elif len(results) == 3 and order == 3: |
325 | 1 | tkerber | txt = "REVERTING TO 2ND ORDER FIT\n(only 3 data points)\n\n"
|
326 | 1 | tkerber | order = 2
|
327 | 1 | tkerber | else:
|
328 | 1 | tkerber | txt = ""
|
329 | 1 | tkerber | |
330 | 1 | tkerber | if order > 0: |
331 | 1 | tkerber | fit0 = np.poly1d(np.polyfit(results[:,0], results[:,1], order)) |
332 | 1 | tkerber | fit1 = np.polyder(fit0, 1)
|
333 | 1 | tkerber | fit2 = np.polyder(fit1, 1)
|
334 | 1 | tkerber | |
335 | 1 | tkerber | x0 = None
|
336 | 1 | tkerber | for t in np.roots(fit1): |
337 | 1 | tkerber | if fit2(t) > 0: |
338 | 1 | tkerber | x0 = t |
339 | 1 | tkerber | break
|
340 | 1 | tkerber | if x0 is None: |
341 | 1 | tkerber | txt = txt + "No minimum found!"
|
342 | 1 | tkerber | else:
|
343 | 1 | tkerber | e0 = fit0(x0) |
344 | 1 | tkerber | e2 = fit2(x0) |
345 | 1 | tkerber | txt += "E = "
|
346 | 1 | tkerber | if order == 3: |
347 | 1 | tkerber | txt += "A(x - x0)³ + "
|
348 | 1 | tkerber | txt += "B(x - x0)² + C\n\n"
|
349 | 1 | tkerber | txt += "B = %.5g eV\n" % (e2,)
|
350 | 1 | tkerber | txt += "C = %.5g eV\n" % (e0,)
|
351 | 1 | tkerber | txt += "x0 = %.5g\n" % (x0,)
|
352 | 1 | tkerber | lowest = self.scale_offset.value - self.max_scale.value |
353 | 1 | tkerber | highest = self.scale_offset.value + self.max_scale.value |
354 | 1 | tkerber | if x0 < lowest or x0 > highest: |
355 | 1 | tkerber | txt += "\nWARNING: Minimum is outside interval\n"
|
356 | 1 | tkerber | txt += "It is UNRELIABLE!\n"
|
357 | 1 | tkerber | else:
|
358 | 1 | tkerber | self.minimum_ok = True |
359 | 1 | tkerber | self.x0 = x0
|
360 | 1 | tkerber | self.fit_output.set_text(txt)
|