5555from omc3 .utils .contexts import timeit
5656
5757if TYPE_CHECKING :
58- from collections .abc import Generator
58+ from collections .abc import Generator , Iterable
5959
6060 from generic_parser import DotDict
6161
6262LOGGER = logging_tools .get_logger (__name__ )
6363
6464DEFAULT_CONFIG_FILENAME = "analysis_{time:s}.ini"
65+ DATATYPE_TBT : str = "tbt_data"
6566
6667
6768def hole_in_one_params () -> EntryPointParameters :
@@ -89,7 +90,8 @@ def hole_in_one_entrypoint(opt: DotDict, rest: list[str]) -> None:
8990 Action: ``store_true``
9091
9192 Harpy Kwargs:
92- - **files**: TbT files to analyse
93+ - **files**: TbT files to analyse.
94+ Can also be the TbtData objects directly, if 'tbt_data' is chosen as datatype.
9395
9496 Flags: **--files**
9597 Required: ``True``
@@ -405,58 +407,68 @@ def _run_harpy(harpy_options: DotDict) -> list[Path]:
405407 iotools .create_dirs (harpy_options .outputdir )
406408 with timeit (lambda spanned : LOGGER .info (f"Total time for Harpy: { spanned } " )):
407409 lins = []
408- tbt_datas = [
409- (tbt .read_tbt (file , datatype = harpy_options .tbt_datatype ), file )
410- for file in harpy_options .files
411- ]
410+ tbt_datas = _parse_tbt_data (harpy_options .files , harpy_options .tbt_datatype )
412411 for tbt_data , file in tbt_datas :
413412 lins .extend (
414413 [
415- handler .run_per_bunch (bunch_data , harpy_options , bunch_file )
416- for bunch_data , bunch_file in _add_suffix_and_iter_bunches (
414+ handler .run_per_bunch (bunch_data , harpy_options , name_for_bunch )
415+ for bunch_data , name_for_bunch in _add_suffix_and_iter_bunches (
417416 tbt_data , harpy_options , file
418417 )
419418 ]
420419 )
421420 return lins
422421
423422
423+ def _parse_tbt_data (files : Iterable [Path | str | tbt .TbtData ], tbt_datatype : str
424+ ) -> list [tuple [tbt .TbtData , str ]]:
425+ """Parse the turn-by-turn data reading given files or TbtData objects."""
426+ if tbt_datatype == DATATYPE_TBT :
427+ try :
428+ return [(file , Path (file .meta ["file" ]).name ) for file in files ]
429+ except KeyError as e :
430+ raise KeyError (
431+ "To determine output naming for hole-in-one, "
432+ "the given TbT objects must contain a 'file' entry in their meta-data."
433+ ) from e
434+
435+ return [(tbt .read_tbt (file , datatype = tbt_datatype ), Path (file ).name ) for file in files ]
436+
437+
424438def _add_suffix_and_iter_bunches (
425- tbt_data : tbt .TbtData , options : DotDict , file : Path
426- ) -> Generator [tuple [tbt .TbtData , Path ], None , None ]:
427- """Add suffix to output files and iterate over bunches."""
428- dir_name : Path = file .parent
429- file_name : str = file .name
439+ tbt_data : tbt .TbtData , options : DotDict , file_name : str
440+ ) -> Generator [tuple [tbt .TbtData , str ], None , None ]:
441+ """Add the additional suffix (if given by user) to output files and
442+ split the TbT data into bunches to analyse them individually."""
430443 suffix : str = options .suffix or ""
431444
432445 # Single bunch
433446 if tbt_data .nbunches == 1 :
434- if suffix :
435- file = dir_name / f"{ file_name } { suffix } "
436- yield tbt_data , file
447+ file_name_out = f"{ file_name } { suffix } "
448+ yield tbt_data , file_name_out
437449 return
438450
439451 # Multibunch
440452 if options .bunch_ids is not None :
441453 unknown_bunches = set (options .bunch_ids ) - set (tbt_data .bunch_ids )
442454 if unknown_bunches :
443- LOGGER .warning (f"Bunch IDs { unknown_bunches } not present in multi-bunch file { file } ." )
455+ LOGGER .warning (f"Bunch IDs { unknown_bunches } not present in multi-bunch file { file_name } ." )
444456
445457 for index in range (tbt_data .nbunches ):
446458 bunch_id = tbt_data .bunch_ids [index ]
447459 if options .bunch_ids is not None and bunch_id not in options .bunch_ids :
448460 continue
449461
450462 bunch_id_str = f"_bunchID{ bunch_id } "
451- file = dir_name / f"{ file_name } { bunch_id_str } { suffix } "
463+ file_name_out = f"{ file_name } { bunch_id_str } { suffix } "
452464 yield (
453465 tbt .TbtData (
454466 matrices = [tbt_data .matrices [index ]],
455467 nturns = tbt_data .nturns ,
456468 bunch_ids = [bunch_id ],
457469 meta = tbt_data .meta ,
458470 ),
459- file ,
471+ file_name_out ,
460472 )
461473
462474
@@ -499,18 +511,34 @@ def _harpy_entrypoint(params: list[str]) -> tuple[DotDict, list[str]]:
499511 raise AttributeError (
500512 "The magnet order for resonance lines calculation should be between 2 and 8 (inclusive)."
501513 )
502- options .files = [Path (file ) for file in options .files ]
503514 options .outputdir = Path (options .outputdir )
504515 return options , rest
505516
506517
507518def harpy_params () -> EntryPointParameters :
508519 """Create the entry point parameters for harpy."""
520+ # fmt: off
509521 params = EntryPointParameters ()
510- params .add_parameter (name = "files" , required = True , nargs = "+" , help = "TbT files to analyse" )
511- params .add_parameter (name = "outputdir" , required = True , help = "Output directory." )
512- params .add_parameter (name = "suffix" , type = str , help = "User-defined suffix for output filenames." )
513- params .add_parameter (name = "model" , help = "Model for BPM locations" )
522+ params .add_parameter (
523+ name = "files" ,
524+ required = True ,
525+ nargs = "+" ,
526+ help = "TbT files to analyse. Can also be the TbtData objects directly, if 'tbt_data' is chosen as datatype."
527+ )
528+ params .add_parameter (
529+ name = "outputdir" ,
530+ required = True ,
531+ help = "Output directory."
532+ )
533+ params .add_parameter (
534+ name = "suffix" ,
535+ type = str ,
536+ help = "User-defined suffix for output filenames."
537+ )
538+ params .add_parameter (
539+ name = "model" ,
540+ help = "Model for BPM locations"
541+ )
514542 params .add_parameter (
515543 name = "unit" ,
516544 type = str ,
@@ -541,7 +569,7 @@ def harpy_params() -> EntryPointParameters:
541569 params .add_parameter (
542570 name = "tbt_datatype" ,
543571 default = HARPY_DEFAULTS ["tbt_datatype" ],
544- choices = list (tbt .io .TBT_MODULES .keys ()),
572+ choices = list (tbt .io .TBT_MODULES .keys ()) + [ DATATYPE_TBT ] ,
545573 help = "Choose the datatype from which to import. " ,
546574 )
547575
@@ -584,7 +612,11 @@ def harpy_params() -> EntryPointParameters:
584612 "and renormalisation in iterative SVD cleaning of dominant BPMs."
585613 " This is also equal to maximal number of BPMs removed per SVD mode." ,
586614 )
587- params .add_parameter (name = "bad_bpms" , nargs = "*" , help = "Bad BPMs to clean." )
615+ params .add_parameter (
616+ name = "bad_bpms" ,
617+ nargs = "*" ,
618+ help = "Bad BPMs to clean."
619+ )
588620 params .add_parameter (
589621 name = "wrong_polarity_bpms" ,
590622 nargs = "*" ,
@@ -691,6 +723,7 @@ def harpy_params() -> EntryPointParameters:
691723 default = HARPY_DEFAULTS ["resonances" ],
692724 help = "Maximum magnet order of resonance lines to calculate." ,
693725 )
726+ # fmt: on
694727 return params
695728
696729
@@ -708,11 +741,23 @@ def _optics_entrypoint(params: list[str]) -> tuple[DotDict, list[str]]:
708741
709742def optics_params () -> EntryPointParameters :
710743 """Create the entry point parameters for optics."""
744+ # fmt: off
711745 params = EntryPointParameters ()
712- params .add_parameter (name = "files" , required = True , nargs = "+" , help = "Files for analysis" )
713- params .add_parameter (name = "outputdir" , required = True , help = "Output directory" )
714746 params .add_parameter (
715- name = "calibrationdir" , type = str , help = "Path to calibration files directory."
747+ name = "files" ,
748+ required = True ,
749+ nargs = "+" ,
750+ help = "Files for analysis"
751+ )
752+ params .add_parameter (
753+ name = "outputdir" ,
754+ required = True ,
755+ help = "Output directory"
756+ )
757+ params .add_parameter (
758+ name = "calibrationdir" ,
759+ type = str ,
760+ help = "Path to calibration files directory."
716761 )
717762 params .add_parameter (
718763 name = "coupling_method" ,
@@ -761,7 +806,9 @@ def optics_params() -> EntryPointParameters:
761806 help = "Use 3 BPM method in beta from phase" ,
762807 )
763808 params .add_parameter (
764- name = "only_coupling" , action = "store_true" , help = "Calculate only coupling. "
809+ name = "only_coupling" ,
810+ action = "store_true" ,
811+ help = "Calculate only coupling. "
765812 )
766813 params .add_parameter (
767814 name = "compensation" ,
@@ -797,6 +844,7 @@ def optics_params() -> EntryPointParameters:
797844 help = "Filter files to analyse by this value (in analysis for tune, phase, rdt and crdt). "
798845 "Use `None` for no filtering" ,
799846 )
847+ # fmt: on
800848 return params
801849
802850
0 commit comments