From 857d11b6b346a6956c9e0ea6288a8dd509fce179 Mon Sep 17 00:00:00 2001 From: FusRoman <46221629+FusRoman@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:42:32 +0100 Subject: [PATCH] Issue/155/orbfit timeout (#156) * addtimeout option and orbfit logs * incorporate verbose options from command line to orbfit * pep8 * pep8 * fix isinstance call --- bin/fink_fat_cli.py | 6 +- fink_fat/__init__.py | 2 +- fink_fat/orbit_fitting/orbfit_local.py | 137 ++++++++++++++----- fink_fat/test/call_orbfit/K23Nb8K.inp | 1 + fink_fat/test/call_orbfit/K23Nb8K.oop | 37 +++++ fink_fat/test/call_orbfit/mpcobs/K23Nb8K.obs | 6 + 6 files changed, 154 insertions(+), 35 deletions(-) create mode 100644 fink_fat/test/call_orbfit/K23Nb8K.inp create mode 100644 fink_fat/test/call_orbfit/K23Nb8K.oop create mode 100644 fink_fat/test/call_orbfit/mpcobs/K23Nb8K.obs diff --git a/bin/fink_fat_cli.py b/bin/fink_fat_cli.py index 5ad83ed5..507cfabe 100755 --- a/bin/fink_fat_cli.py +++ b/bin/fink_fat_cli.py @@ -289,7 +289,8 @@ def fink_fat_main(arguments): int(config["SOLVE_ORBIT_PARAMS"]["n_triplets"]), int(config["SOLVE_ORBIT_PARAMS"]["noise_ntrials"]), prop_epoch=float(prop_epoch) if prop_epoch != "None" else None, - verbose=int(config["SOLVE_ORBIT_PARAMS"]["orbfit_verbose"]) + verbose_orbfit=int(config["SOLVE_ORBIT_PARAMS"]["orbfit_verbose"]), + verbose=arguments["--verbose"] ).drop("provisional designation", axis=1) orbfit_time = t.time() - t_before @@ -1168,7 +1169,8 @@ def fink_fat_main(arguments): int(config["SOLVE_ORBIT_PARAMS"]["n_triplets"]), int(config["SOLVE_ORBIT_PARAMS"]["noise_ntrials"]), prop_epoch=float(prop_epoch) if prop_epoch != "None" else None, - verbose=int(config["SOLVE_ORBIT_PARAMS"]["orbfit_verbose"]) + verbose_orbfit=int(config["SOLVE_ORBIT_PARAMS"]["orbfit_verbose"]), + verbose=arguments["--verbose"] ).drop("provisional designation", axis=1) orbfit_time = t.time() - t_before diff --git a/fink_fat/__init__.py b/fink_fat/__init__.py index d871f88d..16bd5930 100644 --- a/fink_fat/__init__.py +++ b/fink_fat/__init__.py @@ -12,4 +12,4 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "0.15.5" +__version__ = "0.15.6" diff --git a/fink_fat/orbit_fitting/orbfit_local.py b/fink_fat/orbit_fitting/orbfit_local.py index a292356c..ee55ead4 100644 --- a/fink_fat/orbit_fitting/orbfit_local.py +++ b/fink_fat/orbit_fitting/orbfit_local.py @@ -1,4 +1,3 @@ -import signal import numpy as np import pandas as pd from shutil import rmtree @@ -11,6 +10,8 @@ import traceback import logging +from typing import Union +import pathlib orbfit_column_name = [ "trajectory_id", @@ -32,7 +33,7 @@ ] -def call_orbitfit(ram_dir, first_designation, second_designation=None): +def call_orbitfit(ram_dir, first_designation, second_designation=None, verbose=False): """ Call the OrbFit software in a subprocess. Kill it after 2 second if OrbFit are blocked. @@ -56,17 +57,23 @@ def call_orbitfit(ram_dir, first_designation, second_designation=None): >>> os.path.exists("fink_fat/test/call_orbfit/K21E00A.oel") True - >>> os.remove("fink_fat/test/call_orbfit/K21E00A.odc") - >>> os.remove("fink_fat/test/call_orbfit/K21E00A.olg") - >>> os.remove("fink_fat/test/call_orbfit/K21E00A.pro") - >>> os.remove("fink_fat/test/call_orbfit/mpcobs/K21E00A.rwo") - >>> os.remove("fink_fat/test/call_orbfit/K21E00A.oel") - >>> call_orbitfit("fink_fat/test/call_orbfit/", "K19V01E", "K20V02K") >>> os.path.exists("fink_fat/test/call_orbfit/K19V01E_K20V02K.oel") True + >>> call_orbitfit("fink_fat/test/call_orbfit/", "K23Nb8K") + + >>> call_orbitfit("fink_fat/test/call_orbfit/", "K21E00A", verbose=True) + >>> call_orbitfit("fink_fat/test/call_orbfit/", "K19V01E", "K20V02K", verbose=True) + + >>> os.path.exists("fink_fat/test/K23Nb8K.log") + True + >>> os.path.exists("fink_fat/test/K21E00A.log") + True + >>> os.path.exists("fink_fat/test/K19V01E_K20V02K.log") + True + >>> os.remove("fink_fat/test/call_orbfit/K19V01E_K20V02K.odc") >>> os.remove("fink_fat/test/call_orbfit/K19V01E_K20V02K.olg") >>> os.remove("fink_fat/test/call_orbfit/K19V01E_K20V02K.pro") @@ -74,18 +81,31 @@ def call_orbitfit(ram_dir, first_designation, second_designation=None): >>> os.remove("fink_fat/test/call_orbfit/mpcobs/K20V02K.rwo") >>> os.remove("fink_fat/test/call_orbfit/K19V01E_K20V02K.oel") >>> os.remove("fink_fat/test/call_orbfit/K19V01E_K20V02K.err") + + >>> os.remove("fink_fat/test/call_orbfit/K21E00A.odc") + >>> os.remove("fink_fat/test/call_orbfit/K21E00A.olg") + >>> os.remove("fink_fat/test/call_orbfit/K21E00A.pro") + >>> os.remove("fink_fat/test/call_orbfit/K21E00A.clo") + >>> os.remove("fink_fat/test/call_orbfit/K21E00A.err") + >>> os.remove("fink_fat/test/call_orbfit/mpcobs/K21E00A.rwo") + >>> os.remove("fink_fat/test/call_orbfit/K21E00A.oel") + + >>> os.remove("fink_fat/test/call_orbfit/K23Nb8K.odc") + >>> os.remove("fink_fat/test/call_orbfit/K23Nb8K.olg") + >>> os.remove("fink_fat/test/call_orbfit/K23Nb8K.pro") + >>> os.remove("fink_fat/test/call_orbfit/K23Nb8K.clo") + >>> os.remove("fink_fat/test/call_orbfit/K23Nb8K.err") + >>> os.remove("fink_fat/test/call_orbfit/mpcobs/K23Nb8K.rwo") + >>> os.remove("fink_fat/test/call_orbfit/K23Nb8K.oel") + + >>> os.remove("fink_fat/test/K23Nb8K.log") + >>> os.remove("fink_fat/test/K21E00A.log") + >>> os.remove("fink_fat/test/K19V01E_K20V02K.log") """ orbitfit_path = os.path.join(os.environ["ORBFIT_HOME"], "bin", "") if second_designation is None: - command = ( - orbitfit_path - + "orbfit.x < " - + ram_dir - + first_designation - + ".inp " - + ">/dev/null 2>&1" - ) + command = orbitfit_path + "orbfit.x < " + ram_dir + first_designation + ".inp " else: command = ( orbitfit_path @@ -95,22 +115,73 @@ def call_orbitfit(ram_dir, first_designation, second_designation=None): + "_" + second_designation + ".inp " - + ">/dev/null 2>&1" ) - with subprocess.Popen( - command, shell=True, stdout=subprocess.DEVNULL, preexec_fn=os.setsid - ) as process: + def generate_logs( + run_exception: Union[subprocess.CalledProcessError, subprocess.TimeoutExpired] + ): + err_type = ( + f"timeout exceeded: {run_exception.timeout} seconds" + if isinstance(run_exception, subprocess.TimeoutExpired) + else f"return code: {run_exception.returncode}" + ) + str_err = f""" +--- ORBFIT ERROR --- +command: {run_exception.cmd} +{err_type} + +stdout: +{run_exception.stdout.decode("utf-8")} + + + +stderr: +{run_exception.stderr.decode("utf-8")} + + + +subprocess output: +{run_exception.output.decode("utf-8")} +------------------------------------- +""" + return str_err + + def write_logs(str_log, first_designation, second_designation): + ram_path_parent = pathlib.Path(ram_dir).parent.absolute() + log_file_name = first_designation if second_designation is None else first_designation + "_" + second_designation + with open(os.path.join(ram_path_parent, f"{log_file_name}.log"), "w") as f: + f.write(str_log) + + try: + completed_process = subprocess.run( + command, shell=True, capture_output=True, timeout=5 + ) try: - output = process.communicate(timeout=5)[0] - return output - except subprocess.TimeoutExpired: # pragma: no cover - os.killpg(process.pid, signal.SIGINT) # send signal to the process group - output = process.communicate()[0] - return output + completed_process.check_returncode() + if verbose: + str_out = f""" +--- ORBFIT OUTPUT --- +args: {completed_process.args} +returncode: {completed_process.returncode} + +stdout: +{completed_process.stdout.decode("utf-8")} + + + +stderr: +{completed_process.stderr.decode("utf-8")} +------------------------------------- +""" + write_logs(str_out, first_designation, second_designation) + + except subprocess.CalledProcessError as e: + write_logs(generate_logs(e), first_designation, second_designation) + except subprocess.TimeoutExpired as te: + write_logs(generate_logs(te), first_designation, second_designation) -def get_orbit_param(ram_dir, df, n_triplets, noise_ntrials, prop_epoch=None, verbose=1): +def get_orbit_param(ram_dir, df, n_triplets, noise_ntrials, prop_epoch=None, verbose_orbfit=1, verbose=None): """ Compute the orbital elements of one trajectory. @@ -180,7 +251,7 @@ def get_orbit_param(ram_dir, df, n_triplets, noise_ntrials, prop_epoch=None, ver prop_epoch="JD {} UTC".format(df_one_traj["jd"].values[-1]), n_triplets=n_triplets, noise_ntrials=noise_ntrials, - verbose=verbose, + verbose=verbose_orbfit, ) else: of.write_oop( @@ -189,11 +260,11 @@ def get_orbit_param(ram_dir, df, n_triplets, noise_ntrials, prop_epoch=None, ver prop_epoch="JD {} UTC".format(prop_epoch), n_triplets=n_triplets, noise_ntrials=noise_ntrials, - verbose=verbose, + verbose=verbose_orbfit, ) try: - call_orbitfit(ram_dir, prov_desig) + call_orbitfit(ram_dir, prov_desig, verbose=verbose) except Exception as e: # pragma: no cover print(e) print("ERROR CALLING ORBFIT: {}".format(prov_desig)) @@ -271,7 +342,8 @@ def compute_df_orbit_param( n_triplets=10, noise_ntrials=10, prop_epoch=None, - verbose=1, + verbose_orbfit=1, + verbose=None ): """ Compute the orbital elements of a set of trajectories. Computation are done in parallel. @@ -339,7 +411,8 @@ def compute_df_orbit_param( n_triplets, noise_ntrials, prop_epoch, - verbose, + verbose_orbfit, + verbose ) for tr_chunk, chunk_dir in zip(trajectory_id_chunks, chunk_ramdir) if len(tr_chunk) > 0 diff --git a/fink_fat/test/call_orbfit/K23Nb8K.inp b/fink_fat/test/call_orbfit/K23Nb8K.inp new file mode 100644 index 00000000..8f85ed1c --- /dev/null +++ b/fink_fat/test/call_orbfit/K23Nb8K.inp @@ -0,0 +1 @@ +fink_fat/test/call_orbfit/K23Nb8K \ No newline at end of file diff --git a/fink_fat/test/call_orbfit/K23Nb8K.oop b/fink_fat/test/call_orbfit/K23Nb8K.oop new file mode 100644 index 00000000..96509676 --- /dev/null +++ b/fink_fat/test/call_orbfit/K23Nb8K.oop @@ -0,0 +1,37 @@ +output. + .elements = 'KEP' + .epoch = JD 2460150.946169 UTC +init_orbdet. + .verbose = 2 + .n_triplets = 30 + .noise.ntrials = 20 +operations. + .init_orbdet = 2 + .diffcor = 1 + .ident = 1 + .ephem = 0 +error_model. + .name='fcct14' +IERS. + .extrapolation = .T. +reject. + .rejopp = .FALSE. +propag. + .iast = 17 + .npoint = 600 + .dmea = 0.2d0 + .dter = 0.05d0 + .filbe=fink_fat/test/call_orbfit/AST17 + .irel=2 + +lsfit. + .nit_max = 1 + .nitg_max = 1 + .conv_cntr = 1.0 + .div_cntr = 1.0 + +output_files. + .elem = fink_fat/test/call_orbfit/K23Nb8K.oel +object1. + .obs_dir = fink_fat/test/call_orbfit/mpcobs + .name = K23Nb8K \ No newline at end of file diff --git a/fink_fat/test/call_orbfit/mpcobs/K23Nb8K.obs b/fink_fat/test/call_orbfit/mpcobs/K23Nb8K.obs new file mode 100644 index 00000000..de4fadf2 --- /dev/null +++ b/fink_fat/test/call_orbfit/mpcobs/K23Nb8K.obs @@ -0,0 +1,6 @@ + K23Nb8K C2023 07 11.36610 00 50 04.56 +01 39 00.42 18.3 r I41 + K23Nb8K C2023 07 13.37210 00 51 37.51 +01 48 00.46 19.8 g I41 + K23Nb8K C2023 07 13.39971 00 51 39.84 +01 48 14.09 19.1 r I41 + K23Nb8K C2023 07 19.33909 00 57 07.32 +02 19 06.73 19.5 r I41 + K23Nb8K C2023 07 22.35769 00 58 55.40 +02 25 37.45 20.0 g I41 + K23Nb8K C2023 07 25.38530 00 59 48.01 +02 27 14.30 20.2 r I41 \ No newline at end of file