From 49cbfe5cbf1e267da7a67039fb4ce0fa4fb094fe Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Thu, 26 Feb 2026 03:25:49 +0530 Subject: [PATCH 1/8] Fix: translate 'total' to '_GLOBAL' for --event argument (#18) --- preciceprofiling/analyze.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/preciceprofiling/analyze.py b/preciceprofiling/analyze.py index 8dd165b..9b916a6 100644 --- a/preciceprofiling/analyze.py +++ b/preciceprofiling/analyze.py @@ -117,6 +117,10 @@ def runAnalyze(ns): def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): + # Translate display name "total" back to internal name "_GLOBAL" + if event == "total": + event = "_GLOBAL" + run = Run(profilingfile) participants = run.participants() From 7ae4f2693cfa8c4774c60138db9f7882b9a154e5 Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Thu, 26 Feb 2026 03:31:11 +0530 Subject: [PATCH 2/8] Fix: make participant argument optional, analyze all if omitted (#7) --- preciceprofiling/analyze.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/preciceprofiling/analyze.py b/preciceprofiling/analyze.py index 9b916a6..1eeae0d 100644 --- a/preciceprofiling/analyze.py +++ b/preciceprofiling/analyze.py @@ -11,7 +11,7 @@ def makeAnalyzeParser(add_help: bool = True): Parallel solvers show events of the primary rank next to the secondary ranks spending the least and most time in advance of preCICE. """ analyze = argparse.ArgumentParser(description=analyze_help, add_help=add_help) - analyze.add_argument("participant", type=str, help="The participant to analyze") + analyze.add_argument("participant", type=str, nargs="?", default=None, help="The participant to analyze. If omitted, all participants are analyzed.") addInputArgument(analyze) addUnitArgument(analyze) analyze.add_argument( @@ -123,11 +123,18 @@ def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): run = Run(profilingfile) - participants = run.participants() + all_participants = run.participants() + + if participant is None: + for p in all_participants: + print(f"\n=== Participant: {p} ===") + analyzeCommand(profilingfile, p, event, outfile, unit) + return 0 + assert ( - participant in participants + participant in all_participants ), f"Given participant {participant} doesn't exist. Known: " + ", ".join( - participants + all_participants ) df = run.toDataFrame(participant=participant) From b974ea3b21710d832ee1fd1a06f140f58567a2fb Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Sat, 28 Feb 2026 00:35:22 +0530 Subject: [PATCH 3/8] Apply black formatting --- preciceprofiling/analyze.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/preciceprofiling/analyze.py b/preciceprofiling/analyze.py index 1eeae0d..e780d97 100644 --- a/preciceprofiling/analyze.py +++ b/preciceprofiling/analyze.py @@ -11,7 +11,13 @@ def makeAnalyzeParser(add_help: bool = True): Parallel solvers show events of the primary rank next to the secondary ranks spending the least and most time in advance of preCICE. """ analyze = argparse.ArgumentParser(description=analyze_help, add_help=add_help) - analyze.add_argument("participant", type=str, nargs="?", default=None, help="The participant to analyze. If omitted, all participants are analyzed.") + analyze.add_argument( + "participant", + type=str, + nargs="?", + default=None, + help="The participant to analyze. If omitted, all participants are analyzed.", + ) addInputArgument(analyze) addUnitArgument(analyze) analyze.add_argument( From 38603b8e87c1aa5bcfdceb242123593284583878 Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Sun, 8 Mar 2026 16:48:12 +0530 Subject: [PATCH 4/8] Fix: error on --output without participant, pass None instead of outfile in loop --- preciceprofiling/analyze.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/preciceprofiling/analyze.py b/preciceprofiling/analyze.py index e780d97..2ba1cbd 100644 --- a/preciceprofiling/analyze.py +++ b/preciceprofiling/analyze.py @@ -132,9 +132,16 @@ def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): all_participants = run.participants() if participant is None: + if outfile is not None: + print( + "Error: --output requires a specific participant. " + "Use `analyze --output `.", + file=sys.stderr, + ) + return 1 for p in all_participants: print(f"\n=== Participant: {p} ===") - analyzeCommand(profilingfile, p, event, outfile, unit) + analyzeCommand(profilingfile, p, event, None, unit) return 0 assert ( From a97b363e653817b67e0e6f6b9603a095c9a9629d Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Sun, 8 Mar 2026 16:54:21 +0530 Subject: [PATCH 5/8] Test: add tests for optional participant and outfile error --- tests/test_examples.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_examples.py b/tests/test_examples.py index e25a50b..5a2367b 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -86,3 +86,24 @@ def test_truncated_case(case: pathlib.Path, useDir: bool): cwd = pathlib.Path(tmp) truncate_case_files(case, cwd) run_case(cwd, cwd, useDir) + + +def test_analyze_all_participants(): + """analyze with no participant should analyze all participants""" + case = pathlib.Path(__file__).parent / "cases" / "fiveparticipants-json" + with tempfile.TemporaryDirectory() as tmp: + cwd = pathlib.Path(tmp) + profiling = cwd / "profiling.db" + assert mergeCommand([case], profiling, True) == 0 + assert analyzeCommand(profiling, None, "advance", None, "us") == 0 + + +def test_analyze_no_participant_with_outfile_errors(): + """analyze with no participant but --output should return error""" + case = pathlib.Path(__file__).parent / "cases" / "fiveparticipants-json" + with tempfile.TemporaryDirectory() as tmp: + cwd = pathlib.Path(tmp) + profiling = cwd / "profiling.db" + assert mergeCommand([case], profiling, True) == 0 + result = analyzeCommand(profiling, None, "advance", cwd / "out.csv", "us") + assert result == 1 From 967713e4a58caaf38ae480538fbf392e30b8f156 Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Sun, 8 Mar 2026 16:57:30 +0530 Subject: [PATCH 6/8] Refactor: extract computeAnalysis to eliminate recursive analyzeCommand call --- preciceprofiling/analyze.py | 63 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/preciceprofiling/analyze.py b/preciceprofiling/analyze.py index 2ba1cbd..9028f93 100644 --- a/preciceprofiling/analyze.py +++ b/preciceprofiling/analyze.py @@ -122,40 +122,12 @@ def runAnalyze(ns): ) -def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): - # Translate display name "total" back to internal name "_GLOBAL" - if event == "total": - event = "_GLOBAL" - - run = Run(profilingfile) - - all_participants = run.participants() - - if participant is None: - if outfile is not None: - print( - "Error: --output requires a specific participant. " - "Use `analyze --output `.", - file=sys.stderr, - ) - return 1 - for p in all_participants: - print(f"\n=== Participant: {p} ===") - analyzeCommand(profilingfile, p, event, None, unit) - return 0 - - assert ( - participant in all_participants - ), f"Given participant {participant} doesn't exist. Known: " + ", ".join( - all_participants - ) - +def computeAnalysis(run, participant, event, unit="us"): + """Compute and print the analysis DataFrame for a single participant.""" df = run.toDataFrame(participant=participant) print(f"Output timing are in {unit}.") - # Filter by participant - # Convert duration to requested unit dur_factor = 1000 * ns_to_unit_factor(unit) df = ( df.filter(pl.col("participant") == participant) @@ -234,6 +206,37 @@ def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): ) printWide(joined) + return joined + + +def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): + # Translate display name "total" back to internal name "_GLOBAL" + if event == "total": + event = "_GLOBAL" + + run = Run(profilingfile) + all_participants = run.participants() + + if participant is None: + if outfile is not None: + print( + "Error: --output requires a specific participant. " + "Use `analyze --output `.", + file=sys.stderr, + ) + return 1 + for p in all_participants: + print(f"\n=== Participant: {p} ===") + computeAnalysis(run, p, event, unit) + return 0 + + assert ( + participant in all_participants + ), f"Given participant {participant} doesn't exist. Known: " + ", ".join( + all_participants + ) + + joined = computeAnalysis(run, participant, event, unit) if outfile: print(f"Writing to {outfile}") From 0e293f4b4cec19bb7d64fb592dd8c927f5e804df Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Sun, 8 Mar 2026 21:28:55 +0530 Subject: [PATCH 7/8] Refactor: move print and assertion per reviewer feedback --- preciceprofiling/analyze.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/preciceprofiling/analyze.py b/preciceprofiling/analyze.py index 9028f93..6bd9b0b 100644 --- a/preciceprofiling/analyze.py +++ b/preciceprofiling/analyze.py @@ -123,11 +123,9 @@ def runAnalyze(ns): def computeAnalysis(run, participant, event, unit="us"): - """Compute and print the analysis DataFrame for a single participant.""" + """Compute the analysis DataFrame for a single participant.""" df = run.toDataFrame(participant=participant) - print(f"Output timing are in {unit}.") - dur_factor = 1000 * ns_to_unit_factor(unit) df = ( df.filter(pl.col("participant") == participant) @@ -205,7 +203,6 @@ def computeAnalysis(run, participant, event, unit="us"): .collect() ) - printWide(joined) return joined @@ -217,6 +214,12 @@ def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): run = Run(profilingfile) all_participants = run.participants() + assert participant is None or participant in all_participants, ( + f"Given participant {participant} doesn't exist. Known: " + ", ".join(all_participants) + ) + + print(f"Output timings are in {unit}.") + if participant is None: if outfile is not None: print( @@ -227,16 +230,11 @@ def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): return 1 for p in all_participants: print(f"\n=== Participant: {p} ===") - computeAnalysis(run, p, event, unit) + printWide(computeAnalysis(run, p, event, unit)) return 0 - assert ( - participant in all_participants - ), f"Given participant {participant} doesn't exist. Known: " + ", ".join( - all_participants - ) - joined = computeAnalysis(run, participant, event, unit) + printWide(joined) if outfile: print(f"Writing to {outfile}") From cab3ca2d791562e03cfd5ee713780a420e317458 Mon Sep 17 00:00:00 2001 From: AdityaGupta716 Date: Sun, 8 Mar 2026 21:30:03 +0530 Subject: [PATCH 8/8] Apply black formatting --- preciceprofiling/analyze.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/preciceprofiling/analyze.py b/preciceprofiling/analyze.py index 6bd9b0b..cec3d5f 100644 --- a/preciceprofiling/analyze.py +++ b/preciceprofiling/analyze.py @@ -214,8 +214,10 @@ def analyzeCommand(profilingfile, participant, event, outfile=None, unit="us"): run = Run(profilingfile) all_participants = run.participants() - assert participant is None or participant in all_participants, ( - f"Given participant {participant} doesn't exist. Known: " + ", ".join(all_participants) + assert ( + participant is None or participant in all_participants + ), f"Given participant {participant} doesn't exist. Known: " + ", ".join( + all_participants ) print(f"Output timings are in {unit}.")