From bef907c0322475f17eb454410d0acea00b3ed0bc Mon Sep 17 00:00:00 2001 From: allevitan Date: Fri, 20 Mar 2026 20:01:40 +0100 Subject: [PATCH] Added a minimum plotting interval to model.inspect(dataset) to make it easier to not plot so much without manual intervention --- examples/fancy_ptycho.py | 12 ++++++------ examples/gold_ball_ptycho.py | 11 ++++------- examples/near_field_ptycho.py | 9 +++------ examples/simple_ptycho.py | 6 ++---- examples/transmission_RPI.py | 12 ++++++------ src/cdtools/models/base.py | 14 +++++++++++++- 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/examples/fancy_ptycho.py b/examples/fancy_ptycho.py index 3ac8974..eb9de73 100644 --- a/examples/fancy_ptycho.py +++ b/examples/fancy_ptycho.py @@ -32,9 +32,9 @@ recon = cdtools.reconstructors.AdamReconstructor(model, dataset) # The batch size sets the minibatch size for loss in recon.optimize(50, lr=0.02, batch_size=10): print(model.report()) - # Plotting is expensive, so we only do it every tenth epoch - if model.epoch % 10 == 0: - model.inspect(dataset) + # Because plotting can be expensive, setting a minimum plotting interval + # (in seconds) can avoid excessive replots. + model.inspect(dataset, min_interval=5) # It's common to chain several different reconstruction loops. Here, we # started with an aggressive refinement to find the probe in the previous @@ -42,12 +42,12 @@ for loss in recon.optimize(50, lr=0.02, batch_size=10): # and larger minibatch for loss in recon.optimize(50, lr=0.005, batch_size=50): print(model.report()) - if model.epoch % 10 == 0: - model.inspect(dataset) + model.inspect(dataset, min_interval=5) # This orthogonalizes the recovered probe modes model.tidy_probes() -model.inspect(dataset) +# Setting replot_all will reopen any windows which were closed earlier +model.inspect(dataset, replot_all=True) model.compare(dataset) plt.show() diff --git a/examples/gold_ball_ptycho.py b/examples/gold_ball_ptycho.py index fd7ba32..944c760 100644 --- a/examples/gold_ball_ptycho.py +++ b/examples/gold_ball_ptycho.py @@ -54,13 +54,11 @@ with model.save_on_exception( for loss in recon.optimize(20, lr=0.005, batch_size=50): print(model.report()) - if model.epoch % 10 == 0: - model.inspect(dataset) + model.inspect(dataset, min_interval=5) for loss in recon.optimize(50, lr=0.002, batch_size=100): print(model.report()) - if model.epoch % 10 == 0: - model.inspect(dataset) + model.inspect(dataset, min_interval=5) # We can often reset our guess of the probe positions once we have a # good guess of probe and object, but in this case it causes the @@ -71,8 +69,7 @@ with model.save_on_exception( # the loss fails to improve after 10 epochs for loss in recon.optimize(100, lr=0.001, batch_size=100, schedule=True): print(model.report()) - if model.epoch % 10 == 0: - model.inspect(dataset) + model.inspect(dataset, min_interval=5) model.tidy_probes() @@ -80,6 +77,6 @@ model.tidy_probes() # This saves the final result model.save_to_h5('example_reconstructions/gold_balls.h5', dataset) -model.inspect(dataset) +model.inspect(dataset, replot_all=True) model.compare(dataset) plt.show() diff --git a/examples/near_field_ptycho.py b/examples/near_field_ptycho.py index 7b86bd3..4534ee5 100644 --- a/examples/near_field_ptycho.py +++ b/examples/near_field_ptycho.py @@ -40,18 +40,15 @@ recon = cdtools.reconstructors.AdamReconstructor(model, dataset) for loss in recon.optimize(100, lr=0.04, batch_size=10): print(model.report()) - # Plotting is expensive, so we only do it every tenth epoch - if model.epoch % 10 == 0: - model.inspect(dataset) + model.inspect(dataset, min_interval=5) for loss in recon.optimize(50, lr=0.005, batch_size=50): print(model.report()) - if model.epoch % 10 == 0: - model.inspect(dataset) + model.inspect(dataset, min_interval=5) # This orthogonalizes the recovered probe modes model.tidy_probes() -model.inspect(dataset) +model.inspect(dataset, replot_all=True) model.compare(dataset) plt.show() diff --git a/examples/simple_ptycho.py b/examples/simple_ptycho.py index 467b9af..af8aeb1 100644 --- a/examples/simple_ptycho.py +++ b/examples/simple_ptycho.py @@ -30,10 +30,8 @@ for loss in model.Adam_optimize(30, dataset, batch_size=10): # We print a quick report of the optimization status print(model.report()) # And liveplot the updates to the model as they happen - if model.epoch % 10 == 0: - model.inspect(dataset) + model.inspect(dataset) -# We study the results -model.inspect(dataset, replot_all=True) +# We open a comparison of the simulated and measured data model.compare(dataset) plt.show() diff --git a/examples/transmission_RPI.py b/examples/transmission_RPI.py index c8fd424..32c02d5 100644 --- a/examples/transmission_RPI.py +++ b/examples/transmission_RPI.py @@ -30,20 +30,20 @@ if t.cuda.is_available(): # Note that the inspect step takes the vast majority of the time # The regularization is an L2 regularizer that empirically helps accelerate # convergence -for loss in model.Adam_optimize(30, dataset, lr=0.4, regularization_factor=[0.05,0.05]): - model.inspect(dataset) +for loss in model.LBFGS_optimize(30, dataset, lr=0.4, regularization_factor=[0.05,0.05]): + model.inspect(dataset, min_interval=5) print(model.report()) # Now we use the regularizer to damp all but the top modes for loss in model.LBFGS_optimize(50, dataset, lr=0.4, regularization_factor=[0.001,0.1]): - #model.inspect(dataset) + model.inspect(dataset, min_interval=5) print(model.report()) -# Save results to a python dictionary -results = model.save_results() +# Save results to an h5 file +model.save_to_h5('example_reconstructions/transmission_RPI.h5', dataset) # Finally, we plot the results -model.inspect(dataset) +model.inspect(dataset, replot_all=True) model.compare(dataset) plt.show() diff --git a/src/cdtools/models/base.py b/src/cdtools/models/base.py index f6473dc..de9cc72 100644 --- a/src/cdtools/models/base.py +++ b/src/cdtools/models/base.py @@ -69,6 +69,7 @@ class CDIModel(t.nn.Module): self.panel_plot_mode = panel_plot_mode self.plot_level = plot_level self.has_inspect_been_called = False + self.last_inspected_time = None def from_dataset(self, dataset): raise NotImplementedError() @@ -557,7 +558,7 @@ class CDIModel(t.nn.Module): plot_list = [] - def inspect(self, dataset=None, replot_all=False): + def inspect(self, dataset=None, replot_all=False, min_interval=None): """Plots all the plots defined in the model's plot_panel_list and plot_list attributes Updates any previously plotted figures that are still open. Figures @@ -586,8 +587,17 @@ class CDIModel(t.nn.Module): Optional, a dataset matched to the model type replot_all : bool, default: False If True, recreate figures that were previously closed by the user. + min_interval : float, optional + If set, skip updating plots if fewer than this many seconds have + elapsed since the last call to inspect(). The time of the last + update is stored in self.last_inspected_time. """ + if (min_interval is not None + and self.last_inspected_time is not None + and time.time() - self.last_inspected_time < min_interval): + return + plot_panel_list = getattr(self, 'plot_panel_list', None) or [] plot_list = getattr(self, 'plot_list', None) or [] @@ -624,6 +634,8 @@ class CDIModel(t.nn.Module): fig.canvas.flush_events() self.has_inspect_been_called = True + self.last_inspected_time = time.time() + def _is_backend_interactive( self