predictor/internal/pkg/grib/config.go
2026-03-22 16:29:53 +09:00

139 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 для 096 ч с шагом 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
}