|
33 | 33 | from astropy.time import Time
|
34 | 34 | from rubin_scheduler.scheduler import sim_runner
|
35 | 35 | from rubin_scheduler.scheduler.utils import SchemaConverter
|
| 36 | +from rubin_scheduler.site_models.almanac import Almanac |
| 37 | + |
| 38 | +from rubin_sim.maf.utils.opsim_utils import get_sim_data |
36 | 39 |
|
37 | 40 | LOGGER = logging.getLogger(__name__)
|
38 | 41 |
|
@@ -931,3 +934,189 @@ def compile_sim_archive_metadata_cli(*args):
|
931 | 934 | compilation_resource = ResourcePath(compilation_uri)
|
932 | 935 |
|
933 | 936 | compilation_resource = compile_sim_metadata(archive_uri, compilation_resource, append=append)
|
| 937 | + |
| 938 | + |
| 939 | +def find_latest_prenight_sim_for_nights( |
| 940 | + first_day_obs: str | None = None, |
| 941 | + last_day_obs: str | None = None, |
| 942 | + tags: tuple[str] = ("ideal", "nominal"), |
| 943 | + max_simulation_age: int = 2, |
| 944 | + archive_uri: str = "s3://rubin:rubin-scheduler-prenight/opsim/", |
| 945 | + compilation_uri: str = "s3://rubin:rubin-scheduler-prenight/opsim/compiled_metadata_cache.h5", |
| 946 | +) -> pd.DataFrame: |
| 947 | + """Find the most recent prenight simulation that covers a night. |
| 948 | +
|
| 949 | + Parameters |
| 950 | + ---------- |
| 951 | + first_day_obs : `str` or `None` |
| 952 | + The date of the evening for the first night for which to get |
| 953 | + a simulation. If `None`, then the current date will be used. |
| 954 | + last_day_obs : `str` or `None` |
| 955 | + The date of the evening for the last night for which to get |
| 956 | + a simulation. If `None`, then the current date will be used. |
| 957 | + tags : `tuple[str]` |
| 958 | + A tuple of tags to filter simulations by. |
| 959 | + Defaults to ``('ideal', 'nominal')``. |
| 960 | + max_simulation_age : `int` |
| 961 | + The maximum age of simulations to consider, in days. |
| 962 | + Simulations older than ``max_simulation_age`` will not be considered. |
| 963 | + Defaults to 2. |
| 964 | + archive_uri : `str` |
| 965 | + The URI of the archive from which to fetch the simulation. |
| 966 | + Defaults to ``s3://rubin:rubin-scheduler-prenight/opsim/``. |
| 967 | + compilation_uri : `str` |
| 968 | + The URI of the compiled metadata HDF5 file for efficient querying. |
| 969 | + Defaults to |
| 970 | + ``s3://rubin:rubin-scheduler-prenight/opsim/compiled_metadata_cache.h5``. |
| 971 | +
|
| 972 | + Returns |
| 973 | + ------- |
| 974 | + sim_metadata : `dict` |
| 975 | + A dictionary with metadata for the simulation. |
| 976 | + """ |
| 977 | + |
| 978 | + if first_day_obs is None: |
| 979 | + first_day_obs = Time(Time.now().mjd - 0.5, format="mjd").iso[:10] |
| 980 | + if last_day_obs is None: |
| 981 | + last_day_obs = first_day_obs |
| 982 | + |
| 983 | + sim_metadata = read_archived_sim_metadata( |
| 984 | + archive_uri, num_nights=max_simulation_age, compilation_resource=compilation_uri |
| 985 | + ) |
| 986 | + |
| 987 | + best_sim = None |
| 988 | + for uri, sim in sim_metadata.items(): |
| 989 | + sim["uri"] = uri |
| 990 | + sim["exec_date"] = uri.split("/")[-3] |
| 991 | + sim["date_index"] = int(uri.split("/")[-2]) |
| 992 | + |
| 993 | + if sim["simulated_dates"]["first"] > first_day_obs: |
| 994 | + continue |
| 995 | + if sim["simulated_dates"]["last"] < last_day_obs: |
| 996 | + continue |
| 997 | + if not set(tags).issubset(sim["tags"]): |
| 998 | + continue |
| 999 | + if best_sim is not None: |
| 1000 | + if sim["exec_date"] < best_sim["exec_date"]: |
| 1001 | + continue |
| 1002 | + if sim["date_index"] < best_sim["date_index"]: |
| 1003 | + continue |
| 1004 | + best_sim = sim |
| 1005 | + |
| 1006 | + if best_sim is not None: |
| 1007 | + best_sim["opsim_rp"] = ( |
| 1008 | + ResourcePath(archive_uri) |
| 1009 | + .join(best_sim["exec_date"], forceDirectory=True) |
| 1010 | + .join(f"{best_sim['date_index']}", forceDirectory=True) |
| 1011 | + .join(best_sim["files"]["observations"]["name"]) |
| 1012 | + ) |
| 1013 | + |
| 1014 | + return best_sim |
| 1015 | + |
| 1016 | + |
| 1017 | +def fetch_latest_prenight_sim_for_nights( |
| 1018 | + first_day_obs: str | None = None, |
| 1019 | + last_day_obs: str | None = None, |
| 1020 | + tags: tuple[str] = ("ideal", "nominal"), |
| 1021 | + max_simulation_age: int = 2, |
| 1022 | + archive_uri: str = "s3://rubin:rubin-scheduler-prenight/opsim/", |
| 1023 | + compilation_uri: str = "s3://rubin:rubin-scheduler-prenight/opsim/compiled_metadata_cache.h5", |
| 1024 | + **kwargs, |
| 1025 | +) -> pd.DataFrame: |
| 1026 | + """Fetches visit parameters from the latest archived pre-night simulation |
| 1027 | + with requested tags for a specified day of observing. |
| 1028 | +
|
| 1029 | + Parameters |
| 1030 | + ---------- |
| 1031 | + first_day_obs : `str` or `None` |
| 1032 | + The date of the evening for the first night for which to get |
| 1033 | + a simulation. If `None`, then the current date will be used. |
| 1034 | + last_day_obs : `str` or `None` |
| 1035 | + The date of the evening for the last night for which to get |
| 1036 | + a simulation. If `None`, then the current date will be used. |
| 1037 | + tags : `tuple[str]` |
| 1038 | + A tuple of tags to filter simulations by. |
| 1039 | + Defaults to ``('ideal', 'nominal')``. |
| 1040 | + max_simulation_age : `int` |
| 1041 | + The maximum age of simulations to consider, in days. Simulations older |
| 1042 | + than ``max_simulation_age`` will not be considered. Defaults to 2. |
| 1043 | + archive_uri : `str` |
| 1044 | + The URI of the archive from which to fetch the simulation. |
| 1045 | + Defaults to ``s3://rubin:rubin-scheduler-prenight/opsim/``. |
| 1046 | + compilation_uri : `str` |
| 1047 | + The URI of the compiled metadata HDF5 file for efficient querying. |
| 1048 | + Defaults to ``s3://rubin:rubin-scheduler-prenight/opsim/compiled_metadata_cache.h5``. |
| 1049 | + **kwargs |
| 1050 | + Additional keyword arguments passed to |
| 1051 | + `rubin_sim.maf.get_sim_data`. |
| 1052 | +
|
| 1053 | + Returns |
| 1054 | + ------- |
| 1055 | + visits : `pd.DataFrame` |
| 1056 | + A pandas DataFrame containing visit parameters. |
| 1057 | + """ |
| 1058 | + |
| 1059 | + sim_metadata = find_latest_prenight_sim_for_nights( |
| 1060 | + first_day_obs, last_day_obs, tags, max_simulation_age, archive_uri, compilation_uri |
| 1061 | + ) |
| 1062 | + visits = get_sim_data(sim_metadata["opsim_rp"], **kwargs) |
| 1063 | + |
| 1064 | + return visits |
| 1065 | + |
| 1066 | + |
| 1067 | +def fetch_obsloctap_visits(day_obs: str | None = None, nights: int = 2) -> pd.DataFrame: |
| 1068 | + """Return visits from latest nominal prenight briefing simulation. |
| 1069 | +
|
| 1070 | + Parameters |
| 1071 | + ---------- |
| 1072 | + day_obs : `str` |
| 1073 | + The day_obs of the night, in YYYY-MM-DD format (e.g. 2025-03-26). |
| 1074 | + Default None will use the date of the next sunset. |
| 1075 | + nights : `int` |
| 1076 | + The number of nights of observations to return. |
| 1077 | + Defaults to 2. |
| 1078 | +
|
| 1079 | + Returns |
| 1080 | + ------- |
| 1081 | + visits : `pd.DataFrame` |
| 1082 | + The visits from the prenight simulation. |
| 1083 | + """ |
| 1084 | + dbcols = [ |
| 1085 | + "observationStartMJD", |
| 1086 | + "fieldRA", |
| 1087 | + "fieldDec", |
| 1088 | + "rotSkyPos", |
| 1089 | + "band", |
| 1090 | + "visitExposureTime", |
| 1091 | + "night", |
| 1092 | + "target_name", |
| 1093 | + ] |
| 1094 | + |
| 1095 | + # Start with the first night that starts after the reference time, |
| 1096 | + # which is the current time by default. |
| 1097 | + # So, if the reference time is during a night, it starts with the |
| 1098 | + # following night. |
| 1099 | + night_bounds = pd.DataFrame(Almanac().sunsets) |
| 1100 | + reference_time = Time.now() if day_obs is None else Time(day_obs, format="iso", scale="utc") |
| 1101 | + first_night = night_bounds.query(f"sunset > {reference_time.mjd}").night.min() |
| 1102 | + last_night = first_night + nights - 1 |
| 1103 | + |
| 1104 | + night_bounds.set_index("night", inplace=True) |
| 1105 | + start_mjd = night_bounds.loc[first_night, "sunset"] |
| 1106 | + end_mjd = night_bounds.loc[last_night, "sunrise"] |
| 1107 | + |
| 1108 | + sqlconstraint = (f"observationStartMJD BETWEEN {start_mjd} AND {end_mjd}",) |
| 1109 | + max_simulation_age = int(np.ceil(Time.now().mjd - reference_time.mjd)) + 1 |
| 1110 | + |
| 1111 | + first_day_obs = Time(start_mjd - 0.5, format="mjd").iso[:10] |
| 1112 | + last_day_obs = Time(end_mjd - 0.5, format="mjd").iso[:10] |
| 1113 | + visits = fetch_latest_prenight_sim_for_nights( |
| 1114 | + first_day_obs, |
| 1115 | + last_day_obs, |
| 1116 | + tags=("ideal", "nominal"), |
| 1117 | + max_simulation_age=max_simulation_age, |
| 1118 | + sqlconstraint=sqlconstraint, |
| 1119 | + dbcols=dbcols, |
| 1120 | + ) |
| 1121 | + |
| 1122 | + return visits |
0 commit comments