package grib import ( "fmt" "time" "git.intra.yksa.space/gsn/predictor/internal/pkg/errcodes" env "github.com/caarlos0/env/v11" ) // DatasetConfig описывает параметры GFS-датасета: сетку, временные шаги, // уровни давления и URL для загрузки. type DatasetConfig struct { // Сетка Resolution float64 // шаг сетки в градусах (0.25 или 0.5) NLat int // точек по широте (721 для 0.25°, 361 для 0.5°) NLon int // точек по долготе (1440 для 0.25°, 720 для 0.5°) // Время NT int // кол-во временных шагов (97 для 0–96 ч с шагом 1) MaxHour int // последний час прогноза (96) TimeStep int // интервал между шагами, часы (1 или 3) // Вертикаль NP int // кол-во уровней давления Levels []float64 // уровни давления в гПа, по убыванию (1000 … 1) // Переменные в кубе (порядок важен: индексы 0, 1, 2, …) NVar int // кол-во переменных Variables []string // GRIB-имена для фильтрации idx (HGT, UGRD, VGRD) // URL загрузки (fmt-шаблоны: date, hour, hour, step) URLMask string // основной pgrb2 URLMaskB string // дополнительный pgrb2b // Имена файлов FileSuffix string // токен разрешения в именах файлов ("0p25", "0p50") } // SizePerVar возвращает размер одной переменной в кубе, байт. func (dc *DatasetConfig) SizePerVar() int64 { return int64(dc.NT) * int64(dc.NP) * int64(dc.NLat) * int64(dc.NLon) * 4 } // CubeSize возвращает полный размер куба, байт. func (dc *DatasetConfig) CubeSize() int64 { return dc.SizePerVar() * int64(dc.NVar) } // GridSize возвращает NLat * NLon. func (dc *DatasetConfig) GridSize() int { return dc.NLat * dc.NLon } // InvResolution возвращает 1/Resolution — множитель для перевода координат в индексы. func (dc *DatasetConfig) InvResolution() float64 { return 1.0 / dc.Resolution } // Steps возвращает список часов прогноза [0, TimeStep, 2*TimeStep, …, MaxHour]. func (dc *DatasetConfig) Steps() []int { out := make([]int, 0, dc.NT) for h := 0; h <= dc.MaxHour; h += dc.TimeStep { out = append(out, h) } return out } // FileName возвращает имя основного GRIB-файла (pgrb2). func (dc *DatasetConfig) FileName(run time.Time, step int) string { return fmt.Sprintf("gfs.t%02dz.pgrb2.%s.f%03d", run.Hour(), dc.FileSuffix, step) } // FileNameB возвращает имя вторичного GRIB-файла (pgrb2b). func (dc *DatasetConfig) FileNameB(run time.Time, step int) string { return fmt.Sprintf("gfs.t%02dz.pgrb2b.%s.f%03d", run.Hour(), dc.FileSuffix, step) } // GribURL возвращает URL основного GRIB-файла. func (dc *DatasetConfig) GribURL(run time.Time, step int) string { return fmt.Sprintf(dc.URLMask, run.Format("20060102"), run.Hour(), run.Hour(), step) } // GribURLB возвращает URL вторичного GRIB-файла. func (dc *DatasetConfig) GribURLB(run time.Time, step int) string { return fmt.Sprintf(dc.URLMaskB, run.Format("20060102"), run.Hour(), run.Hour(), step) } // DefaultDatasetConfig возвращает конфиг GFS 0.25° / 1 час / 47 уровней. func DefaultDatasetConfig() DatasetConfig { return DatasetConfig{ Resolution: 0.25, NLat: 721, NLon: 1440, NT: 97, MaxHour: 96, TimeStep: 1, NP: 47, Levels: []float64{ 1000, 975, 950, 925, 900, 875, 850, 825, 800, 775, 750, 725, 700, 675, 650, 625, 600, 575, 550, 525, 500, 475, 450, 425, 400, 375, 350, 325, 300, 275, 250, 225, 200, 175, 150, 125, 100, 70, 50, 30, 20, 10, 7, 5, 3, 2, 1, }, NVar: 3, Variables: []string{"HGT", "UGRD", "VGRD"}, URLMask: "https://noaa-gfs-bdp-pds.s3.amazonaws.com/gfs.%s/%02d/atmos/gfs.t%02dz.pgrb2.0p25.f%03d", URLMaskB: "https://noaa-gfs-bdp-pds.s3.amazonaws.com/gfs.%s/%02d/atmos/gfs.t%02dz.pgrb2b.0p25.f%03d", FileSuffix: "0p25", } } // --------------------------------------------------------------------------- type Config struct { Dir string `env:"DIR" envDefault:"C:/tmp/grib"` TTL time.Duration `env:"TTL" envDefault:"48h"` CacheTTL time.Duration `env:"CACHE_TTL" envDefault:"1h"` Parallel int `env:"PARALLEL" envDefault:"8"` Dataset DatasetConfig } func NewConfig() (*Config, error) { cfg := &Config{} if err := env.ParseWithOptions(cfg, env.Options{ PrefixTagName: "GSN_PREDICTOR_GRIB_", }); err != nil { return nil, errcodes.Wrap(err, "failed to parse GRIB config") } cfg.Dataset = DefaultDatasetConfig() return cfg, nil }