Skip to content
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

WIP: observing mode group #1470

Merged
merged 14 commits into from
Nov 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -9548,6 +9548,39 @@ type Query {
includeDeleted: Boolean! = false
): ObservationSelectResult!

"""
Observations grouped by commonly held observing modes. Identify the program by
specifying only one of programId, programReference, or proposalReference. If
more than one is provided, all must match. If none are set, nothing will
match.
"""
observingModeGroup(

"Program ID"
programId: ProgramId

"Proposal Reference"
proposalReference: ProposalReferenceLabel

"Program reference"
programReference: ProgramReferenceLabel

"""
Filters the selection of observations.
"""
WHERE: WhereObservation

"""
Limits the result to at most this number of matches (but never more than 1000).
"""
LIMIT: NonNegInt

"""
Set to true to include deleted values
"""
includeDeleted: Boolean! = false
): ObservingModeGroupSelectResult!

"""
Returns the program with the given id or reference, if any. Identify the
program by specifying only one of programId, programReference, or
Expand Down Expand Up @@ -9626,21 +9659,6 @@ type Query {
"""
proposalStatusMeta: [ProposalStatusMeta!]!

# Observations grouped by commonly held science mode
# observingModeGroup(
# # Program ID
# programId: ProgramId!

# # Filters the selection of observations.
# WHERE: WhereObservation

# # Limits the result to at most this number of matches (but never more than 1000).
# LIMIT: NonNegInt

# # Set to true to include deleted values
# includeDeleted: Boolean! = false
# ): ObservingModeGroupSelectResult!

# Observations grouped by commonly held science requirements
# scienceRequirementsGroup(
# # Program ID
Expand Down Expand Up @@ -9825,11 +9843,6 @@ type ObservingMode {
}

type ObservingModeGroup {
"""
IDs of observations that use the same constraints
"""
observationIds: [ObservationId!]!

"""
Observations associated with the common value
"""
Expand All @@ -9853,7 +9866,12 @@ type ObservingModeGroup {
"""
Commonly held value across the observations
"""
observingMode: ObservingMode
observingMode: ObservingMode!

"""
Link back to program.
"""
program: Program!
}

"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-- Source profile type discriminator
--
CREATE TYPE e_source_profile_type AS ENUM(
'point',
'uniform',
'gaussian'
);

-- Summary of source profile information useful for determining object size,
-- and therefore impacting the binning calculation.
--
CREATE TYPE source_profile_summary AS (
c_profile_type e_source_profile_type,
c_fwhm d_angle_µas
);

-- Extracts the source profile summary from the source profile JSONB.
--
CREATE OR REPLACE FUNCTION extract_source_profile_summary(
src_profile jsonb
)
RETURNS source_profile_summary AS $$
DECLARE
profile_type e_source_profile_type;
fwhm d_angle_µas;
BEGIN
SELECT
COALESCE(
CASE WHEN src_profile -> 'point' ?| array['bandNormalized', 'emissionLines'] THEN 'point'::e_source_profile_type END,
CASE WHEN src_profile -> 'uniform' ?| array['bandNormalized', 'emissionLines'] THEN 'uniform'::e_source_profile_type END,
CASE WHEN src_profile -> 'gaussian' ? 'fwhm' THEN 'gaussian'::e_source_profile_type END
),
-- We only have a fwhm for gaussian profiles, otherwise it is null.
(src_profile->'gaussian'->'fwhm'->>'microarcseconds')::d_angle_µas
INTO profile_type, fwhm;

IF profile_type IS NULL THEN
RAISE EXCEPTION 'Invalid source profile: expected one of "point", "uniform", or "gaussian".';
END IF;

IF profile_type = 'gaussian' AND fwhm IS NULL THEN
RAISE EXCEPTION 'Invalid source profile: gaussian profile with no fwhm value.';
END IF;

RETURN (profile_type, fwhm);
END;
$$ LANGUAGE plpgsql IMMUTABLE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
-- Object angular size estimate based on image quality and source profile.
--
CREATE OR REPLACE FUNCTION calculate_object_size(
iq numeric(2, 1),
src_profile jsonb
)
RETURNS d_angle_µas AS $$
DECLARE
profile_type e_source_profile_type;
fwhm d_angle_µas;
fwhm_arcsec double precision;
iq_arcsec double precision;
result d_angle_µas;
BEGIN

SELECT c_profile_type, c_fwhm
INTO profile_type, fwhm
FROM extract_source_profile_summary(src_profile);

CASE
WHEN profile_type = 'point' THEN
result := iq * 1000000;
WHEN profile_type = 'uniform' THEN
result := 648000000000; -- 180 degrees
ELSE
iq_arcsec := iq::double precision;
fwhm_arcsec := fwhm::double precision / 1e6;
result := (sqrt(fwhm_arcsec * fwhm_arcsec + iq_arcsec * iq_arcsec) * 1e6)::d_angle_µas;
END CASE;

RETURN result;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- The effective width of the object, which is the object size estimate but
-- limited by the slit width.
--
CREATE OR REPLACE FUNCTION calculate_effective_width(
iq numeric(2, 1),
slit_µas d_angle_µas,
src_profile jsonb
)
RETURNS d_angle_µas AS $$
DECLARE
obj_size_µas d_angle_µas;
BEGIN
SELECT calculate_object_size(iq, src_profile) INTO obj_size_µas;
RETURN least(obj_size_µas, slit_µas);
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- GMOS longslit spectral binning calculation.
--
CREATE OR REPLACE FUNCTION calculate_gmos_long_slit_spectral_binning(
iq numeric(2, 1),
slit_µas d_angle_µas,
dispersion_pm smallint,
resolution smallint,
blaze_nm smallint,
src_profile jsonb,
sampling double precision
)
RETURNS smallint AS $$
DECLARE
eff_width_µas d_angle_µas;
eff_res_as double precision;
n_pix double precision;
binning smallint[];
bin smallint;
BEGIN
SELECT calculate_effective_width(iq, slit_µas, src_profile) INTO eff_width_µas;
eff_res_as := resolution::double precision / 2.0 / (eff_width_µas::double precision / 1000000.0);
n_pix := blaze_nm::double precision / eff_res_as / (dispersion_pm::double precision / 1000.0);

SELECT ARRAY(SELECT c_count FROM t_gmos_binning ORDER BY c_count DESC) INTO binning;
FOREACH bin IN ARRAY binning
LOOP
IF sampling < (n_pix / bin::double precision) THEN
RETURN bin;
END IF;
END LOOP;

RETURN 1;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- GMOS longslit spatial binning calculation.
--
CREATE OR REPLACE FUNCTION calculate_gmos_long_slit_spatial_binning(
iq numeric(2, 1),
pixel_scale_µas d_angle_µas,
src_profile jsonb,
sampling double precision
)
RETURNS smallint AS $$
DECLARE
obj_size_µas d_angle_µas;
n_pix double precision;
binning smallint[];
bin smallint;
BEGIN
SELECT calculate_object_size(iq, src_profile) INTO obj_size_µas;
n_pix := (obj_size_µas::double precision / 1000000.0) /
(pixel_scale_µas::double precision / 1000000.0 * sampling);

SELECT ARRAY(SELECT c_count FROM t_gmos_binning ORDER BY c_count DESC) INTO binning;
FOREACH bin IN ARRAY binning
LOOP
IF bin::double precision < n_pix THEN
RETURN bin;
END IF;
END LOOP;

RETURN 1;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

CREATE OR REPLACE FUNCTION lookup_bin_tag(
bin smallint
)
RETURNS d_tag AS $$
BEGIN
RETURN (SELECT c_tag FROM t_gmos_binning WHERE c_count = bin);
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- Set the default binning.
--
CREATE OR REPLACE PROCEDURE set_gmos_long_slit_default_binning(
oid d_observation_id
) AS $$
DECLARE
iq numeric(2, 1);
mode e_observing_mode_type;
pixel_scale_µas d_angle_µas;
slit_µas d_angle_µas;
dispersion_pm smallint;
resolution smallint;
blaze_nm smallint;
src_profile jsonb;
profiles jsonb[];
current_xbin smallint;
current_ybin smallint;
min_xbin smallint := 100;
min_ybin smallint := 100;
xbin d_tag;
ybin d_tag;
BEGIN

-- Determine the IQ and observing mode.
SELECT q.c_value, o.c_observing_mode_type
INTO iq, mode
FROM t_observation o
LEFT JOIN t_image_quality q on o.c_image_quality = q.c_tag
WHERE o.c_observation_id = oid;

-- Lookup the slit width, dispersion, resolution and blaze wavelength.
CASE
WHEN mode = 'gmos_north_long_slit' THEN
SELECT c_pixel_size FROM t_gmos_north_detector WHERE c_tag = 'HAMAMATSU' INTO pixel_scale_µas;

SELECT f.c_slit_width, d.c_dispersion_pm, d.c_reference_resolution, d.c_blaze_wavelength_nm
INTO slit_µas, dispersion_pm, resolution, blaze_nm
FROM t_gmos_north_long_slit g
LEFT JOIN t_gmos_north_fpu f ON g.c_fpu = f.c_tag
LEFT JOIN t_gmos_north_disperser d ON g.c_grating = d.c_tag
WHERE g.c_observation_id = oid;

WHEN mode = 'gmos_south_long_slit' THEN
SELECT c_pixel_size FROM t_gmos_south_detector WHERE c_tag = 'HAMAMATSU' INTO pixel_scale_µas;

SELECT f.c_slit_width, d.c_dispersion_pm, d.c_reference_resolution, d.c_blaze_wavelength_nm
INTO slit_µas, dispersion_pm, resolution, blaze_nm
FROM t_gmos_south_long_slit g
LEFT JOIN t_gmos_south_fpu f ON g.c_fpu = f.c_tag
LEFT JOIN t_gmos_south_disperser d ON g.c_grating = d.c_tag
WHERE g.c_observation_id = oid;

ELSE
-- Only applies ot GMOS Long Slit.
RETURN;
END CASE;

-- Get all the source profiles in the asterism.
SELECT ARRAY(
SELECT t.c_source_profile
FROM t_asterism_target a
LEFT JOIN t_target t ON a.c_target_id = t.c_target_id
WHERE a.c_observation_id = oid
) INTO profiles;

-- HERE we want to compute the spectral (xbin) and spatial (ybin) binning for
-- each profile and take the minimum value in each case.
FOREACH src_profile IN ARRAY profiles
LOOP
-- Compute spectral (xbin) and spatial (ybin) binning for each profile
current_xbin := calculate_gmos_long_slit_spectral_binning(
iq, slit_µas, dispersion_pm, resolution, blaze_nm, src_profile, sampling := 2.5
);
current_ybin := calculate_gmos_long_slit_spatial_binning(
iq, pixel_scale_µas, src_profile, sampling := 2.5
);

-- Track minimum xbin and ybin values
min_xbin := LEAST(COALESCE(min_xbin, current_xbin), current_xbin);
min_ybin := LEAST(COALESCE(min_ybin, current_ybin), current_ybin);
END LOOP;

-- Turn the binning number into a d_tag, if possible.
xbin := lookup_bin_tag(min_xbin);
ybin := lookup_bin_tag(min_ybin);

-- Set the binning in the appropriate long slit table (north or south).
IF xbin IS NOT NULL AND ybin IS NOT NULL THEN
EXECUTE format('UPDATE t_%I SET c_xbin_default = $1, c_ybin_default = $2 WHERE c_observation_id = $3', mode::text)
USING xbin, ybin, oid;
END IF;
END;
$$ LANGUAGE plpgsql;
Loading
Loading