1515
1616def test_pyptv_batch (test_data_dir ):
1717 """Test batch processing with test cavity data using YAML parameters and validate output."""
18- test_dir = test_data_dir
18+ test_dir = test_data_dir / "test_cavity"
1919 assert test_dir .exists (), f"Test directory { test_dir } not found"
2020
2121 yaml_file = test_dir / "parameters_Run1.yaml"
@@ -48,7 +48,7 @@ def test_pyptv_batch(test_data_dir):
4848
4949def test_pyptv_batch_with_repetitions (test_data_dir ):
5050 """Test batch processing with multiple repetitions"""
51- test_dir = test_data_dir
51+ test_dir = test_data_dir / "test_cavity"
5252 yaml_file = test_dir / "parameters_Run1.yaml"
5353
5454 # Test smaller frame range with repetitions
@@ -81,7 +81,7 @@ def test_pyptv_batch_validation_errors():
8181
8282def test_pyptv_batch_produces_results (test_data_dir ):
8383 """Test that batch processing actually produces correspondence and tracking results"""
84- test_dir = test_data_dir
84+ test_dir = test_data_dir / "test_cavity"
8585 yaml_file = test_dir / "parameters_Run1.yaml"
8686
8787 # Test specific frame
@@ -119,7 +119,7 @@ def test_pyptv_batch_produces_results(test_data_dir):
119119
120120def test_pyptv_batch_tracking_results (test_data_dir ):
121121 """Test that batch processing with multiple frames produces tracking results and validates output."""
122- test_dir = test_data_dir
122+ test_dir = test_data_dir / "test_cavity"
123123 yaml_file = test_dir / "parameters_Run1.yaml"
124124 start_frame = 10000
125125 end_frame = 10004
@@ -141,7 +141,7 @@ def test_pyptv_batch_tracking_results(test_data_dir):
141141
142142def test_pyptv_batch_tracking_mode_only (test_data_dir ):
143143 """Test batch processing with mode='tracking' only, with debug output"""
144- test_dir = test_data_dir
144+ test_dir = test_data_dir / "test_cavity"
145145 yaml_file = test_dir / "parameters_Run1.yaml"
146146 start_frame = 10000
147147 end_frame = 10004
@@ -176,34 +176,38 @@ def test_pyptv_batch_tracking_mode_only_with_temp_yaml(test_data_dir):
176176 import tempfile
177177 import shutil
178178 import yaml
179- test_dir = test_data_dir
179+ test_dir = test_data_dir / "test_cavity"
180180 orig_yaml = test_dir / "parameters_Run1.yaml"
181181 start_frame = 10000
182182 end_frame = 10004
183183 res_dir = test_dir / "res"
184184 if res_dir .exists ():
185185 shutil .rmtree (res_dir )
186186 # Copy original YAML to temp file
187- with tempfile .NamedTemporaryFile ('w' , delete = False , suffix = '.yaml' , dir = test_dir ) as tmp :
188- temp_yaml = tmp .name
189- with open (orig_yaml , 'r' ) as orig_f :
190- orig_content = yaml .safe_load (orig_f )
191- yaml .safe_dump (orig_content , tmp )
192- print (f"Running tracking mode with temp YAML: { temp_yaml } " )
193- print (f"Frame range: { start_frame } to { end_frame } " )
194- pyptv_batch .main (temp_yaml , start_frame , end_frame , mode = "sequence" )
195- # Extract and print tracking parameters
196- with open (temp_yaml , 'r' ) as f :
197- params = yaml .safe_load (f )
198- track_params = params .get ('track' , {})
199- print ("Tracking parameters:" )
200- for k , v in track_params .items ():
201- print (f" { k } : { v } " )
187+ temp_yaml = None
202188 try :
189+ with tempfile .NamedTemporaryFile ('w' , delete = False , suffix = '.yaml' , dir = test_dir ) as tmp :
190+ temp_yaml = tmp .name
191+ with open (orig_yaml , 'r' ) as orig_f :
192+ orig_content = yaml .safe_load (orig_f )
193+ yaml .safe_dump (orig_content , tmp )
194+ print (f"Running tracking mode with temp YAML: { temp_yaml } " )
195+ print (f"Frame range: { start_frame } to { end_frame } " )
196+ pyptv_batch .main (temp_yaml , start_frame , end_frame , mode = "sequence" )
197+ # Extract and print tracking parameters
198+ with open (temp_yaml , 'r' ) as f :
199+ params = yaml .safe_load (f )
200+ track_params = params .get ('track' , {})
201+ print ("Tracking parameters:" )
202+ for k , v in track_params .items ():
203+ print (f" { k } : { v } " )
203204 pyptv_batch .main (temp_yaml , start_frame , end_frame , mode = "tracking" )
204205 except Exception as e :
205206 print (f"Tracking mode batch processing failed: { str (e )} " )
206207 pytest .fail (f"Tracking mode batch processing failed: { str (e )} " )
208+ finally :
209+ if temp_yaml is not None :
210+ Path (temp_yaml ).unlink (missing_ok = True )
207211 assert res_dir .exists (), "Results directory should be created in tracking mode"
208212 print (f"Tracking mode test completed for frames { start_frame } to { end_frame } " )
209213 for frame in range (start_frame , end_frame + 1 ):
@@ -222,104 +226,122 @@ def test_pyptv_batch_tracking_mode_only_with_temp_yaml_collect_results(test_data
222226 import yaml
223227 import re
224228 import subprocess
225- test_dir = test_data_dir
229+ test_dir = test_data_dir / "test_cavity"
226230 orig_yaml = test_dir / "parameters_Run1.yaml"
227231 start_frame = 10000
228232 end_frame = 10004
229233 res_dir = test_dir / "res"
230234 if res_dir .exists ():
231235 shutil .rmtree (res_dir )
232- # Copy original YAML to temp file
233- with tempfile . NamedTemporaryFile ( 'w' , delete = False , suffix = '.yaml' , dir = test_dir ) as tmp :
234- temp_yaml = tmp . name
235- with open ( orig_yaml , 'r' ) as orig_f :
236- orig_content = yaml . safe_load ( orig_f )
237- yaml . safe_dump ( orig_content , tmp )
238- # Extract tracking parameters
239- with open (temp_yaml , 'r' ) as f :
240- params = yaml .safe_load (f )
241- track_params = params . get ( 'track' , {} )
242- # Run sequence mode (no need to capture output)
243- pyptv_batch . main (temp_yaml , start_frame , end_frame , mode = "sequence" )
244- # Run tracking mode and capture output to file, set cwd to test_dir
245- with tempfile . NamedTemporaryFile ( 'w+' , delete = False , suffix = '.txt' , dir = test_dir ) as out_file :
246- out_path = out_file . name
247- cmd = [ sys . executable , '-m' , 'pyptv. pyptv_batch' , os . path . basename (temp_yaml ), str ( start_frame ), str ( end_frame ), '-- mode' , 'tracking' ]
248- try :
249- subprocess . run ( cmd , stdout = out_file , stderr = subprocess . STDOUT , check = True , cwd = test_dir )
250- except subprocess . CalledProcessError :
251- out_file . flush ()
252- with open ( out_path , 'r' ) as f :
253- print ( " \n --- Subprocess output ---" )
254- print ( f . read ())
255- raise
256- # Parse 'Average over sequence' line from file
257- avg_particles = avg_links = avg_lost = None
258- with open ( out_path , 'r' ) as f :
259- for line in f :
260- m = re . search ( r" Average over sequence, particles:\s*([\d\.-]+), links:\s*([\d\.-]+), lost:\s*([\d\.-]+)" , line )
261- if m :
262- avg_particles = float ( m . group ( 1 ))
263- avg_links = float ( m . group ( 2 ))
264- avg_lost = float ( m . group ( 3 ) )
265- break
266- # Create DataFrame to collect results
267- results = []
268- # Store original run
269- row = { ** track_params , 'avg_particles' : avg_particles , 'avg_links' : avg_links , 'avg_lost' : avg_lost , 'param_changed' : None , 'change' : 0.0 }
270- results . append ( row )
271-
272- # Loop: for each numeric track_param, perturb by +10% and rerun tracking
273- for param , value in track_params . items ():
274- if isinstance ( value , ( int , float )):
275- # Create new temp YAML with perturbed parameter
276- with tempfile . NamedTemporaryFile ( 'w' , delete = False , suffix = '.yaml' , dir = test_dir ) as tmp2 :
277- temp_yaml2 = tmp2 . name
278- with open ( orig_yaml , 'r' ) as orig_f :
279- orig_content2 = yaml . safe_load ( orig_f )
280- # Update the parameter by +10%
281- new_val = value * 1.1
282- orig_content2 [ 'track' ][ param ] = type ( value )( new_val )
283- yaml . safe_dump ( orig_content2 , tmp2 )
284- # Run sequence mode (to prep files)
285- pyptv_batch . main ( temp_yaml2 , start_frame , end_frame , mode = "sequence" )
286- # Run tracking mode and capture output
287- with tempfile . NamedTemporaryFile ( 'w+' , delete = False , suffix = '.txt' , dir = test_dir ) as out_file2 :
288- out_path2 = out_file2 . name
289- cmd2 = [ sys . executable , '-m' , 'pyptv.pyptv_batch' , os . path . basename ( temp_yaml2 ), str ( start_frame ), str ( end_frame ), '--mode' , 'tracking' ]
236+ temp_yaml = None
237+ out_path = None
238+ summary_path = test_dir / "tracking_run_summary.csv"
239+ try :
240+ # Copy original YAML to temp file
241+ with tempfile . NamedTemporaryFile ( 'w' , delete = False , suffix = '.yaml' , dir = test_dir ) as tmp :
242+ temp_yaml = tmp . name
243+ with open (orig_yaml , 'r' ) as orig_f :
244+ orig_content = yaml .safe_load (orig_f )
245+ yaml . safe_dump ( orig_content , tmp )
246+ # Extract tracking parameters
247+ with open (temp_yaml , 'r' ) as f :
248+ params = yaml . safe_load ( f )
249+ track_params = params . get ( 'track' , {})
250+ # Run sequence mode (no need to capture output)
251+ pyptv_batch . main (temp_yaml , start_frame , end_frame , mode = "sequence" )
252+ # Run tracking mode and capture output to file, set cwd to test_dir
253+ with tempfile . NamedTemporaryFile ( 'w+' , delete = False , suffix = '.txt' , dir = test_dir ) as out_file :
254+ out_path = out_file . name
255+ cmd = [ sys . executable , '-m' , 'pyptv.pyptv_batch' , os . path . basename ( temp_yaml ), str ( start_frame ), str ( end_frame ), '--mode' , 'tracking' ]
256+ try :
257+ subprocess . run ( cmd , stdout = out_file , stderr = subprocess . STDOUT , check = True , cwd = test_dir )
258+ except subprocess . CalledProcessError :
259+ out_file . flush ()
260+ with open ( out_path , 'r' ) as f :
261+ print ( " \n --- Subprocess output ---" )
262+ print ( f . read ())
263+ raise
264+ # Parse ' Average over sequence' line from file
265+ avg_particles = avg_links = avg_lost = None
266+ with open ( out_path , 'r' ) as f :
267+ for line in f :
268+ m = re . search ( r"Average over sequence, particles:\s*([\d\.-]+), links:\s*([\d\.-]+), lost:\s*([\d\.-]+)" , line )
269+ if m :
270+ avg_particles = float ( m . group ( 1 ))
271+ avg_links = float ( m . group ( 2 ))
272+ avg_lost = float ( m . group ( 3 ))
273+ break
274+ # Create DataFrame to collect results
275+ results = []
276+ # Store original run
277+ row = { ** track_params , 'avg_particles' : avg_particles , 'avg_links' : avg_links , 'avg_lost' : avg_lost , 'param_changed' : None , 'change' : 0.0 }
278+ results . append ( row )
279+
280+ # Loop: for each numeric track_param, perturb by +10% and rerun tracking
281+ for param , value in track_params . items ():
282+ if isinstance ( value , ( int , float )) :
283+ temp_yaml2 = None
284+ out_path2 = None
285+ # Create new temp YAML with perturbed parameter
286+ with tempfile . NamedTemporaryFile ( 'w' , delete = False , suffix = '.yaml' , dir = test_dir ) as tmp2 :
287+ temp_yaml2 = tmp2 . name
288+ with open ( orig_yaml , 'r' ) as orig_f :
289+ orig_content2 = yaml . safe_load ( orig_f )
290+ # Update the parameter by +10%
291+ new_val = value * 1.1
292+ orig_content2 [ 'track' ][ param ] = type ( value )( new_val )
293+ yaml . safe_dump ( orig_content2 , tmp2 )
290294 try :
291- subprocess .run (cmd2 , stdout = out_file2 , stderr = subprocess .STDOUT , check = True , cwd = test_dir )
292- except subprocess .CalledProcessError :
293- out_file2 .flush ()
295+ # Run sequence mode (to prep files)
296+ pyptv_batch .main (temp_yaml2 , start_frame , end_frame , mode = "sequence" )
297+ # Run tracking mode and capture output
298+ with tempfile .NamedTemporaryFile ('w+' , delete = False , suffix = '.txt' , dir = test_dir ) as out_file2 :
299+ out_path2 = out_file2 .name
300+ cmd2 = [sys .executable , '-m' , 'pyptv.pyptv_batch' , os .path .basename (temp_yaml2 ), str (start_frame ), str (end_frame ), '--mode' , 'tracking' ]
301+ try :
302+ subprocess .run (cmd2 , stdout = out_file2 , stderr = subprocess .STDOUT , check = True , cwd = test_dir )
303+ except subprocess .CalledProcessError :
304+ out_file2 .flush ()
305+ with open (out_path2 , 'r' ) as f :
306+ print (f"\n --- Subprocess output for { param } +10% ---" )
307+ print (f .read ())
308+ continue # Skip this run if it failed
309+ # Parse output
310+ avg_particles2 = avg_links2 = avg_lost2 = None
294311 with open (out_path2 , 'r' ) as f :
295- print (f"\n --- Subprocess output for { param } +10% ---" )
296- print (f .read ())
297- continue # Skip this run if it failed
298- # Parse output
299- avg_particles2 = avg_links2 = avg_lost2 = None
300- with open (out_path2 , 'r' ) as f :
301- for line in f :
302- m = re .search (r"Average over sequence, particles:\s*([\d\.-]+), links:\s*([\d\.-]+), lost:\s*([\d\.-]+)" , line )
303- if m :
304- avg_particles2 = float (m .group (1 ))
305- avg_links2 = float (m .group (2 ))
306- avg_lost2 = float (m .group (3 ))
307- break
308- # Store result
309- perturbed_params = dict (track_params )
310- perturbed_params [param ] = type (value )(new_val )
311- row2 = {** perturbed_params , 'avg_particles' : avg_particles2 , 'avg_links' : avg_links2 , 'avg_lost' : avg_lost2 , 'param_changed' : param , 'change' : 0.1 }
312- results .append (row2 )
313-
314- df = pd .DataFrame (results )
315- print ("\n Tracking run summary (including perturbations):" )
316- print (df )
317- df .to_csv (test_dir / "tracking_run_summary.csv" , index = False )
318-
319- # Find best row: least avg_lost, then most avg_links
320- best = df .sort_values (['avg_lost' , 'avg_links' ], ascending = [True , False ]).iloc [0 ]
321- print ("\n Best tracking result (least lost, most links):" )
322- print (best )
312+ for line in f :
313+ m = re .search (r"Average over sequence, particles:\s*([\d\.-]+), links:\s*([\d\.-]+), lost:\s*([\d\.-]+)" , line )
314+ if m :
315+ avg_particles2 = float (m .group (1 ))
316+ avg_links2 = float (m .group (2 ))
317+ avg_lost2 = float (m .group (3 ))
318+ break
319+ # Store result
320+ perturbed_params = dict (track_params )
321+ perturbed_params [param ] = type (value )(new_val )
322+ row2 = {** perturbed_params , 'avg_particles' : avg_particles2 , 'avg_links' : avg_links2 , 'avg_lost' : avg_lost2 , 'param_changed' : param , 'change' : 0.1 }
323+ results .append (row2 )
324+ finally :
325+ if temp_yaml2 is not None :
326+ Path (temp_yaml2 ).unlink (missing_ok = True )
327+ if out_path2 is not None :
328+ Path (out_path2 ).unlink (missing_ok = True )
329+
330+ df = pd .DataFrame (results )
331+ print ("\n Tracking run summary (including perturbations):" )
332+ print (df )
333+ df .to_csv (summary_path , index = False )
334+
335+ # Find best row: least avg_lost, then most avg_links
336+ best = df .sort_values (['avg_lost' , 'avg_links' ], ascending = [True , False ]).iloc [0 ]
337+ print ("\n Best tracking result (least lost, most links):" )
338+ print (best )
339+ finally :
340+ if temp_yaml is not None :
341+ Path (temp_yaml ).unlink (missing_ok = True )
342+ if out_path is not None :
343+ Path (out_path ).unlink (missing_ok = True )
344+ summary_path .unlink (missing_ok = True )
323345
324346
325347def optimize_tracking_parameters (test_data_dir ):
@@ -332,7 +354,7 @@ def optimize_tracking_parameters(test_data_dir):
332354 import numpy as np
333355 from scipy .optimize import minimize
334356
335- test_dir = test_data_dir
357+ test_dir = test_data_dir / "test_cavity"
336358 orig_yaml = test_dir / "parameters_Run1.yaml"
337359 start_frame = 10000
338360 end_frame = 10004 # Use only 2 frames for speed
0 commit comments