Ez a módszer alapjaiban kissé különbözik a többitől. Az előzőleg leírt globális metódusok, mint domain átváltás, kivágás, stb. itt is működnek, de másképpen kell kezelni őket. Megjegyezném, hogy mivel ez a módszer interaktív elemet tartalmaz, az egyelőre csak Jupyter Notebook-ban stabil.
import numpy as np
import matplotlib.pyplot as plt
import pysprint as ps
Példaként a korábban már bemutatott ps.Generator
segítségével generálni fogok egy sorozat interferogramot, majd azon bemutatom a kiértékelés menetét. Valós méréseknél teljesen hasonlóképpen végezhető a kiértékelés. A legegyszerűbb módszer, hogy különböző karok közti időbeli késleltetésnél generáljunk és elmentsük azokat az alábbi cellában látható. A megkülönböztethetőség miatt minden fájlt a hozzá tartozó karok közti időbeli késleltetésnek megfelelően nevezem el.
for delay in range(-200, 201, 50):
g = ps.Generator(1, 3, 2, delay, GDD=400, TOD=-500, normalize=True)
g.generate_freq()
np.savetxt(f'{delay}.txt', np.transpose([g.x, g.y]), delimiter=',')
A kód lefuttatásával a munkafüzet környtárában megjelent 7 új txt fájl.
Ehhez a kiértékelési módszerhez először fel kell építeni egy listát a felhasználandó interferogramok fájlneveivel. Ezt manuálisan is megtehetjük, itt ezt elkerülve egy rövidítést fogok használni.
ifg_files = [f"{delay}.txt" for delay in range(-200, 201, 50)]
print(ifg_files)
['-200.txt', '-150.txt', '-100.txt', '-50.txt', '0.txt', '50.txt', '100.txt', '150.txt', '200.txt']
Ha nem hasonló sémára épülnek a felhasználandó fájlok nevei, akkor természetesen a fenti trükk nem működik és egyenként kell beírnunk őket. Miután definiáltuk a fájlneveket a következő lépés a
ps.SPPMethod(ifg_names, sam_names=None, ref_names=None, **kwargs)
meghívása:
myspp = ps.SPPMethod(ifg_files, decimal=".", sep=",", skiprows=0, meta_len=0)
A **kwargs
keyword argumentumok itt elfogadják a korábban már bemutatott parse_raw
funkció argumentumait (a kódban belül azt is hívja meg egyesével minden interferogramon), hiszen a fájlok sémáját itt is fontos megadni a helyes betöltéshez. A tárgy- és referencianyaláb spektrumai természetesen opcionális argumentumok, mi dönthetjük el, hogy normáljuk-e az interferogramokat.
Az SPPMethod
objektum először ellenőrzi, hogy a listában lévő fájlnevek valóban léteznek-e, és ha nem, akkor hibával tér vissza. Az SPPMethod
-nak vannak további metódusai, ilyen pl. a len(..)
, vagy az SPPMethod.info
. Az első visszaadja, hogy hány interferogram van jelenleg az objektumban (ez jelen esetben 9), a második pedig a kiértékelés során mutatja majd, hogy hány interferogramból rögzítettünk információt (ez jelenleg 0/9). Később talán append
(ilyen már van a 0.12.5 verziótól), insert
és delete
metódusokat is beépítek.
print(len(myspp))
9
print(myspp.info)
Progress: 0/9
Az SPPMethod objektum listaszerűen viselkedik: lehet indexelni is. Mivel benne 9 darab interferogram van, ezért egy ilyen indexelés egy ps.Dataset
objektumot ad vissza. Ez az alapja minden kiértékelési módszernek, így ez ismeri a korábban bemutatott metódusokat. Tegyük fel, hogy a 3. interferogram adatait ki szeretnénk iratni, és szeretnénk megkapni az y értékeit np.ndarray
-ként. Ekkor a 2 indexet használva (mivel itt is 0-tól indul a számozás):
# a harmadik interferogram adatainak kiíratása
print(myspp[2])
Dataset ---------- Parameters ---------- Datapoints: 12559 Predicted domain: frequency Range: from 0.99998 to 3.00000 PHz Normalized: False Delay value: Not given SPP position(s): Not given ---------------------------- Metadata extracted from file ---------------------------- {}
# a harmadik interferogram y értékeinek kinyerése, mint np.array
y_ertekek = myspp[2].data.y.values
print(y_ertekek)
print(type(y_ertekek))
[4.22567237e-08 4.23424876e-08 4.24102757e-08 ... 4.41394357e-08 4.30227521e-08 4.18888151e-08] <class 'numpy.ndarray'>
Újra hangsúlyozom, minden eddig bemutatott metódus ezeken a kvázi listaelemeken is működik, köztük a chdomain
, vagy slice
is. Ezt használjuk ki a kiértékeléshez egy for ciklusban. A kiértékeléshez a definiált SPPMethod
-on meg kell hívni egy for ciklust. Ez végigfut a benne lévő összes interferogramon. Azt, hogy mit akarunk csinálni adott interferogrammal, azt a cikluson belül tudjuk megadni. Az alapvető séma a következő:
for ifg in myspp: - előfeldolgozása az adott interferogramnak - az interaktív SPP Panel megnyitása és adatok rögzítése - a calculate metódus meghívása a cikluson kívül(!)
Ez kód formájában az alábbi cellában látható. Itt külön jelöltem, hogy melyik rész meddig tart.
# az interaktív számításokat fontos a with blokkon belülre írni
with ps.interactive():
for ifg in myspp:
# -----------------------------------Előfeldolgozás-----------------------------------------
# Ha valós mérésünk van, érdemes valamilyen módon kiíratni a kommentet,
# ami az interferogram fájlban van, hogy meg tudjuk állapítani milyen késleltetésnél készült.
# Jelen esetben ennek nincs értelme, mivel a szimulált fájlokkal dolgozom.
# Ezt legegyszerűbben az alábbi sorral tehetnénk meg:
# print(ifg.meta['comment'])
# vagy esetleg a teljes metaadatok kiíratása:
# print(ifg.meta)
# Ha hullámhossztartományban vagyunk, először át kell váltani.
# Én frekvenciatartományban szimuláltam, ezért itt kihagyom. Ha szükség van rá a
# következő sort kell használni.
# ifg.chdomain()
# Pl. 1.2 PHz alatti körfrekvenciaértékek kivágása. Mivel nem adtam meg stop értéket, így a felső
# határt érintetlenül hagyná, ha futtatnám. Nyilván ez is opcionális.
# ifg.slice(start=1.2)
# -----------------------------Az interaktív panel megnyitása-------------------------------
ifg.open_SPP_panel()
# ---------------------------------A ciklus utáni rész------------------------------------------
# A cikluson kívül a save_data metódus meghívása, hogy elmentsük a beírt adatainkat fájlba is.
# Ez természetesen opcionális, de annak érdekében, hogy biztosan ne veszítsünk adatot érdemes ezt is elvégezni.
myspp.save_data('spp.txt')
# a cikluson kívül meghívjuk a calculate függvényt
myspp.calculate(reference_point=2, order=3);
A magyarázatok nélkül szimulált esetben az egész kód az alábbi, összesen 8 sorra egyszerűsödik. Valós mérés esetén néhány előfeldolgozási lépés és kiíratás természetesen még hozzáadódhat ehhez.
import pysprint as ps
ifg_files = [f"{delay}.txt" for delay in range(-200, 201, 50)]
s = ps.SPPMethod(ifg_files, decimal=".", sep=",", skiprows=0, meta_len=0)
with ps.interactive():
for ifg in s:
ifg.open_SPP_panel()
s.save_data('spp.txt')
s.calculate(reference_point=2, order=2, show_graph=True)
Miután a számolást már elvégezte a program, akkor elérhetővé válik rajta a GD
property. Ez az illesztett görbét reprezentálja, típusa ps.core.phase.Phase
. Bővebben erről a Phase
leírásában.
myspp.GD
<pysprint.core.phase.Phase at 0x24048f0d908>
Mivel az spp.txt
fájlba elmentettük az bevitt adatokat, azokból egyszerűen lehet újraszámolni az illesztést. Töltsük be np.loadtxt
segítségével, majd használjuk a ps.SPPMethod.calculate_from_raw
függvényt.
delay, position = np.loadtxt('spp.txt', delimiter=',', unpack=True)
myspp.calculate_from_raw(delay, position, reference_point=2, order=3);
Az előbbi esetben látható, hogy ugyan azt az eredményt kaptuk, mint előzőleg. Ez akkor is hasznos lehet, ha már megvannak a leolvasott SPP pozícióink a hozzá tartozó késleltetésekkel és csak a számolást akarjuk elvégezni. Ekkor még létre sem kell hozni egy új objektumot, csak meghívhatjuk a függvényt következő módon:
# ehhez beírtam egy teljesen véletlenszerű adatsort
delay_minta = [-100, 200, 500, 700, 900]
position_minta = [2, 2.1, 2.3, 2.45, 2.6]
ps.SPPMethod.calculate_from_raw(delay_minta, position_minta, reference_point=2, order=3);
FONTOS MEGJEGYZÉS:
Az order
argumentum a program során mindig a keresett diszperzió rendjét adja meg.
Mivel továbbra is ugyan ezekkel az adatsorokkal és a myspp
objektummal dolgozom, most törlöm az összes rögzített adatot belőlük. Ehhez a SPPMethod.flush
függvényt használom. (Valószínűleg ez a felhasználónak kevésszer szükséges, de elérhető.)
myspp.flush()
Korábban már észrevehettük, hogy a kiíratás során - legyen bármilyen módszerről is szó - megjelentek olyan sorok is, hogy Delay value: Not given
és SPP position(s): Not given
. Például a myspp
első interferogramja esetén most ez a helyzet:
print(myspp[0])
Dataset ---------- Parameters ---------- Datapoints: 12559 Predicted domain: frequency Range: from 0.99998 to 3.00000 PHz Normalized: False Delay value: Not given SPP position(s): Not given ---------------------------- Metadata extracted from file ---------------------------- {}
Ahogyan a Dataset
leírásában már szerepelt, lehetőségünk van megadni a betöltött interferogramokon az SPP módszerhez szükséges adatokat. Ekkor a ps.SPPMethod.calculate_from_ifg(ifgs, reference_point, order)
függvénnyel kiértékelhetjük a benne lévő interferogramokat a következő módon:
# kicsomagolok öt interferogramot a generált 7 közül
elso_ifg = myspp[0]
masodik_ifg = myspp[1]
harmadik_ifg = myspp[2]
negyedik_ifg = myspp[3]
otodik_ifg = myspp[4]
# beállítok rájuk véletlenszerűen SPP adatokat
elso_ifg.delay = 0
elso_ifg.positions = 2
masodik_ifg.delay = 100
masodik_ifg.positions = 2
harmadik_ifg.delay = 150
harmadik_ifg.positions = 1.6
negyedik_ifg.delay = 200
negyedik_ifg.positions = 1.2
otodik_ifg.delay = 250
otodik_ifg.positions = 1, 3, 1.2
# listába teszem őket
ifgs = [elso_ifg, masodik_ifg, harmadik_ifg, negyedik_ifg, otodik_ifg]
# meghívom a calculate_from_ifg függvényt
ps.SPPMethod.calculate_from_ifg(ifgs, reference_point=2, order=3);
Ez úgy lehet hasznos, hogy amikor más módszerrel több interferogramot is kiértékelünk egymás után, csak rögzítjük az SPP adatokat is, aztán a program ezekből egyenként összegyűjti a szükséges információt a kiértékeléshez, majd abból számol.
Az SPPMethod
alapvető működését az adatok rögzítése közben az alábbi ábra mutatja.
A hurok az SPPMethod
-ból indul, ahol a használandó fájlok neveit, betöltési adatokat, stb. adunk meg. Ezen a ponton még semmilyen számolás és betöltés nem történik. Ezután az SPPMethod
bármely elemének hívására egy Dataset
objektum jön létre. Ezen megnyitható az SPPEditor
, amiben az állandó fázisú pont(ok) helyét és a karok közti késleltetést lehet megadni. Hitelesítés után az SPP-vel kapcsolatos információk az interaktív szerkesztőből visszakerülnek a létrehozott Dataset
objektumba és ott rögzítődnek. Minden így létrejött Dataset
objektum kapcsolva van az SPPMethod
-hoz, amiből felépült, így amikor megváltozik egy SPP-vel kapcsolatos adat, az egyből megváltozik az SPPMetod
-ban is. A Registry
gondoskodik arról, hogy minden objektum ami a memóriában van az rögzítődjön, illetve szükség esetén elérhető legyen.
Cache
Ha próbálunk elérni egy adott elemet (akár a for
ciklussal, akár indexelve, vagy egyéb módon), létrejön egy Dataset
objektum. Ez a Dataset
objektum miután már egyszer elértük a memóriában marad és megtart minden rajta végrehajtott változtatást, beállítást. Alapértelmezetten 128 db interferogram marad a memóriában egyszerre, de ez a határ szükség esetén megváltoztatható. Az éppen aktuálisan a memóriában lévő interferogramok száma (az adott SPPMethod
-hoz tartozó) a kiíratás során a Interferograms cached
cellában látható.
Callbacks
A fenti ábrán a ciklus utolsó lépése során (ahol a Dataset
átadja az SPP-vel kapcsolatos adatait a SPPMethod
-nak) lehetőség van további ún. callback függvények meghívására. Egy ilyen beépített callback függvény a pysprint.eager_executor
. Ez arra használható, hogy minden egyes SPP-vel kapcsolatos adat rögzítése/változtatása után a program azonnal kiszámolja az éppen meglévő adatokból a diszperziót. A korábbiakhoz teljesen hasonlóan kell eljárnunk, csupán a callback
argumentumot kell megadnunk kiegészítésként. Itt a kötelező argumentumokon túl megadtam a logfile
és verbosity
értékeit is: ez minden lépés során a "mylog.log"
fájlba el fogja menteni az adott illesztés eredményeit és egyéb információkat, továbbá a verbosity=1
miatt a rögzített adatsort is. Ezzel akár könnyen nyomon követhető a kiértékelés menete.
# a folyamatos kiértékeléshez szükséges callback függvény importálása
from pysprint import eager_executor
myspp2 = ps.SPPMethod(
ifg_files,
decimal=".",
sep=",",
skiprows=0,
meta_len=0,
callback=eager_executor(reference_point=2, order=3, logfile="mylog.log", verbosity=1)
)
myspp2
SPPMethod | |
Interferograms accumulated | 9 |
Interferograms cached | 0 |
Eagerly calculating | True |
Data recorded from | 0 |
Ekkor láthatjuk, hogy az Eagerly calculating
már True
értékre változik.
Természetesen a program csak értelmes esetekben fogja elvégezni a számolást (pl. szükséges, hogy az adatpontok száma nagyobb legyen, mint az illesztés rendje ). A teljesség kedvéért megemlítendő, hogy könnyen írható akár saját callback függvény is. Futtassuk le az újonnan létrehozott myspp2
-n a már megismert for ciklust:
with ps.interactive():
for ifg in myspp2:
ifg.open_SPP_panel()
A fenti cella futtatása közben - miután rögzítettünk elég adatot - megjelentek az eredmények, és minden új adatpont hozzáadása esetén frissültek is. Az adatok rögzítését itt ugyan az interaktív felületet használva végeztem, de akár kódban is megtehető: a myspp
elemein kell az delay
és positions
argumentumokat beállítani, és minden új adat hozzáadásánál újra fogja számolni a program. Az előző számolásom közben készült logfile a következő:
!type mylog.log
--------------------------------------------------------------------------------------- Date: 2020-11-06 12:30:58.626033 Datapoints used: 3 R^2: 1.00000 Results: GD = 0.00000 fs^1 GDD = -404.77112 fs^2 TOD = 474.20243 fs^3 Values used: x: [2.29964 2.13404 1.99999] y: [-100. -50. 0.] --------------------------------------------------------------------------------------- Date: 2020-11-06 12:31:05.392053 Datapoints used: 4 R^2: 0.99995 Results: GD = -0.57803 fs^1 GDD = -391.43064 fs^2 TOD = 395.01166 fs^3 Values used: x: [2.29964 2.13404 1.99999 1.87777] y: [-100. -50. 0. 50.] --------------------------------------------------------------------------------------- Date: 2020-11-06 12:31:12.187050 Datapoints used: 5 R^2: 0.99919 Results: GD = 0.17702 fs^1 GDD = -369.04341 fs^2 TOD = 206.88147 fs^3 Values used: x: [2.29964 2.13404 1.99999 1.87777 1.74372] y: [-100. -50. 0. 50. 100.] --------------------------------------------------------------------------------------- Date: 2020-11-06 12:31:16.542842 Datapoints used: 6 R^2: 0.99597 Results: GD = -2.18052 fs^1 GDD = -386.71039 fs^2 TOD = 419.67620 fs^3 Values used: x: [2.29964 2.13404 1.99999 1.87777 1.74372 1.68063] y: [-100. -50. 0. 50. 100. 150.] --------------------------------------------------------------------------------------- Date: 2020-11-06 12:31:18.408715 Datapoints used: 6 R^2: 0.99597 Results: GD = -2.18052 fs^1 GDD = -386.71039 fs^2 TOD = 419.67620 fs^3 Values used: x: [2.29964 2.13404 1.99999 1.87777 1.74372 1.68063] y: [-100. -50. 0. 50. 100. 150.] --------------------------------------------------------------------------------------- Date: 2020-11-30 11:31:07.047433 Datapoints used: 3 R^2: 1.00000 Results: GD = 0.00000 fs^1 GDD = -379.90924 fs^2 TOD = 254.74825 fs^3 Values used: x: [2.29175 2.13799 1.99999] y: [-100. -50. 0.] --------------------------------------------------------------------------------------- Date: 2020-11-30 11:31:11.768879 Datapoints used: 4 R^2: 0.99972 Results: GD = 1.35592 fs^1 GDD = -411.13105 fs^2 TOD = 444.77475 fs^3 Values used: x: [2.29175 2.13799 1.99999 1.8896 ] y: [-100. -50. 0. 50.] --------------------------------------------------------------------------------------- Date: 2020-11-30 11:31:15.360363 Datapoints used: 5 R^2: 0.99727 Results: GD = 2.91855 fs^1 GDD = -365.00138 fs^2 TOD = 43.72692 fs^3 Values used: x: [2.29175 2.13799 1.99999 1.8896 1.73189] y: [-100. -50. 0. 50. 100.] --------------------------------------------------------------------------------------- Date: 2020-11-30 11:31:20.184841 Datapoints used: 6 R^2: 0.98970 Results: GD = 0.15004 fs^1 GDD = -389.76997 fs^2 TOD = 334.26674 fs^3 Values used: x: [2.29175 2.13799 1.99999 1.8896 1.73189 1.68852] y: [-100. -50. 0. 50. 100. 150.] --------------------------------------------------------------------------------------- Date: 2020-11-30 11:31:20.666243 Datapoints used: 6 R^2: 0.98970 Results: GD = 0.15004 fs^1 GDD = -389.76997 fs^2 TOD = 334.26674 fs^3 Values used: x: [2.29175 2.13799 1.99999 1.8896 1.73189 1.68852] y: [-100. -50. 0. 50. 100. 150.]