-
Notifications
You must be signed in to change notification settings - Fork 0
Pclean #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Pclean #14
Changes from 2 commits
f50a992
bba8a9e
d30c681
bef1b69
7fe8f80
ff549d8
a77948d
9e8006b
c2fc77f
75bb79f
21816c5
42a23b7
9431f9e
333dc0b
a989bc2
03edbbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,188 @@ | ||||||||||||||
| import pickle | ||||||||||||||
| import shutil | ||||||||||||||
| import subprocess | ||||||||||||||
| import sys | ||||||||||||||
| import tempfile | ||||||||||||||
| import textwrap | ||||||||||||||
| from pathlib import Path | ||||||||||||||
| from typing import Any, Dict, Optional, Union | ||||||||||||||
|
|
||||||||||||||
| import casaconfig.config | ||||||||||||||
| import casatasks | ||||||||||||||
|
|
||||||||||||||
| from pipeline.infrastructure import logging | ||||||||||||||
|
|
||||||||||||||
| LOG = logging.get_logger(__name__) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def find_executable(start_dir: Optional[str] = None) -> Dict[str, Optional[str]]: | ||||||||||||||
| """Search upward from start_dir for MPI-related executables. | ||||||||||||||
|
|
||||||||||||||
| The function looks for 'bin/mpirun', 'bin/mpicasa' and 'bin/casa' in the | ||||||||||||||
| current directory and each parent until the filesystem root is reached. | ||||||||||||||
|
|
||||||||||||||
| Args: | ||||||||||||||
| start_dir: Directory to start searching from. If None, use cwd. | ||||||||||||||
|
|
||||||||||||||
| Returns: | ||||||||||||||
| Mapping from executable name to absolute path or None if not found. | ||||||||||||||
| """ | ||||||||||||||
|
||||||||||||||
| search_patterns = ['mpirun', 'mpicasa', 'casa'] | ||||||||||||||
| exe_dict: Dict[str, Optional[str]] = dict.fromkeys(search_patterns) | ||||||||||||||
| current = Path(start_dir or Path.cwd()).resolve() | ||||||||||||||
|
|
||||||||||||||
| for pattern in search_patterns: | ||||||||||||||
| cur = current | ||||||||||||||
| found: Optional[str] = None | ||||||||||||||
| while True: | ||||||||||||||
| candidate = cur / 'bin' / pattern | ||||||||||||||
| if candidate.is_file(): | ||||||||||||||
| found = str(candidate) | ||||||||||||||
| break | ||||||||||||||
| parent = cur.parent | ||||||||||||||
| if cur == parent: | ||||||||||||||
| break | ||||||||||||||
| cur = parent | ||||||||||||||
| exe_dict[pattern] = found | ||||||||||||||
|
|
||||||||||||||
| return exe_dict | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| def pclean( | ||||||||||||||
| *args: Any, | ||||||||||||||
| parallel: Union[bool, Dict[str, int]] = False, | ||||||||||||||
| **kwargs: Any, | ||||||||||||||
| ) -> Any: | ||||||||||||||
| """Execute tclean and return the result. | ||||||||||||||
|
|
||||||||||||||
| When `parallel` is False, tclean is invoked directly in-process. When | ||||||||||||||
| `parallel` is True or a dict, the function serializes arguments to a | ||||||||||||||
| temporary file and runs a Python subprocess (optionally under MPI). | ||||||||||||||
| The subprocess writes a pickled result to a second temporary file which | ||||||||||||||
| is read back here. This allows tclean to run in parallel mode even when the | ||||||||||||||
| main process is not running under MPI. | ||||||||||||||
|
|
||||||||||||||
| Args: | ||||||||||||||
| *args: Positional arguments forwarded to tclean. | ||||||||||||||
| parallel: False to run serially, True or dict to run via subprocess/MPI. | ||||||||||||||
| **kwargs: Keyword arguments forwarded to tclean. | ||||||||||||||
|
|
||||||||||||||
| Returns: | ||||||||||||||
| The (picklable) return value from tclean. | ||||||||||||||
|
|
||||||||||||||
| Raises: | ||||||||||||||
| RuntimeError: If the subprocess reports an error or the call fails. | ||||||||||||||
| """ | ||||||||||||||
| if parallel is False: | ||||||||||||||
| return casatasks.tclean(*args, parallel=parallel, **kwargs) | ||||||||||||||
|
|
||||||||||||||
| # prepare temporary files | ||||||||||||||
| with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix='.pkl') as f_in: | ||||||||||||||
| input_file = f_in.name | ||||||||||||||
| pickle.dump({'args': args, 'parallel': True, 'kwargs': kwargs}, f_in) | ||||||||||||||
r-xue marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
|
||||||||||||||
| with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix='.pkl') as f_out: | ||||||||||||||
| output_file = f_out.name | ||||||||||||||
|
Comment on lines
+115
to
+121
|
||||||||||||||
|
|
||||||||||||||
| try: | ||||||||||||||
| # Inline script to run inside the subprocess | ||||||||||||||
| script = textwrap.dedent( | ||||||||||||||
| f"""\ | ||||||||||||||
|
|
||||||||||||||
r-xue marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||
| import pickle | ||||||||||||||
| import sys | ||||||||||||||
|
|
||||||||||||||
| # casaconfig should be imported/updated first before importing casatasks | ||||||||||||||
| # the statement order matters here | ||||||||||||||
| import casaconfig.config | ||||||||||||||
| casaconfig.config.logfile={casatasks.casalog.logfile()!r} | ||||||||||||||
| casaconfig.config.log2term = {bool(getattr(casaconfig.config, 'log2term', False))} | ||||||||||||||
|
|
||||||||||||||
| import casampi.private.start_mpi # nescary if not executed via casashell | ||||||||||||||
r-xue marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||
| import casatasks | ||||||||||||||
| from casatasks import tclean | ||||||||||||||
| casatasks.casalog.showconsole(onconsole={bool(getattr(casaconfig.config, 'log2term', False))}) | ||||||||||||||
|
|
||||||||||||||
| # from casampi.MPIEnvironment import MPIEnvironment | ||||||||||||||
| # from casampi.MPICommandClient import MPICommandClient | ||||||||||||||
| # __client = MPICommandClient() | ||||||||||||||
| # __client.set_log_mode('redirect') | ||||||||||||||
| # __client.start_services() | ||||||||||||||
r-xue marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
|
||||||||||||||
| # from casampi.MPIEnvironment import MPIEnvironment | |
| # from casampi.MPICommandClient import MPICommandClient | |
| # __client = MPICommandClient() | |
| # __client.set_log_mode('redirect') | |
| # __client.start_services() | |
r-xue marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting capture_output=False means stdout and stderr won't be captured, so lines 163-164 and 167-168 will always log None. Either set capture_output=True to capture the output for logging, or remove the logging statements for stdout/stderr since they will be empty.
| completed = subprocess.run(call_args, check=True, shell=False, capture_output=False, text=True) | |
| completed = subprocess.run(call_args, check=True, shell=False, capture_output=True, text=True) |
Outdated
Copilot
AI
Dec 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the subprocess crashes or is killed before it can write to the output file, attempting to open and read the output file will raise a FileNotFoundError or EOFError (for an empty pickle file). Consider checking if the output file exists and has content before attempting to unpickle it, or wrap this in a try-except to provide a clearer error message.
Uh oh!
There was an error while loading. Please reload this page.