minor bug in plot_comparison

Dear CADET-Process Devs,

when using the attribute plot_individual = True while having multiple references with the same metric in one comparator, the created file is overwritten due to the same file names used. This is not so important as show = 1 still works, but I guess it’s not as intended.

The MinExample doesn’t make any sense, but it shows the issue:

from examples.batch_elution.process import process
import numpy as np

from CADETProcess.simulator import Cadet
simulator = Cadet()
simulation_result = simulator.simulate(process)
from CADETProcess.reference import ReferenceIO

# gauss peaks as reference data
def gaussian(x, peak_time, peak_height, std_dev):
    return peak_height * np.exp(-((x - peak_time) ** 2) / (2 * std_dev ** 2))

time = np.array(np.linspace(0, process.cycle_time, 10*60))

data1 = gaussian(time, 6*60, 15, 10)
data2 = gaussian(time, 7.5*60, 5, 30)
data = np.stack((data1, data2)).T

# reference for component A and B
reference_1 = ReferenceIO('ref1', time, data, component_system= process.component_system)
reference_2 = ReferenceIO('ref2', time, data, component_system= process.component_system)

from CADETProcess.comparison import Comparator
metric = 'NRMSE'

comparator = Comparator('comp_test')
comparator.add_reference(reference_1)
comparator.add_difference_metric(
                            metric, reference_1,
                            'column.inlet',
                            )
comparator.add_reference(reference_2)
comparator.add_difference_metric(
                            metric, reference_2,
                            'outlet.inlet',
                            )
_  = comparator.plot_comparison(simulation_result, plot_individual= 0, show=0, file_name= 'C:\\Data\\test.png') # works -> 2 plots in 1 file
# _  = comparator.plot_comparison(simulation_result, plot_individual= 1, show=0, file_name= 'C:\\Data\\test.png') # doesn't work -> 1 plot in 1 file instead of 2 plots in 2 files

Hey @ortler,

good catch! I think this is because both references have the same string representation and are consequently overwritten, as can be seen here.

To be honest, I was never really happy about how saving figures with the plot_individual flag was implemented. I mean, what does the file_name argument even mean when we have to store multiple figures? Should it be used as a name base and then get some suffix (which is what I assume I tried here), or should we throw an error? Do you have any suggestions?

In the meantime, I would suggest not giving a file_name when using plot_individual and save the figures manually:

[...]
figs, axs = comparator.plot_comparison(simulation_result, plot_individual=True, show=True)

file_name = 'comparison'
file_format = 'png'
for i, fig in enumerate(figs):
    fig.savefig(f"{file_name}_{i}.{file_format}")

Hope that helps. But seriously, please let us know your thoughts on how this could be handled better! :nerd_face:

Hi Johannes,

thank you for that workaround code.

Yes, file_name doesn’t make much sense in case of multiple files. The metric label is already used for creating the filename. Why not introduce file_path as parameter and let the name be created automatically based on the already given names:
comparatar.name + reference.name + component.name + metric
if available: species.name
optional: solution.path

But, too many of the names result in a very long file name which is not ideal, however not enough names might result in the very same file name again. So you could also introduce a parameter where the user specifies the names used, as they should know best which names are different in the comparator plots, eg:
file_name_constructor = [comparatar.name, component.name, metric ]

Yeah, that might be an idea. I’ll consider it. However, this is not something that’s super high up on the priority list.

Hi Johannes,

I realized from the beginning it won’t be a priority.
When time allows it would be also nice to align all build-in plot functions. I’m thinking of the reference and simulation plots as an example which have more functionality than the comparison plots.

Absolutely, it would be great if one day we could meet to write some design specs for this. Like,

  • What features should be exposed?
  • How do we expose them ((kw)args or simple classes (such as Layout))?
  • How to handle multiple axes?

etc.