Skip to content

Commit

Permalink
Multiple plots (#294)
Browse files Browse the repository at this point in the history
* handling plotting of multiple different plots

* updating readme

* addressing test faiulures
  • Loading branch information
K20shores authored Dec 19, 2024
1 parent 1d7fe7c commit ee8950a
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 61 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ Some basic plots can be made to show concentrations throughout the simulation
music_box -e Chapman -o output.csv --plot O1D
```

You can also make multiple plots by specifying groupings of species

```
music_box -e TS1 --plot O3 --plot PAN,HF
```

Note that the windows may overlap each other

### gnuplot
If you want ascii plots (maybe you're running over ssh and can't view a graphical window), you can set
the plot tool to gnuplo (`--plot-tool gnuplot`) to view some output
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
temperature [K],pressure [Pa],ALKNIT [mol m-3],BCARY [mol m-3],BENZENE [mol m-3],BIGALD [mol m-3],BIGALD1 [mol m-3],BIGALD2 [mol m-3],BIGALD3 [mol m-3],BIGALD4 [mol m-3],BIGALK [mol m-3],BIGENE [mol m-3],C2H2 [mol m-3],C2H4 [mol m-3],C2H5OH [mol m-3],C2H6 [mol m-3],C3H6 [mol m-3],C3H8 [mol m-3],CH2O [mol m-3],CH3CHO [mol m-3],CH3CN [mol m-3],CH3COCH3 [mol m-3],CH3COCHO [mol m-3],CH3COOH [mol m-3],CH3OH [mol m-3],CH3OOH [mol m-3],CH4 [mol m-3],CHBR3 [mol m-3],CO [mol m-3],CRESOL [mol m-3],DMS [mol m-3],GLYOXAL [mol m-3],H2O [mol m-3],H2O2 [mol m-3],HCN [mol m-3],HCOOH [mol m-3],HNO3 [mol m-3],HO2 [mol m-3],HO2NO2 [mol m-3],HONITR [mol m-3],HYAC [mol m-3],ISOP [mol m-3],ISOPNITA [mol m-3],ISOPNITB [mol m-3],MACR [mol m-3],MEK [mol m-3],MPAN [mol m-3],MTERP [mol m-3],MVK [mol m-3],N2O [mol m-3],N2O5 [mol m-3],NH3 [mol m-3],NH4 [mol m-3],NO [mol m-3],NO2 [mol m-3],NO3 [mol m-3],NOA [mol m-3],O3 [mol m-3],OH [mol m-3],ONITR [mol m-3],PAN [mol m-3],PBZNIT [mol m-3],PHENOL [mol m-3],SO2 [mol m-3],TERPNIT [mol m-3],TOLUENE [mol m-3],XYLENES [mol m-3],soa1_a1 [mol m-3],soa1_a2 [mol m-3],soa2_a1 [mol m-3],soa2_a2 [mol m-3],soa3_a1 [mol m-3],soa3_a2 [mol m-3],soa4_a1 [mol m-3],soa4_a2 [mol m-3],soa5_a1 [mol m-3],soa5_a2 [mol m-3],N2 [mol m-3],O2 [mol m-3],Ar [mol m-3]
301.2998962402344,98686.96875,5.603783432693925e-09,2.2607983319386883e-11,5.3438343598018845e-08,1.1622480176624961e-10,3.949651829917149e-10,3.2024342362921576e-10,3.149655742601085e-10,3.299796778673191e-09,1.3034612000288947e-07,5.975007262776328e-09,4.623101951555162e-08,7.977027443107004e-08,7.949745938522076e-08,3.040138111357546e-08,1.0126377938632938e-08,1.796534727049404e-08,3.6610101448469703e-07,6.260487753033498e-08,4.184854788701237e-09,1.8656763367010984e-07,3.070441976990186e-08,7.124479768791289e-09,3.3059375445405663e-07,3.6980307328282435e-08,7.789969282928601e-05,4.7272428119800195e-11,1.0531638421904841e-05,1.1250992394279092e-09,7.002762595579087e-13,2.3304938166282407e-08,1.2233689908226364,1.437437888324614e-07,2.7931805331101406e-08,9.805022737819429e-09,1.0224929301507477e-07,2.2180927264394425e-09,1.3299543507860196e-09,6.726044083352935e-09,6.891334824450089e-08,9.53811923861675e-08,4.661388909034105e-09,3.2254741082993373e-09,3.3039960695493364e-08,6.483837632180496e-08,1.0975576982985293e-08,1.0349549594478156e-08,6.798736557212757e-08,1.3223281330605193e-05,2.098874295982549e-11,2.9015892846647824e-08,6.000425951169596e-10,1.46746366000281e-08,9.363114015499265e-08,1.556751689945006e-11,5.3663167372937604e-09,3.437541864197501e-06,1.2562542105442303e-11,2.310847431093351e-09,7.78443781245254e-08,8.97603481725576e-10,9.44391061997537e-10,9.473863505976901e-08,1.8337329916352115e-09,6.23002621991548e-08,2.4350367626260163e-08,6.876871987801876e-05,1.9135487963195047e-07,3.969404938075627e-05,1.1374107271975104e-07,2.4266818409796294e-05,6.830680710820984e-08,3.232213140614209e-05,9.095374317832795e-08,8.41052865033824e-06,2.3667041520636728e-08,30.760167432953534,8.251401913972705,0.36793704705674146
PHOT.jsoa5_a2.s-1,PHOT.jsoa5_a1.s-1,PHOT.jsoa4_a2.s-1,PHOT.jsoa4_a1.s-1,PHOT.jsoa3_a2.s-1,PHOT.jsoa3_a1.s-1,PHOT.jsoa2_a2.s-1,PHOT.jsoa2_a1.s-1,PHOT.jsoa1_a2.s-1,PHOT.jsoa1_a1.s-1,PHOT.jsf6.s-1,PHOT.jxylolooh.s-1,PHOT.jxylenooh.s-1,PHOT.jxooh.s-1,PHOT.jtolooh.s-1,PHOT.jterprd2.s-1,PHOT.jterprd1.s-1,PHOT.jterpooh.s-1,PHOT.jterpnit.s-1,PHOT.jterp2ooh.s-1,PHOT.jtepomuc.s-1,PHOT.jso3.s-1,PHOT.jso2.s-1,PHOT.jso.s-1,PHOT.jrooh.s-1,PHOT.jpooh.s-1,PHOT.jphenooh.s-1,PHOT.jpan.s-1,PHOT.jonitr.s-1,PHOT.jocs.s-1,PHOT.joclo.s-1,PHOT.jo3_b.s-1,PHOT.jo3_a.s-1,PHOT.jo2_b.s-1,PHOT.jo2_a.s-1,PHOT.jnterpooh.s-1,PHOT.jnoa.s-1,PHOT.jno3_b.s-1,PHOT.jno3_a.s-1,PHOT.jno2.s-1,PHOT.jno.s-1,PHOT.jnc4cho.s-1,PHOT.jn2o5_b.s-1,PHOT.jn2o5_a.s-1,PHOT.jn2o.s-1,PHOT.jmvk.s-1,PHOT.jmpan.s-1,PHOT.jmgly.s-1,PHOT.jmekooh.s-1,PHOT.jmek.s-1,PHOT.jmacr_b.s-1,PHOT.jmacr_a.s-1,PHOT.jisopooh.s-1,PHOT.jisopnooh.s-1,PHOT.jhyac.s-1,PHOT.jhpald.s-1,PHOT.jhonitr.s-1,PHOT.jhocl.s-1,PHOT.jhobr.s-1,PHOT.jho2no2_b.s-1,PHOT.jho2no2_a.s-1,PHOT.jhno3.s-1,PHOT.jhf.s-1,PHOT.jhcl.s-1,PHOT.jhcfc22.s-1,PHOT.jhcfc142b.s-1,PHOT.jhcfc141b.s-1,PHOT.jhbr.s-1,PHOT.jh2so4.s-1,PHOT.jh2402.s-1,PHOT.jglyoxal.s-1,PHOT.jglyald.s-1,PHOT.jeooh.s-1,PHOT.jcofcl.s-1,PHOT.jcof2.s-1,PHOT.jco2.s-1,PHOT.jclono2_b.s-1,PHOT.jclono2_a.s-1,PHOT.jclo.s-1,PHOT.jcl2o2.s-1,PHOT.jcl2.s-1,PHOT.jchbr3.s-1,PHOT.jch4_b.s-1,PHOT.jch4_a.s-1,PHOT.jch3ooh.s-1,PHOT.jch3co3h.s-1,PHOT.jch3cl.s-1,PHOT.jch3cho.s-1,PHOT.jch3ccl3.s-1,PHOT.jch3br.s-1,PHOT.jch2o_b.s-1,PHOT.jch2o_a.s-1,PHOT.jch2br2.s-1,PHOT.jcfcl3.s-1,PHOT.jcfc115.s-1,PHOT.jcfc114.s-1,PHOT.jcfc113.s-1,PHOT.jcf3br.s-1,PHOT.jcf2clbr.s-1,PHOT.jcf2cl2.s-1,PHOT.jccl4.s-1,PHOT.jc6h5ooh.s-1,PHOT.jc3h7ooh.s-1,PHOT.jc2h5ooh.s-1,PHOT.jbzooh.s-1,PHOT.jbrono2_b.s-1,PHOT.jbrono2_a.s-1,PHOT.jbro.s-1,PHOT.jbrcl.s-1,PHOT.jbigald4.s-1,PHOT.jbigald3.s-1,PHOT.jbigald2.s-1,PHOT.jbigald1.s-1,PHOT.jbigald.s-1,PHOT.jbepomuc.s-1,PHOT.jbenzooh.s-1,PHOT.jalkooh.s-1,PHOT.jalknit.s-1,PHOT.jacet.s-1
1e-20,1e-20,1e-20,1e-20,1e-20,1e-20,1e-20,1e-20,1e-20,1e-20,1e-20,5.95e-06,5.95e-06,5.95e-06,5.95e-06,7.18e-06,7.18e-06,5.95e-06,5.95e-06,5.95e-06,0.00101,2.36e-07,1.18e-22,2.58e-22,5.95e-06,5.95e-06,5.95e-06,9.69e-07,7.18e-06,6.18e-12,0.0726,0.000449,5.13e-05,1.4e-28,0.0,5.95e-06,3.47e-05,0.0124,0.199,0.0101,0.0,3.47e-05,3.76e-09,5.54e-05,9.25e-25,5.2e-06,9.69e-07,0.00014,5.95e-06,1.25e-06,2.77e-06,2.77e-06,5.95e-06,5.95e-06,2.47e-06,6.03e-05,3.47e-05,0.000304,0.00237,1.72e-05,1.71e-06,8.54e-07,0.0,6.59e-25,8.55e-27,3.98e-26,4.36e-24,2.5499999999999998e-23,3.1e-10,3.68e-09,0.00014,6.18e-06,5.95e-06,2.2999999999999997e-24,3.3e-25,5.28e-30,1e-05,4.59e-05,0.00024,0.00198,0.00252,1.81e-06,0.0,0.0,5.95e-06,2.23e-06,4.68e-25,7.18e-06,1.79e-23,9.35e-16,4.92e-05,3.47e-05,1.15e-09,1.3799999999999998e-23,1.2300000000000001e-26,2.09e-25,2.6e-24,1.45e-12,6.95e-09,2.5e-24,1.2799999999999999e-23,5.95e-06,5.95e-06,5.95e-06,5.95e-06,0.000224,0.00127,0.0407,0.0112,6.03e-05,0.00201,0.00201,0.00141,0.00201,0.00101,5.95e-06,5.95e-06,5.95e-06,1.25e-06
3 changes: 1 addition & 2 deletions src/acom_music_box/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def parse_arguments():
parser.add_argument(
'--plot',
type=str,
action='append',
help='Plot a comma-separated list of species if gnuplot is available (e.g., CONC.A,CONC.B).'
)
parser.add_argument(
Expand Down Expand Up @@ -114,8 +115,6 @@ def main():
else:
musicBoxConfigFile = args.config

plot_species_list = args.plot.split(',') if args.plot else None

if not musicBoxConfigFile:
error = "Configuration file is required."
logger.error(error)
Expand Down
111 changes: 59 additions & 52 deletions src/acom_music_box/plot_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ class PlotOutput:
The DataFrame to be plotted.
args : argparse.Namespace
Command-line arguments or configurations specifying plot options.
species_list : list
A list of species to plot.
species_list : list of lists
A list of lists, where each sublist contains species to be plotted in a separate window.
Examples
--------
Expand All @@ -35,7 +35,7 @@ class PlotOutput:
... 'CONC.B.mol m-3': [4, 5, 6],
... 'CONC.C.mol m-3': [7, 8, 9]
... })
>>> args = Namespace(plot='CONC.A,CONC.B', plot_tool='matplotlib')
>>> args = Namespace(plot=['CONC.A,CONC.B'], plot_tool='matplotlib')
>>> plot_output = PlotOutput(df, args)
>>> plot_output.plot()
"""
Expand All @@ -54,7 +54,10 @@ def __init__(self, df, args):

self.df = df.copy(deep=True)
self.args = args
self.species_list = self._format_species_list(self.args.plot.split(',') if self.args.plot else None)
if self.args.plot:
self.species_list = [self._format_species_list(group.split(',')) for group in self.args.plot]
else:
self.species_list = None

def _format_species_list(self, species_list):
"""
Expand Down Expand Up @@ -90,64 +93,68 @@ def _plot_with_gnuplot(self):
Plot the specified species using gnuplot.
"""
# Prepare columns and data for plotting
columns = ['time'] + self.species_list
data_to_plot = self.df[columns]

data_csv = data_to_plot.to_csv(index=False)

try:
with tempfile.NamedTemporaryFile(suffix='.csv', mode='w+', delete=True) as data_file:
data_file.write(data_csv)
data_file.flush()
data_file_path = data_file.name

plot_commands = ',\n\t'.join(
f"'{data_file_path}' using 1:{i+2} with lines title '{species}'" for i,
species in enumerate(self.species_list))

gnuplot_command = f"""
set datafile separator ",";
set terminal dumb size 120,25;
set xlabel 'Time';
set ylabel 'Value';
set title 'Time vs Species';
plot {plot_commands}
"""

subprocess.run(['gnuplot', '-e', gnuplot_command], check=True)
except FileNotFoundError as e:
logging.critical("gnuplot is not installed. Skipping plotting.")
raise e
except subprocess.CalledProcessError as e:
logging.error(f"Error occurred while plotting: {e}")
raise e
if not self.species_list:
return;
for species_group in self.species_list:
columns = ['time'] + species_group
data_to_plot = self.df[columns]

data_csv = data_to_plot.to_csv(index=False)

try:
with tempfile.NamedTemporaryFile(suffix='.csv', mode='w+', delete=True) as data_file:
data_file.write(data_csv)
data_file.flush()
data_file_path = data_file.name

plot_commands = ',\n\t'.join(
f"'{data_file_path}' using 1:{i+2} with lines title '{species}'" for i,
species in enumerate(species_group))

gnuplot_command = f"""
set datafile separator ",";
set terminal dumb size 120,25;
set xlabel 'Time';
set ylabel 'Value';
set title 'Time vs Species';
plot {plot_commands}
"""

subprocess.run(['gnuplot', '-e', gnuplot_command], check=True)
except FileNotFoundError as e:
logging.critical("gnuplot is not installed. Skipping plotting.")
raise e
except subprocess.CalledProcessError as e:
logging.error(f"Error occurred while plotting: {e}")
raise e

def _plot_with_matplotlib(self):
"""
Plot the specified species using matplotlib.
"""
if not self.species_list:
return;
for species_group in self.species_list:
indexed = self.df.set_index('time')
fig, ax = plt.subplots()
indexed[species_group].plot(ax=ax)

indexed = self.df.set_index('time')

fig, ax = plt.subplots()
indexed[self.species_list].plot(ax=ax)

ax.set(xlabel='Time [s]', ylabel='Concentration [mol m-3]', title='Time vs Species')
ax.set(xlabel='Time [s]', ylabel='Concentration [mol m-3]', title='Time vs Species')

ax.spines[:].set_visible(False)
ax.spines['left'].set_visible(True)
ax.spines['bottom'].set_visible(True)
ax.spines[:].set_visible(False)
ax.spines['left'].set_visible(True)
ax.spines['bottom'].set_visible(True)

ax.grid(alpha=0.5)
ax.legend()
ax.grid(alpha=0.5)
ax.legend()

# Enable interactive data cursors with hover functionality
cursor = mplcursors.cursor(hover=True)
# Enable interactive data cursors with hover functionality
cursor = mplcursors.cursor(hover=True)

# Customize the annotation format
@cursor.connect("add")
def on_add(sel):
sel.annotation.set_text(f'Time: {sel.target[0]:.2f}\nConcentration: {sel.target[1]:1.2e}')
# Customize the annotation format
@cursor.connect("add")
def on_add(sel):
sel.annotation.set_text(f'Time: {sel.target[0]:.2f}\nConcentration: {sel.target[1]:1.2e}')

plt.show()

Expand Down
24 changes: 19 additions & 5 deletions tests/unit/test_plot_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ def setUp(self):
})

def test_format_species_list(self):
args = Namespace(plot='A,B', plot_tool='matplotlib')
args = Namespace(plot=['A', 'B'], plot_tool='matplotlib')
plot_output = PlotOutput(self.df, args)
expected_list = ['CONC.A', 'CONC.B']
expected_list = [['CONC.A'], ['CONC.B']]
self.assertEqual(plot_output.species_list, expected_list)

args = Namespace(plot='CONC.A,CONC.B', plot_tool='matplotlib')
args = Namespace(plot=['CONC.A', 'CONC.B'], plot_tool='matplotlib')
plot_output = PlotOutput(self.df, args)
self.assertEqual(plot_output.species_list, expected_list)

def test_plot_with_gnuplot(self):
args = Namespace(plot='A,B', plot_tool='gnuplot')
args = Namespace(plot=['A', 'B'], plot_tool='gnuplot')
plot_output = PlotOutput(self.df, args)
if shutil.which('gnuplot') is None:
with self.assertRaises(FileNotFoundError):
Expand All @@ -40,7 +40,21 @@ def test_plot_with_gnuplot(self):
plot_output.plot()

def test_plot_with_matplotlib(self):
args = Namespace(plot='A,B', plot_tool='matplotlib')
args = Namespace(plot=['A', 'B'], plot_tool='matplotlib')
plot_output = PlotOutput(self.df, args)
plot_output.plot()

def test_multiple_groups_with_gnuplot(self):
args = Namespace(plot=['A,B', 'C'], plot_tool='gnuplot')
plot_output = PlotOutput(self.df, args)
if shutil.which('gnuplot') is None:
with self.assertRaises(FileNotFoundError):
plot_output.plot()
else:
plot_output.plot()

def test_multiple_groups_with_matplotlib(self):
args = Namespace(plot=['A,B', 'C'], plot_tool='matplotlib')
plot_output = PlotOutput(self.df, args)
plot_output.plot()

Expand Down

0 comments on commit ee8950a

Please sign in to comment.