package gfs import "fmt" // Family is the dataset family ("gfs" or "gefs"). Variants of different // families have different URL layouts but share the cube format. type Family int const ( FamilyGFS Family = iota FamilyGEFS ) func (f Family) String() string { switch f { case FamilyGEFS: return "gefs" default: return "gfs" } } // HasMember reports whether the family requires a member index in URLs. func (f Family) HasMember() bool { return f == FamilyGEFS } // GEFS variant constants. // // The 21-member ensemble is gec00 (control) + gep01..gep20 (perturbations). // NOAA publishes more members today but 21 matches the historical Tawhiri // configuration and is what the phase 2 spec calls for. const GEFSMembers = 21 // GefsMemberName returns the file-name token for a GEFS member. // member=0 → "gec00", member=1..20 → "gep01".."gep20". func GefsMemberName(member int) string { if member == 0 { return "gec00" } return fmt.Sprintf("gep%02d", member) } // GEFS S3 mirror. const GEFSS3BaseURL = "https://noaa-gefs-pds.s3.amazonaws.com" // GefsGribURL returns the S3 URL for a GEFS primary GRIB file. func GefsGribURL(date string, runHour, member, forecastStep int, resToken string) string { return fmt.Sprintf("%s/gefs.%s/%02d/atmos/pgrb2ap5/%s.t%02dz.pgrb2a.%s.f%03d", GEFSS3BaseURL, date, runHour, GefsMemberName(member), runHour, resToken, forecastStep) } // GefsGribURLB returns the S3 URL for a GEFS secondary GRIB file. func GefsGribURLB(date string, runHour, member, forecastStep int, resToken string) string { return fmt.Sprintf("%s/gefs.%s/%02d/atmos/pgrb2bp5/%s.t%02dz.pgrb2b.%s.f%03d", GEFSS3BaseURL, date, runHour, GefsMemberName(member), runHour, resToken, forecastStep) } // GEFS variants — 0.5° resolution, 3-hour cadence, 192h horizon. var GEFS0p50_3h = &Variant{ ID: "gefs-0p50-3h", Family: FamilyGEFS, ResToken: "0p50", Resolution: 0.5, HourStep: 3, MaxHour: 192, Pressures: GFS0p50_3h.Pressures, PressuresPgrb2: GFS0p50_3h.PressuresPgrb2, PressuresPgrb2b: GFS0p50_3h.PressuresPgrb2b, }