139 lines
4.9 KiB
Go
139 lines
4.9 KiB
Go
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
|
||
}
|