diff --git a/modules/service/src/main/resources/db/migration/V0927__source_profile_summary.sql b/modules/service/src/main/resources/db/migration/V0927__source_profile_summary.sql index ea3f9e520..82112ec0b 100644 --- a/modules/service/src/main/resources/db/migration/V0927__source_profile_summary.sql +++ b/modules/service/src/main/resources/db/migration/V0927__source_profile_summary.sql @@ -19,15 +19,12 @@ DECLARE BEGIN SELECT COALESCE( - CASE WHEN src_profile->'point' IS NOT NULL THEN 'point'::e_source_profile_type END, - CASE WHEN src_profile->'uniform' IS NOT NULL THEN 'uniform'::e_source_profile_type END, - CASE WHEN src_profile->'gaussian' IS NOT NULL THEN 'gaussian'::e_source_profile_type END + 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. - CASE WHEN src_profile->'gaussian' ? 'fwhm' - THEN (src_profile->'gaussian'->'fwhm'->>'microarcseconds')::d_angle_µas - ELSE NULL - END + (src_profile->'gaussian'->'fwhm'->>'microarcseconds')::d_angle_µas INTO profile_type, fwhm; IF profile_type IS NULL THEN diff --git a/modules/service/src/main/resources/db/migration/V0928__gmos_binning.sql b/modules/service/src/main/resources/db/migration/V0928__gmos_binning.sql index ac6a54139..5e89ccd50 100644 --- a/modules/service/src/main/resources/db/migration/V0928__gmos_binning.sql +++ b/modules/service/src/main/resources/db/migration/V0928__gmos_binning.sql @@ -59,7 +59,7 @@ CREATE OR REPLACE FUNCTION calculate_gmos_long_slit_spectral_binning( slit_µas d_angle_µas, dispersion_pm smallint, resolution smallint, - blaze_pm smallint, + blaze_nm smallint, src_profile jsonb, sampling double precision ) @@ -73,7 +73,7 @@ DECLARE 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_pm::double precision / 1000.0) / eff_res_as / (dispersion_pm / 1000.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 @@ -147,13 +147,13 @@ DECLARE slit_µas d_angle_µas; dispersion_pm smallint; resolution smallint; - blaze_pm smallint; + blaze_nm smallint; src_profile jsonb; profiles jsonb[]; current_xbin smallint; current_ybin smallint; - min_xbin smallint := NULL; - min_ybin smallint := NULL; + min_xbin smallint := 100; + min_ybin smallint := 100; xbin d_tag; ybin d_tag; BEGIN @@ -169,7 +169,7 @@ BEGIN 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_pm + 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 @@ -179,10 +179,10 @@ BEGIN 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_pm + 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_north_disperser d ON g.c_grating = d.c_tag + LEFT JOIN t_gmos_south_disperser d ON g.c_grating = d.c_tag WHERE g.c_observation_id = oid; ELSE @@ -193,7 +193,7 @@ BEGIN 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 c_observation_id = oid + WHERE a.c_observation_id = oid ) INTO profiles; -- HERE we want to compute the spectral (xbin) and spatial (ybin) binning for @@ -202,7 +202,7 @@ BEGIN 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_pm, src_profile, sampling := 2.5 + 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 @@ -216,9 +216,9 @@ BEGIN xbin := lookup_bin_tag(min_xbin); ybin := lookup_bin_tag(min_ybin); - RAISE NOTICE 'xbin %, ybin %', xbin, ybin; - - 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; + 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; \ No newline at end of file diff --git a/modules/service/src/main/resources/db/migration/V0929__grouping_modes.sql b/modules/service/src/main/resources/db/migration/V0929__grouping_modes.sql index c0d462016..3337618a6 100644 --- a/modules/service/src/main/resources/db/migration/V0929__grouping_modes.sql +++ b/modules/service/src/main/resources/db/migration/V0929__grouping_modes.sql @@ -213,4 +213,61 @@ CREATE OR REPLACE VIEW v_observation AS LEFT JOIN t_proposal p on p.c_program_id = o.c_program_id LEFT JOIN t_cfp c on p.c_cfp_id = c.c_cfp_id LEFT JOIN t_gmos_north_long_slit mode_gnls ON o.c_observation_id = mode_gnls.c_observation_id - LEFT JOIN t_gmos_south_long_slit mode_gsls ON o.c_observation_id = mode_gsls.c_observation_id; \ No newline at end of file + LEFT JOIN t_gmos_south_long_slit mode_gsls ON o.c_observation_id = mode_gsls.c_observation_id; + +-- Update these views to get the new default binning column +DROP VIEW v_gmos_north_long_slit; + +CREATE OR REPLACE VIEW v_gmos_north_long_slit AS +SELECT + m.*, + o.c_image_quality, + t.c_source_profile +FROM + t_gmos_north_long_slit m +INNER JOIN t_observation o + ON m.c_observation_id = o.c_observation_id +LEFT JOIN v_asterism_single_target_for_long_slit a + ON m.c_observation_id = a.c_observation_id +LEFT JOIN t_target t + ON a.c_target_id = t.c_target_id; + +DROP VIEW v_gmos_south_long_slit; + +CREATE OR REPLACE VIEW v_gmos_south_long_slit AS +SELECT + m.*, + o.c_image_quality, + t.c_source_profile +FROM + t_gmos_south_long_slit m +INNER JOIN t_observation o + ON m.c_observation_id = o.c_observation_id +LEFT JOIN v_asterism_single_target_for_long_slit a + ON m.c_observation_id = a.c_observation_id +LEFT JOIN t_target t + ON a.c_target_id = t.c_target_id; + +CREATE OR REPLACE FUNCTION trigger_set_gmos_long_slit_default_binning() +RETURNS TRIGGER AS $$ +BEGIN + CALL set_gmos_long_slit_default_binning(NEW.c_observation_id); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER gmos_north_long_slit_binning_trigger +AFTER INSERT ON t_gmos_north_long_slit +FOR EACH ROW +EXECUTE FUNCTION trigger_set_gmos_long_slit_default_binning(); + +CREATE TRIGGER gmos_south_long_slit_binning_trigger +AFTER INSERT ON t_gmos_south_long_slit +FOR EACH ROW +EXECUTE FUNCTION trigger_set_gmos_long_slit_default_binning(); + +CREATE TRIGGER asterism_target_binning_trigger +AFTER INSERT ON t_asterism_target +FOR EACH ROW +EXECUTE FUNCTION trigger_set_gmos_long_slit_default_binning(); + diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/mapping/GmosLongSlitMapping.scala b/modules/service/src/main/scala/lucuma/odb/graphql/mapping/GmosLongSlitMapping.scala index 193a27980..603ad1798 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/mapping/GmosLongSlitMapping.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/mapping/GmosLongSlitMapping.scala @@ -6,30 +6,23 @@ package mapping import cats.syntax.all.* import coulomb.* -import grackle.Cursor import grackle.Result import grackle.skunk.SkunkMapping import io.circe.Json import io.circe.syntax.* import lucuma.core.enums.GmosAmpGain import lucuma.core.enums.GmosAmpReadMode -import lucuma.core.enums.GmosNorthFpu import lucuma.core.enums.GmosNorthGrating import lucuma.core.enums.GmosRoi -import lucuma.core.enums.GmosSouthFpu import lucuma.core.enums.GmosSouthGrating import lucuma.core.enums.GmosXBinning import lucuma.core.enums.GmosYBinning -import lucuma.core.enums.ImageQuality import lucuma.core.math.Offset.Q import lucuma.core.math.WavelengthDither import lucuma.core.math.units.Nanometer -import lucuma.core.model.SourceProfile -import lucuma.core.model.sequence.gmos.binning.DefaultSampling import lucuma.core.model.sequence.gmos.longslit.* import lucuma.odb.graphql.table.* import lucuma.odb.json.offset.query.given -import lucuma.odb.json.sourceprofile.given import lucuma.odb.json.wavelength.query.given import lucuma.odb.sequence.gmos.longslit.Config @@ -58,9 +51,11 @@ trait GmosLongSlitMapping[F[_]] import GmosLongSlitMapping._ val xBin: FieldMapping = explicitOrElseDefault[GmosXBinning]("xBin", "explicitXBin", "defaultXBin") + val defaultXBin: FieldMapping = SqlField("defaultXBin", cc.XBinDefault) val explicitXBin: FieldMapping = SqlField("explicitXBin", cc.XBin) val yBin: FieldMapping = explicitOrElseDefault[GmosYBinning]("yBin", "explicitYBin", "defaultYBin") + val defaultYBin: FieldMapping = SqlField("defaultYBin", cc.YBinDefault) val explicitYBin: FieldMapping = SqlField("explicitYBin", cc.YBin) val ampReadMode: FieldMapping = explicitOrElseDefault[GmosAmpReadMode]("ampReadMode", "explicitAmpReadMode", "defaultAmpReadMode") @@ -141,23 +136,11 @@ trait GmosLongSlitMapping[F[_]] // --------------------- common.xBin, - - CursorField[GmosXBinning]( - "defaultXBin", - defaultBinning(northBinning(_, _, _, _, sampling = DefaultSampling)).map(_._1F), - List("fpu", "grating", "imageQuality", "sourceProfile") - ), - + common.defaultXBin, common.explicitXBin, common.yBin, - - CursorField[GmosYBinning]( - "defaultYBin", - defaultBinning(northBinning(_, _, _, _, sampling = DefaultSampling)).map(_._2F), - List("fpu", "grating", "imageQuality", "sourceProfile") - ), - + common.defaultYBin, common.explicitYBin, common.ampReadMode, @@ -248,23 +231,11 @@ trait GmosLongSlitMapping[F[_]] // --------------------- common.xBin, - - CursorField[GmosXBinning]( - "defaultXBin", - defaultBinning(southBinning(_, _, _, _, sampling = DefaultSampling)).map(_._1F), - List("fpu", "grating", "imageQuality", "sourceProfile") - ), - + common.defaultXBin, common.explicitXBin, common.yBin, - - CursorField[GmosYBinning]( - "defaultYBin", - defaultBinning(southBinning(_, _, _, _, sampling = DefaultSampling)).map(_._2F), - List("fpu", "grating", "imageQuality", "sourceProfile") - ), - + common.defaultYBin, common.explicitYBin, common.ampReadMode, @@ -338,20 +309,6 @@ trait GmosLongSlitMapping[F[_]] object GmosLongSlitMapping { - private def defaultBinning[U: ClassTag, G: ClassTag]( - f: (U, SourceProfile, ImageQuality, G) => (GmosXBinning, GmosYBinning) - ): Cursor => Result[(GmosXBinning, GmosYBinning)] = cursor => - for { - fpu <- cursor.fieldAs[U]("fpu") - iq <- cursor.fieldAs[ImageQuality]("imageQuality") - j <- cursor.fieldAs[Option[Json]]("sourceProfile") - g <- cursor.fieldAs[G]("grating") - sp <- j.traverse(json => Result.fromEither(json.as[SourceProfile].leftMap(_.message))) - } yield - sp.fold((GmosXBinning.One, GmosYBinning.One)) { sourceProfile => - f(fpu, sourceProfile, iq, g) - } - private def parseCsvBigDecimals(s: String): List[BigDecimal] = s.split(',').toList.map(n => BigDecimal(n.trim)) diff --git a/modules/service/src/main/scala/lucuma/odb/graphql/table/GmosLongSlitView.scala b/modules/service/src/main/scala/lucuma/odb/graphql/table/GmosLongSlitView.scala index 26d0f9783..a4a81dfb8 100644 --- a/modules/service/src/main/scala/lucuma/odb/graphql/table/GmosLongSlitView.scala +++ b/modules/service/src/main/scala/lucuma/odb/graphql/table/GmosLongSlitView.scala @@ -18,7 +18,9 @@ trait GmosLongSlitView[F[_]] extends BaseMapping[F] { val CentralWavelength: ColumnRef = col("c_central_wavelength", wavelength_pm) val XBin: ColumnRef = col("c_xbin", gmos_x_binning.opt) + val XBinDefault: ColumnRef = col("c_xbin_default", gmos_x_binning) val YBin: ColumnRef = col("c_ybin", gmos_y_binning.opt) + val YBinDefault: ColumnRef = col("c_ybin_default", gmos_y_binning) val AmpReadMode: ColumnRef = col("c_amp_read_mode", gmos_amp_read_mode.opt) val AmpGain: ColumnRef = col("c_amp_gain", gmos_amp_gain.opt) val Roi: ColumnRef = col("c_roi", gmos_roi.opt) diff --git a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala index 2b3b1ba0b..3ae737f3e 100644 --- a/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala +++ b/modules/service/src/test/scala/lucuma/odb/graphql/mutation/createObservation.scala @@ -34,23 +34,17 @@ import lucuma.core.enums.ScienceMode import lucuma.core.enums.Site import lucuma.core.enums.SpectroscopyCapabilities import lucuma.core.enums.TimeAccountingCategory -import lucuma.core.math.Angle import lucuma.core.model.GuestUser import lucuma.core.model.Program import lucuma.core.model.Semester import lucuma.core.model.ServiceUser -import lucuma.core.model.SourceProfile -import lucuma.core.model.SpectralDefinition import lucuma.core.model.StandardUser import lucuma.core.model.Target import lucuma.core.model.User -import lucuma.core.model.sequence.gmos.binning.northSpectralBinning import lucuma.core.syntax.timespan.* import lucuma.odb.data.PosAngleConstraintMode import lucuma.odb.graphql.input.AllocationInput -import scala.collection.immutable.SortedMap - class createObservation extends OdbSuite { extension (ac: ACursor) @@ -984,6 +978,8 @@ class createObservation extends OdbSuite { nanometers } xBin, + explicitXBin, + defaultXBin, yBin, explicitYBin defaultYBin @@ -1011,6 +1007,8 @@ class createObservation extends OdbSuite { longSlit.downIO[GmosNorthFpu]("fpu"), longSlit.downIO[Double]("centralWavelength", "nanometers"), longSlit.downIO[GmosXBinning]("xBin"), + longSlit.downIO[Option[GmosXBinning]]("explicitXBin"), + longSlit.downIO[GmosXBinning]("defaultXBin"), longSlit.downIO[GmosYBinning]("yBin"), longSlit.downIO[Option[GmosYBinning]]("explicitYBin"), longSlit.downIO[GmosYBinning]("defaultYBin"), @@ -1024,6 +1022,8 @@ class createObservation extends OdbSuite { GmosNorthFpu.LongSlit_0_25, 234.56, GmosXBinning.One, + None, + GmosXBinning.One, GmosYBinning.Two, Some(GmosYBinning.Two), GmosYBinning.One, @@ -1068,6 +1068,8 @@ class createObservation extends OdbSuite { longSlit.downIO[GmosNorthFpu]("fpu"), longSlit.downIO[Double]("centralWavelength", "nanometers"), longSlit.downIO[GmosXBinning]("xBin"), + longSlit.downIO[Option[GmosXBinning]]("explicitXBin"), + longSlit.downIO[GmosXBinning]("defaultXBin"), longSlit.downIO[GmosYBinning]("yBin"), longSlit.downIO[Option[GmosYBinning]]("explicitYBin"), longSlit.downIO[GmosYBinning]("defaultYBin"), @@ -1080,7 +1082,9 @@ class createObservation extends OdbSuite { Some(GmosNorthFilter.GPrime), GmosNorthFpu.LongSlit_5_00, 234.56, - northSpectralBinning(GmosNorthFpu.LongSlit_5_00, SourceProfile.Gaussian(Angle.fromMicroarcseconds(647200), SpectralDefinition.BandNormalized(none, SortedMap.empty)), ImageQuality.PointOne, GmosNorthGrating.B1200_G5301), + GmosXBinning.Two, + None, + GmosXBinning.Two, GmosYBinning.Two, Some(GmosYBinning.Two), GmosYBinning.Two, @@ -1106,6 +1110,8 @@ class createObservation extends OdbSuite { longSlit.downIO[GmosSouthFpu]("fpu"), longSlit.downIO[Double]("centralWavelength", "nanometers"), longSlit.downIO[GmosXBinning]("xBin"), + longSlit.downIO[Option[GmosXBinning]]("explicitXBin"), + longSlit.downIO[GmosXBinning]("defaultXBin"), longSlit.downIO[GmosYBinning]("yBin"), longSlit.downIO[Option[GmosYBinning]]("explicitYBin"), longSlit.downIO[GmosYBinning]("defaultYBin"), @@ -1119,6 +1125,8 @@ class createObservation extends OdbSuite { GmosSouthFpu.LongSlit_0_25, 234.56, GmosXBinning.One, + None, + GmosXBinning.One, GmosYBinning.Two, Some(GmosYBinning.Two), GmosYBinning.One,