11
11
from typing import Optional , Union
12
12
from collections import defaultdict
13
13
14
- import matplotlib as mpl
14
+ import matplotlib . axis
15
15
import matplotlib .pyplot as plt
16
16
import MDAnalysis as mda
17
17
import numpy as np
20
20
import seaborn .objects as so
21
21
from seaborn import axes_style
22
22
23
+ from kimmdy .config import Config
23
24
from kimmdy .parsing import read_json , write_json , read_time_marker
25
+ from kimmdy .plugins import discover_plugins
24
26
from kimmdy .recipe import Bind , Break , DeferredRecipeSteps , Place , RecipeCollection
25
- from kimmdy .utils import run_shell_cmd , get_task_directories
27
+ from kimmdy .utils import read_reaction_time_marker , run_shell_cmd , get_task_directories
26
28
from kimmdy .constants import MARK_DONE , MARK_STARTED
27
29
28
30
@@ -68,7 +70,6 @@ def concat_traj(
68
70
"""
69
71
run_dir = Path (dir ).expanduser ().resolve ()
70
72
analysis_dir = get_analysis_dir (run_dir )
71
-
72
73
directories = get_task_directories (run_dir , steps )
73
74
if not directories :
74
75
raise ValueError (
@@ -90,33 +91,45 @@ def concat_traj(
90
91
output = output_group
91
92
92
93
## gather trajectories
93
- trajectories = []
94
+ trajectories : list [ Path ] = []
94
95
tprs = []
95
96
gros = []
96
97
for d in directories :
97
- trajectories .extend (d .glob (f"*.{ filetype } " ))
98
+ trjs = list (d .glob (f"*.{ filetype } " ))
99
+ trajectories .extend ([t for t in trjs if not ".kimmdytrunc." in t .name ])
98
100
tprs .extend (d .glob ("*.tpr" ))
99
101
gros .extend (d .glob ("*.gro" ))
100
102
101
103
assert (
102
104
len (trajectories ) > 0
103
- ), f"No trrs found to concatenate in { run_dir } with subdirectory names { steps } "
105
+ ), f"No trajectories found to concatenate in { run_dir } with subdirectory names { steps } "
106
+
107
+ for i , trj in enumerate (trajectories ):
108
+ task_dir = trj .parent
109
+ time = read_reaction_time_marker (task_dir )
110
+ if time is not None :
111
+ new_trj = trj .with_suffix (".kimmdytrunc.xtc" )
112
+ run_shell_cmd (
113
+ f"echo '0' | gmx trjconv -f { trj } -s { tprs [i ]} -e { time } -o { new_trj } " ,
114
+ cwd = run_dir ,
115
+ )
116
+ trajectories [i ] = new_trj
104
117
105
- trajectories = [str (t ) for t in trajectories ]
106
- print (trajectories )
118
+ flat_trajectories : list [str ] = [str (trj ) for trj in trajectories ]
107
119
108
120
## write concatenated trajectory
109
121
tmp_xtc = str (out_xtc .with_name ("tmp.xtc" ))
110
122
run_shell_cmd (
111
- f "gmx trjcat -f { ' ' .join (trajectories )} -o { tmp_xtc } -cat" ,
123
+ rf "gmx trjcat -f { ' ' .join (flat_trajectories )} -o { tmp_xtc } -cat" ,
112
124
cwd = run_dir ,
113
125
)
114
126
run_shell_cmd (
115
- f "echo 'Protein\n { output } ' | gmx trjconv -dt 0 -f { tmp_xtc } -s { tprs [0 ]} -o { str (out_xtc )} -center -pbc mol" ,
127
+ s = rf "echo -e 'Protein\n{ output } ' | gmx trjconv -dt 0 -f { tmp_xtc } -s { tprs [0 ]} -o { str (out_xtc )} -center -pbc mol" ,
116
128
cwd = run_dir ,
117
129
)
130
+ assert out_xtc .exists (), f"Concatenated trajectory { out_xtc } not found."
118
131
run_shell_cmd (
119
- f "echo 'Protein\n { output } ' | gmx trjconv -dump 0 -f { tmp_xtc } -s { tprs [0 ]} -o { str (out_gro )} -center -pbc mol" ,
132
+ rf "echo -e 'Protein\n{ output } ' | gmx trjconv -dt 0 -dump 0 -f { tmp_xtc } -s { tprs [0 ]} -o { str (out_gro )} -center -pbc mol" ,
120
133
cwd = run_dir ,
121
134
)
122
135
run_shell_cmd (f"rm { tmp_xtc } " , cwd = run_dir )
@@ -126,7 +139,11 @@ def concat_traj(
126
139
127
140
128
141
def plot_energy (
129
- dir : str , steps : Union [list [str ], str ], terms : list [str ], open_plot : bool = False
142
+ dir : str ,
143
+ steps : Union [list [str ], str ],
144
+ terms : list [str ],
145
+ open_plot : bool = False ,
146
+ truncate : bool = True ,
130
147
):
131
148
"""Plot GROMACS energy for a KIMMDY run.
132
149
@@ -139,8 +156,10 @@ def plot_energy(
139
156
Default is "all".
140
157
terms
141
158
Terms from gmx energy that will be plotted. Uses 'Potential' by default.
142
- open_plot :
159
+ open_plot
143
160
Open plot in default system viewer.
161
+ truncate
162
+ Truncate energy files to the reaction time marker.
144
163
"""
145
164
run_dir = Path (dir ).expanduser ().resolve ()
146
165
xvg_entries = ["time" ] + terms
@@ -153,9 +172,10 @@ def plot_energy(
153
172
xvgs_dir .mkdir (exist_ok = True )
154
173
155
174
## gather energy files
156
- edrs = []
175
+ edrs : list [ Path ] = []
157
176
for d in subdirs_matched :
158
- edrs .extend (d .glob ("*.edr" ))
177
+ new_edrs = d .glob ("*.edr" )
178
+ edrs .extend ([edr for edr in new_edrs if not ".kimmdytrunc." in edr .name ])
159
179
assert (
160
180
len (edrs ) > 0
161
181
), f"No GROMACS energy files in { run_dir } with subdirectory names { steps } "
@@ -165,11 +185,19 @@ def plot_energy(
165
185
time_offset = 0
166
186
for i , edr in enumerate (edrs ):
167
187
## write energy .xvg file
188
+ task_dir = edr .parent
168
189
xvg = str (xvgs_dir / edr .parents [0 ].with_suffix (".xvg" ).name )
169
190
step_name = edr .parents [0 ].name .split ("_" )[1 ]
170
191
192
+ time = read_reaction_time_marker (task_dir )
193
+ if time is not None and truncate :
194
+ print (f"Truncating { edr } to { time } ps." )
195
+ new_edr = edr .with_suffix (".kimmdytrunc.edr" )
196
+ run_shell_cmd (f"gmx eneconv -f { edr } -e { time } -o { new_edr } " )
197
+ edr = new_edr
198
+
171
199
run_shell_cmd (
172
- f"echo '{ terms_str } \n \n ' | gmx energy -f { str ( edr ) } -o { xvg } " ,
200
+ f"echo '{ terms_str } \n \n ' | gmx energy -f { edr } -o { xvg } " ,
173
201
cwd = run_dir ,
174
202
)
175
203
@@ -217,7 +245,9 @@ def plot_energy(
217
245
plt .text (x = t , y = v + 0.5 , s = s , fontsize = 6 )
218
246
219
247
ax = plt .gca ()
220
- steps_y_axis = [c for c in ax .get_children () if isinstance (c , mpl .axis .YAxis )][0 ]
248
+ steps_y_axis = [
249
+ c for c in ax .get_children () if isinstance (c , matplotlib .axis .YAxis )
250
+ ][0 ]
221
251
steps_y_axis .set_visible (False )
222
252
output_path = str (run_dir / "analysis" / "energy.png" )
223
253
plt .savefig (output_path , dpi = 300 )
@@ -423,7 +453,6 @@ def radical_migration(
423
453
out_path = analysis_dir / "radical_migration.json"
424
454
with open (out_path , "w" ) as json_file :
425
455
json .dump (unique_migrations , json_file )
426
- print ("Done!" )
427
456
428
457
429
458
def plot_rates (dir : str , open : bool = False ):
@@ -694,7 +723,7 @@ def get_analysis_cmdline_args() -> argparse.Namespace:
694
723
name = "trjcat" , help = "Concatenate trajectories of a KIMMDY run"
695
724
)
696
725
parser_trjcat .add_argument (
697
- "dir" , type = str , help = "KIMMDY run directory to be analysed."
726
+ "dir" , type = str , help = "KIMMDY run directory to be analysed." , nargs = "?"
698
727
)
699
728
parser_trjcat .add_argument ("--filetype" , "-f" , default = "xtc" )
700
729
parser_trjcat .add_argument (
@@ -708,11 +737,13 @@ def get_analysis_cmdline_args() -> argparse.Namespace:
708
737
)
709
738
parser_trjcat .add_argument (
710
739
"--open-vmd" ,
740
+ "-o" ,
711
741
action = "store_true" ,
712
742
help = "Open VMD with the concatenated trajectory." ,
713
743
)
714
744
parser_trjcat .add_argument (
715
745
"--output-group" ,
746
+ "-g" ,
716
747
type = str ,
717
748
help = "Index group to include in the output. Default is 'Protein' for xtc and 'System' for trr." ,
718
749
)
@@ -721,7 +752,7 @@ def get_analysis_cmdline_args() -> argparse.Namespace:
721
752
name = "energy" , help = "Plot GROMACS energy for a KIMMDY run"
722
753
)
723
754
parser_energy .add_argument (
724
- "dir" , type = str , help = "KIMMDY run directory to be analysed."
755
+ "dir" , type = str , help = "KIMMDY run directory to be analysed." , nargs = "?"
725
756
)
726
757
parser_energy .add_argument (
727
758
"--steps" ,
@@ -743,17 +774,21 @@ def get_analysis_cmdline_args() -> argparse.Namespace:
743
774
)
744
775
parser_energy .add_argument (
745
776
"--open-plot" ,
746
- "-p" ,
777
+ "-o" ,
778
+ action = "store_true" ,
779
+ help = "Open plot in default system viewer." ,
780
+ )
781
+ parser_energy .add_argument (
782
+ "--no-truncate" ,
747
783
action = "store_true" ,
748
784
help = "Open plot in default system viewer." ,
749
785
)
750
-
751
786
parser_radical_population = subparsers .add_parser (
752
787
name = "radical_population" ,
753
788
help = "Plot population of radicals for one or multiple KIMMDY run(s)" ,
754
789
)
755
790
parser_radical_population .add_argument (
756
- "dir" , type = str , help = "KIMMDY run directory to be analysed."
791
+ "dir" , type = str , help = "KIMMDY run directory to be analysed." , nargs = "?"
757
792
)
758
793
parser_radical_population .add_argument (
759
794
"--population_type" ,
@@ -820,7 +855,7 @@ def get_analysis_cmdline_args() -> argparse.Namespace:
820
855
help = "Plot rates of all possible reactions after a MD run. Rates must have been saved!" ,
821
856
)
822
857
parser_rates .add_argument (
823
- "dir" , type = str , help = "KIMMDY run directory to be analysed."
858
+ "dir" , type = str , help = "KIMMDY run directory to be analysed." , nargs = "?"
824
859
)
825
860
parser_rates .add_argument (
826
861
"--open" ,
@@ -834,7 +869,7 @@ def get_analysis_cmdline_args() -> argparse.Namespace:
834
869
help = "Plot runtime of the tasks of a kimmdy run." ,
835
870
)
836
871
parser_runtime .add_argument (
837
- "dir" , type = str , help = "KIMMDY run directory to be analysed."
872
+ "dir" , type = str , help = "KIMMDY run directory to be analysed." , nargs = "?"
838
873
)
839
874
parser_runtime .add_argument (
840
875
"--open-plot" ,
@@ -847,27 +882,46 @@ def get_analysis_cmdline_args() -> argparse.Namespace:
847
882
help = "Plot counts of reaction participation per atom id" ,
848
883
)
849
884
parser_reaction_participation .add_argument (
850
- "dir" , type = str , help = "KIMMDY run directory to be analysed."
885
+ "dir" , type = str , help = "KIMMDY run directory to be analysed." , nargs = "?"
851
886
)
852
887
parser_reaction_participation .add_argument (
853
888
"--open-plot" ,
854
889
"-p" ,
855
890
action = "store_true" ,
856
891
help = "Open plot in default system viewer." ,
857
892
)
893
+
894
+ for subparser in subparsers .choices .values ():
895
+ subparser .add_argument (
896
+ "--input" ,
897
+ "-i" ,
898
+ type = str ,
899
+ help = "Kimmdy input file. Default `kimmdy.yml`. Only used to infer the output directory if `dir` is not provided." ,
900
+ default = "kimmdy.yml" ,
901
+ )
902
+
858
903
return parser .parse_args ()
859
904
860
905
861
906
def entry_point_analysis ():
862
907
"""Analyse existing KIMMDY runs."""
863
908
args = get_analysis_cmdline_args ()
909
+ if hasattr (args , "dir" ) and args .dir is None :
910
+ discover_plugins ()
911
+ # the restart option is used here to avoid creating a new
912
+ # output directory and instead use the one from the config verbatim
913
+ # without incrementing a number
914
+ config = Config (input_file = args .input , restart = True )
915
+ args .dir = str (config .out )
864
916
865
917
if args .module == "trjcat" :
866
918
concat_traj (
867
919
args .dir , args .filetype , args .steps , args .open_vmd , args .output_group
868
920
)
869
921
elif args .module == "energy" :
870
- plot_energy (args .dir , args .steps , args .terms , args .open_plot )
922
+ plot_energy (
923
+ args .dir , args .steps , args .terms , args .open_plot , not args .no_truncate
924
+ )
871
925
elif args .module == "radical_population" :
872
926
radical_population (
873
927
args .dir ,
@@ -890,5 +944,5 @@ def entry_point_analysis():
890
944
)
891
945
else :
892
946
print (
893
- "No analysis module specified. Use -h for help and a list of available modules."
947
+ "No analysis module specified. Use -h for -- help and a list of available modules."
894
948
)
0 commit comments