Skip to content

Commit

Permalink
Merge pull request #1573 from gemini-hlsw/sc-4541-add-smartgcal-suppo…
Browse files Browse the repository at this point in the history
…rt-for-f2-ls

SmartGCal support for Flamingos2
  • Loading branch information
cquiroz authored Jan 22, 2025
2 parents ed9827e + bba7ea3 commit 094af37
Show file tree
Hide file tree
Showing 7 changed files with 532 additions and 51 deletions.
8 changes: 4 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ val http4sJdkHttpClientVersion = "0.9.2"
val jwtVersion = "5.0.0"
val logbackVersion = "1.5.16"
val log4catsVersion = "2.7.0"
val lucumaCatalogVersion = "0.48.13"
val lucumaItcVersion = "0.25.0"
val lucumaCoreVersion = "0.113.0"
val lucumaCatalogVersion = "0.49.0"
val lucumaItcVersion = "0.25.1"
val lucumaCoreVersion = "0.114.0"
val lucumaGraphQLRoutesVersion = "0.8.17"
val lucumaSsoVersion = "0.8.0"
val lucumaSsoVersion = "0.8.1"
val munitVersion = "0.7.29" // check test output if you attempt to update this
val munitCatsEffectVersion = "1.0.7" // check test output if you attempt to update this
val munitDisciplineVersion = "1.0.9" // check test output if you attempt to update this
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- Smart gcal table for Flamingos-2. A search is performed using items from the
-- search key columns and the corresponding gcal config in t_gcal is retrieved
-- for the matching row(s).
CREATE TABLE t_smart_f2 (

-- Primary key, which is also a foreign key into t_gcal for the matching GCal
-- configuration. We don't really search this table by primary key but rather
-- by the instrument config (see Search Key below). This key just links the
-- instrument row to the matching gcal row in t_gcal.
c_instrument d_tag NOT NULL,
c_gcal_id int4 NOT NULL,
PRIMARY KEY (c_instrument, c_gcal_id),

FOREIGN KEY (c_instrument, c_gcal_id) REFERENCES t_gcal(c_instrument, c_gcal_id),
CONSTRAINT check_is_f2 CHECK (c_instrument = 'Flamingos2'),

-- Flamingos 2 Search Key. We find smart gcal rows via lookups on the full
-- search key. An index of the first three columns is created after the
-- table is initially loaded.
c_disperser d_tag REFERENCES t_f2_disperser(c_tag),
c_filter d_tag REFERENCES t_f2_filter(c_tag),
c_fpu d_tag REFERENCES t_f2_fpu(c_tag),

-- When there are multiple Gcal configuration values matching the same key,
-- the step order is used to order them. If two or more have the same step
-- order, they may be executed in any order.
c_step_order int8 NOT NULL DEFAULT 0,
CONSTRAINT check_positive_step_order CHECK (c_step_order > 0),

-- Instrument configuration value, not considered part of the search key.
c_exposure_time interval NOT NULL,
CONSTRAINT check_non_negative_exposure_time CHECK (c_exposure_time >= interval '0 seconds')

);
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import cats.syntax.foldable.*
import cats.syntax.functor.*
import eu.timepit.refined.types.numeric.PosInt
import eu.timepit.refined.types.numeric.PosLong
import lucuma.core.enums.F2Disperser
import lucuma.core.enums.F2Filter
import lucuma.core.enums.F2Fpu
import lucuma.core.enums.GcalBaselineType
import lucuma.core.enums.GmosAmpGain
import lucuma.core.enums.GmosGratingOrder
Expand All @@ -25,14 +28,18 @@ import lucuma.core.enums.SmartGcalType
import lucuma.core.math.BoundedInterval
import lucuma.core.math.Wavelength
import lucuma.core.model.sequence.StepConfig.Gcal
import lucuma.core.model.sequence.f2.F2DynamicConfig as Flamingos2
import lucuma.core.model.sequence.gmos.DynamicConfig.GmosNorth
import lucuma.core.model.sequence.gmos.DynamicConfig.GmosSouth
import lucuma.core.util.TimeSpan
import lucuma.odb.smartgcal.data.Flamingos2.TableKey as Flamingos2SearchKey
import lucuma.odb.smartgcal.data.Flamingos2.TableRow as Flamingos2TableRow
import lucuma.odb.smartgcal.data.Gmos.SearchKey.North as GmosNorthSearchKey
import lucuma.odb.smartgcal.data.Gmos.SearchKey.South as GmosSouthSearchKey
import lucuma.odb.smartgcal.data.Gmos.TableRow.North as GmosNorthTableRow
import lucuma.odb.smartgcal.data.Gmos.TableRow.South as GmosSouthTableRow
import lucuma.odb.util.Codecs.*
import lucuma.odb.util.F2Codecs.*
import lucuma.odb.util.GmosCodecs.*
import skunk.*
import skunk.codec.numeric.int4
Expand Down Expand Up @@ -74,6 +81,22 @@ trait SmartGcalService[F[_]] {
sgt: SmartGcalType
): F[List[(GmosSouth => GmosSouth, Gcal)]]

/**
* Selects calibration information corresponding to the given search key and
* type. There can be multiple steps per key.
*
* @param f2 Flamingos 2 search information
* @param sgt SmartGcal type of interest
*
* @return list of tuples, each corresponding to a calibration step; each
* tuple contains an update to the dynamic config to apply (e.g., to
* set its exposure time) and the GCAL unit configuration to use
*/
def selectFlamingos2(
f2: Flamingos2SearchKey,
sgt: SmartGcalType
): F[List[(Flamingos2 => Flamingos2, Gcal)]]

// N.B. Insertion is done by a flyway migration and not via this method. The
// insert here is intended for initializing a database for testing.
def insertGmosNorth(
Expand All @@ -85,6 +108,11 @@ trait SmartGcalService[F[_]] {
id: Int,
row: GmosSouthTableRow
): F[Unit]

def insertFlamingos2(
id: Int,
row: Flamingos2TableRow
): F[Unit]
}

object SmartGcalService {
Expand All @@ -96,19 +124,19 @@ object SmartGcalService {
gn: GmosNorthSearchKey,
sgt: SmartGcalType
): F[List[(GmosNorth => GmosNorth, Gcal)]] =
selectGmos(Statements.selectGmosNorth(gn, sgt)) { exposureTime =>
selectGcal(Statements.selectGmosNorth(gn, sgt)) { exposureTime =>
GmosNorth.exposure.replace(exposureTime)
}

override def selectGmosSouth(
gs: GmosSouthSearchKey,
sgt: SmartGcalType
): F[List[(GmosSouth => GmosSouth, Gcal)]] =
selectGmos(Statements.selectGmosSouth(gs, sgt)) { exposureTime =>
selectGcal(Statements.selectGmosSouth(gs, sgt)) { exposureTime =>
GmosSouth.exposure.replace(exposureTime)
}

private def selectGmos[D](
private def selectGcal[D](
af: AppliedFragment
)(
f: TimeSpan => D => D
Expand All @@ -122,6 +150,14 @@ object SmartGcalService {
}
}

def selectFlamingos2(
f2: Flamingos2SearchKey,
sgt: SmartGcalType
): F[List[(Flamingos2 => Flamingos2, Gcal)]] =
selectGcal(Statements.selectF2(f2, sgt)) { exposureTime =>
Flamingos2.exposure.replace(exposureTime)
}

override def insertGmosNorth(
id: Int,
row: GmosNorthTableRow
Expand Down Expand Up @@ -187,8 +223,41 @@ object SmartGcalService {
_ <- insertGcalRow
_ <- insertInstRow
} yield ()
}

def insertFlamingos2(
id: Int,
row: Flamingos2TableRow
): F[Unit] =
val insertGcalRow =
session.executeCommand(
Statements.InsertGcal(
Instrument.Flamingos2,
id,
row.value.gcalConfig,
row.value.stepCount,
row.value.baselineType
)
).void

val insertInstRow =
session.executeCommand(
Statements.InsertFlamingos2(
Instrument.Flamingos2 ,
id ,
row.line ,
row.key.disperser ,
row.key.filter ,
row.key.fpu ,
row.value.instrumentConfig.exposureTime
)
).void

for {
_ <- insertGcalRow
_ <- insertInstRow
} yield ()

}

object Statements {

Expand All @@ -200,7 +269,7 @@ object SmartGcalService {
case SmartGcalType.NightBaseline => void"g.c_gcal_baseline = 'Night'"
}

private def selectGmos(tableName: String, where: List[AppliedFragment]): AppliedFragment =
private def selectGcal(tableName: String, where: List[AppliedFragment]): AppliedFragment =
sql"""
SELECT g.c_gcal_continuum,
g.c_gcal_ar_arc,
Expand Down Expand Up @@ -236,7 +305,7 @@ object SmartGcalService {
whereSmartGcalType(sgt),
)

selectGmos("t_smart_gmos_north", where)
selectGcal("t_smart_gmos_north", where)
}

def selectGmosSouth(
Expand All @@ -257,7 +326,21 @@ object SmartGcalService {
whereSmartGcalType(sgt),
)

selectGmos("t_smart_gmos_south", where)
selectGcal("t_smart_gmos_south", where)
}

def selectF2(
f2: Flamingos2SearchKey,
sgt: SmartGcalType
): AppliedFragment = {
val where = List(
sql"s.c_disperser IS NOT DISTINCT FROM ${f2_disperser.opt}"(f2.disperser),
sql"s.c_filter IS NOT DISTINCT FROM $f2_filter"(f2.filter),
sql"s.c_fpu IS NOT DISTINCT FROM ${f2_fpu.opt}"(f2.fpu),
whereSmartGcalType(sgt),
)

selectGcal("t_smart_f2", where)
}

val InsertGcal: Fragment[(
Expand Down Expand Up @@ -374,6 +457,35 @@ object SmartGcalService {
$gmos_amp_gain,
$time_span
"""

val InsertFlamingos2: Fragment[(
Instrument ,
Int ,
PosLong ,
Option[F2Disperser],
F2Filter ,
Option[F2Fpu] ,
TimeSpan
)] =
sql"""
INSERT INTO t_smart_f2 (
c_instrument,
c_gcal_id,
c_step_order,
c_disperser,
c_filter,
c_fpu,
c_exposure_time
) SELECT
$instrument,
$int4,
$int8_pos,
${f2_disperser.opt},
$f2_filter,
${f2_fpu.opt},
$time_span
"""

}
}

Loading

0 comments on commit 094af37

Please sign in to comment.