ARPA-SIMC / arkimet

A set of tools to organize, archive and distribute data files.
Other
14 stars 5 forks source link

Understand and control behavior of --annotate #300

Open dcesari opened 1 year ago

dcesari commented 1 year ago

Problema` articolato. Nel tar issue300.tar.gz ci sono 4 messaggi grib, edzw e cnmc differiscono per il centro di emissione (DWD-Offenbach e Roma) mentre _1 e _2 differiscono per l'uso di una variabile standard WMO o locale rispettivamente. Ci sono inoltre delle definizioni aggiuntive locali di prova.

Lo scopo dei tentativi che generano questa issue è arrivare ad avere delle descrizioni corrette del Product in arki-scan --annotate (ovvero nelle interfacce arkiweb/meteohub) anche per parametri che stanno nelle tabelle locali e non in quelle standard WMO.

Però fin dai primi tentativi mi sono scontrato con delle stranezze di comportamento del risultato di --annotate. Primo tentativo con i file grib allegati, non modifico nessun ambiente, uso le definizioni standard di eccodes:

 $ arki-scan --yaml --annotate grib:edzw_1.grib |grep Product
Product: GRIB1(078, 002, 085)   # GRIB1(078, 002, 085)
 $ arki-scan --yaml --annotate grib:cnmc_1.grib |grep Product
Product: GRIB1(080, 002, 085)   # ST Surface temperature of soil K

mentre grib_dump (non so con quale chiave interna) restituisce più giustamente in entrambi i casi:

 # ST Surface temperature of soil K (grib1/2.0.2.table)

trattandosi di variabile standard WMO; questa ci limitiamo a definirla stranezza numero 0.

Se ora faccio il source di ./profile per arricchire le definizioni eccodes con quelle nella cartella corrente, per arki-scan non cambia niente, mentre il grib_dump restituisce

# ST Surface temperature of soil localewmo K (grib1/2.0.2.table)

che è il valore atteso in quanto ho taroccato la tabella relativa in $PWD/definitions .

Quindi il primo problema nel tentativo di estendere le spiegazioni di --annotate è che -apparentemente- arki-scan sembra ignorare eventuali definizioni aggiuntive fornite con la variabile d'ambiente ECCODES_DEFINITION_PATH.

Passando ai messaggi *_2.grib, qui abbiamo una variabile della tabella 201 specifica del DWD che è in qualche modo nota anche nelle definizioni standard di eccodes, estensioni DWD (/usr/share/eccodes/definitions/grib1/localConcepts/edzw, edzw == DWD) e in effetti grib_dump, nel caso di centro DWD riconosce il parametro:

$ grib_dump edzw_2.grib
...
 paramId = 502339;
  #-READ ONLY- cfNameECMF = unknown;
  #-READ ONLY- cfName = unknown;
  #-READ ONLY- cfVarNameECMF = unknown;
  #-READ ONLY- cfVarName = unknown;
  #-READ ONLY- units = ;
  #-READ ONLY- nameECMF = unknown;
  #-READ ONLY- name = Downward direct short wave radiation flux;
...

ma nella didascalia che compare prima di indicatorOfParameter, che pare parzialmente coincidere con l'annotation di arki-scan, la variabile non è riconosciuta

$ grib_dump edzw_2.grib
...
  # Unknown code table entry ()  
  indicatorOfParameter = 22;
...

e quindi arki-scan --annotate grib:edzw_2.grib non fornisce una didascalia in chiaro per il product:

Product: GRIB1(078, 201, 022)   # GRIB1(078, 201, 022)

In questo caso la limitazione è che --annotate prende le informazioni da una parte, mentre le definizioni local del DWD specificano le informazioni da un'altra parte (chiave name ad es.), e quindi, se anche risolvessimo il pimo problema estendendo le definizioni italiane con quelle DWD, non otterremmo comunque la didascalia desiderata con arki-scan.

spanezz commented 1 year ago

La tua analisi è sostanzialmente corretta.

Il codice che formatta i valori per --format è qui: https://github.com/ARPA-SIMC/arkimet/tree/master/python/arkimet/formatter

La formattazione viene fatta a partire dai metadati arkimet e non dal grib, per cui non posso semplicemente interrogare eccodes, e devo reimplementare il lookup delle tabelle di eccodes. L'implementazione attuale è un po' un prototipo, visto che nessuno finora ha veramente guardato a cosa esce da --format, e visto che sei il primo che ci guarda seriamente, hai l'onore di vincere facile e trovare un sacco di magagne.

Il codice si può sicuramente migliorare (c'è da capire come), anche se non si arriverà mai allo stesso livello di quello che dice eccodes, a meno di non mettersi a reimplementare l'interprete delle sue definition, che mi sembra fuori scope.

Il lavoro su questo fronte è duplice: sia implementare quello che manca, sia delineare i compromessi giusti su cosa non implementare per contenere la complessità del codice di formattazione.

spanezz commented 1 year ago

Detto questo, possiamo progettare i passi successivi per migliorare la cosa, almeno sui dati che abbiamo

dcesari commented 1 year ago

Grazie, si ricordavo di questo vecchio problema, che eccodes non espone le API per fare il parsing delle definizioni, poi pensavo che in qualche modo fosse stata trovata una soluzione senza rifare il parsing in casa, mentre evidentemente non è così.

La stranezza n.0, se non sbaglio, si risolverebbe con un'istruzione break dopo questa riga https://github.com/ARPA-SIMC/arkimet/blob/0af4e0861dd497ca37a22739819e53a8d97beb53/python/arkimet/formatter/eccodes.py#L65 perché eccodes quando cerca un file in GRIB_DEFINITION_PATH si ferma al primo che trova, non prosegue (prima metti le definitions specifiche, poi quelle generiche).

Questo comunque non risolverebbe il grosso del problema perché questo gioco delle tabelle esplicite vale solo per un sottoinsieme di tabelle locali (per intenderci, in grib1 quelle tipo grib1/2.[centre].[table2VersionNumber].table a cui si potrebbe estendere il parsing ser già non è così. Purtroppo però, buona parte di quelle che interessano a noi sono definite in una maniera diversa tramite le tabelle in localConcepts/[centre]/... e qui concordo sul fatto che non ha senso mettersi a reimplementare il parsing e la definizione dei localconcepts, se anche fossero documentati in qualche modo.

L'unica cosa che mi viene in mente è tenersi in memoria dei mini-gribtemplate da riempire con i metadati di product (origin, tableversion, indicatorofparameter, discipline, etc.) e poi usarli per chiedere ad eccodes le chiavi descrittive dei parametri. Non so quanto sia (in)efficiente un approccio del genere. Però anche questo non risolverebbe completamente perché diverse variabili in localConcepts (v. ad es. `grib1/localConcepts/edzw/name.def) fanno match solo se fa match contemporaneamente parametro e livello, che per noi sono metadati indipendenti, e quindi il risultato sarebbe parziale.

Insomma, approfondendo, vedo che non c'è una soluzione banale e forse neanche una non banale, ma magari la discussione ti stimola una buona idea...

dcesari commented 1 year ago

Ripensandoci, non sarebbe un gran lavoro (da parte nostra) per grib1 ricreare a mano le tabelle locali esplicite grib1/2.[centre].[table2VersionNumber].table per i parametri che ci interessano, se è possibile fare il parsing anche di quelle in eccodes.py.

Per grib2 bisogna vedere meglio, perché mi pare che non stia funzionando neanche con i parametri standard.

spanezz commented 1 year ago

Intanto ho aggiunto il break che hai suggerito.

spanezz commented 1 year ago

diverse variabili in localConcepts (v. ad es. `grib1/localConcepts/edzw/name.def) fanno match solo se fa match contemporaneamente parametro e livello, che per noi sono metadati indipendenti, e quindi il risultato sarebbe parziale

Ok, parliamo di questo. In teoria, ogni metadato in arkimet dovrebbe essere univocamente definito a prescindere dai valori degli altri metadati, che è il motivo, per esempio, per cui il prodotto riporta dentro l'identificatore del centro di emissione.

Se ho capito bene, ci sono casi per cui questa cosa non è piú vera, perché ci sono parametri che cambiano nome a seconda dei livelli, a parità di centro, categoria, disciplina, numero, dati della tabella.

È una situazione per cui se voglio il prodotto X non posso piú fare match solo sul product: ma devo sempre specificare anche il livello?

A parte dare una possibile ambiguità per un prodotto se si presenta senza il livello associato, questo almeno non dovrebbe dare problemi in fase di identificazione di dati duplicati, perché se un dato valore di un prodotto potrebbe identificare due dati diversi, questi sono comunque sempre accompagnati da un livello che li discrimina, e finché la definizione unique del dataset indica sia prodotto che livello, non c'è il rischio di collisioni

È il caso di rilassare l'idea che ogni elemento dei metadati sia completamente autocontenuto?

Questo potrebbe portare a un po' di cose interessanti:

Non so se questo sia sufficiente a riempire abbastanza l'header di un grib costruito da zero, in modo da poter chiamare la grib_api e farsi dire i nomíni per le varie cose (non so quanto efficiente possa essere una cosa del genere, ma si può provare a metterci su un po' di caching).

Oppure si può arrivare fin dove si arriva parsando le definition senza perderci la testa, e compensare il resto con delle tabelle hardcoded nei file python del formatter, che mi sembra un buon compromesso. Però in tutti i modi mi sembra che questo non arrivi un gran ché in là, almeno finché il formatter non possa riuscire a vedere tutto il metadato.

Sto pensando anche che ci sono situazioni in cui il formatter non può proprio avere a disposizione tutto il metadato: per esempio arki-query --summary-short --annotate perde l'informazione su quali elementi dei metadati siano associati a quali altri

Il caso si complica: organizziamo una piccola call?

dcesari commented 1 year ago

Volentieri, ci risentiamo tra una settimana circa (magari ricordamenlo), che ora sono in affanno.

spanezz commented 1 year ago

For the medium term plan, I opened #306 and #307.

For the short term, I propose to maintain a file in /etc/arkimet/format/ with hand-crafted entries for the cases where the current formatter falls short.

Would you like an example of such a script?

dcesari commented 1 year ago

Yes, please!

spanezz commented 6 months ago

Ok, prova a mettere in /etc/arkimet/format un file prova.py con questo:

from arkimet.formatter import Formatter

def format_level(v):
    # Gestisci alcuni livelli in modo speciale
    if v["style"] == "GRIB1":
        type, l1, l2 = v["level_type"], v.get("l1"), v.get("l2")
        if type == 1:
            return "Tutti giú per terra!"

    # Lascia il comportamento di default
    return None

Formatter.register("level", format_level)

Cercando di formattare un GRIB1 con livello al suolo dovrebbe venire "Tutti giú per terra!" invece del solito valore, e gli altri livelli restano immutati.

Lo script viene eseguito una volta sola, e solo la funzione format_level viene chiamata per ogni livello. Questo vuol dire che fuori dalla format_level si possono caricare e parsare file (tipo dalla eccodes) e il caricamento non avviene per ogni livello. Oppure si può caricare la prima volta che viene formattato un livello, e poi tenere in cache (usando per esempio functools.cache se si può usare python 3.9).

L'esempio è molto basilare: se serve qualcosa di piú specifico datemi un'idea di cosa deve fare e lo raffino

dcesari commented 6 months ago

Grazie, credo di aver capito, sto preparando il formattatore per le variabili che mancano, ci riaggiorniamo.

dcesari commented 3 months ago

Ho finalmente creato in https://github.com/ARPA-SIMC/arkimet/tree/master/conf/format un formattatore per le variabili locali dei modelli cosmo, consiste di qualche riga di formattazione e di un grosso dizionario generato da codice fortran, che a sua volta include pezzi del modello stesso. Testato su un file grib, funziona. Se va bene se ne potrebbe attivare l'installazione nel pacchetto.

Se avete qualche idea di un angolino in cui conservare anche il programma fortran, da rilanciare alla bisogna, fatemi sapere.

edigiacomo commented 3 months ago

Ho aggiunto il file all'installazione, sia in autotools che in meson (anche se noi usiamo solo il secondo).