From 962a59ef08cc981261c1faa3eb35466fd935ea80 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 17 Oct 2024 11:33:02 +0100 Subject: [PATCH] Add scalars as variables instead of attributes Fixes #17 --- src/sdf_xarray/__init__.py | 9 ++++++--- src/sdf_xarray/sdf_interface.pyx | 19 +++++++++++++++++-- tests/example_files/0000.sdf | Bin 176380 -> 176956 bytes tests/example_files/0001.sdf | Bin 94964 -> 95540 bytes tests/example_files/0002.sdf | Bin 94964 -> 95540 bytes tests/example_files/0003.sdf | Bin 94964 -> 95540 bytes tests/example_files/0004.sdf | Bin 94964 -> 95540 bytes tests/example_files/0005.sdf | Bin 94964 -> 95540 bytes tests/example_files/0006.sdf | Bin 94964 -> 95540 bytes tests/example_files/0007.sdf | Bin 94964 -> 95540 bytes tests/example_files/0008.sdf | Bin 94964 -> 95540 bytes tests/example_files/0009.sdf | Bin 94964 -> 95540 bytes tests/example_files/0010.sdf | Bin 304008 -> 304584 bytes tests/example_files/input.deck | 1 + tests/test_basic.py | 12 ++++++++++++ tests/test_cython.py | 4 ++-- 16 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/sdf_xarray/__init__.py b/src/sdf_xarray/__init__.py index 364e5c2..337ec78 100644 --- a/src/sdf_xarray/__init__.py +++ b/src/sdf_xarray/__init__.py @@ -275,9 +275,12 @@ def _grid_species_name(grid_name: str) -> str: continue if isinstance(value, Constant) or value.grid is None: - # No grid, so not physical data, just stick it in as an attribute - # This might have consequences when reading in multiple files? - attrs[key] = value.data + # No grid, so just a scalar + data_attrs = {} + if value.units is not None: + data_attrs["units"] = value.units + + data_vars[key] = Variable((), value.data, data_attrs) continue if value.is_point_data: diff --git a/src/sdf_xarray/sdf_interface.pyx b/src/sdf_xarray/sdf_interface.pyx index fc093be..f5804fd 100644 --- a/src/sdf_xarray/sdf_interface.pyx +++ b/src/sdf_xarray/sdf_interface.pyx @@ -1,6 +1,7 @@ cimport csdf import dataclasses +import re import time from libc.string cimport memcpy @@ -103,11 +104,14 @@ cdef class Mesh(Block): ) +_CONSTANT_UNITS_RE = re.compile(r"(?P.*) \((?P.*)\)$") + @dataclasses.dataclass cdef class Constant: _id: str name: str data: int | str | float + units: str | None @staticmethod cdef Constant from_block(str name, csdf.sdf_block_t* block): @@ -122,8 +126,16 @@ cdef class Constant: if block.datatype == csdf.SDF_DATATYPE_INTEGER8: data = (block.const_value)[0] + # There's no metadata with e.g. units, but there's a + # convention to put one in brackets at the end of the name, + # if so, strip it off to give the name and units + units = None + if match := _CONSTANT_UNITS_RE.match(name): + name = match["name"] + units = match["units"] + return Constant( - _id=block.id.decode("UTF-8"), name=name, data=data + _id=block.id.decode("UTF-8"), name=name, data=data, units=units ) @property @@ -203,7 +215,10 @@ cdef class SDFFile: } elif block.blocktype == csdf.SDF_BLOCKTYPE_CONSTANT: - self.variables[name] = Constant.from_block(name, block) + # We modify the name to remove units, so convert it + # first so we can get the new name + constant = Constant.from_block(name, block) + self.variables[constant.name] = constant elif block.blocktype in ( csdf.SDF_BLOCKTYPE_PLAIN_MESH, diff --git a/tests/example_files/0000.sdf b/tests/example_files/0000.sdf index 9e10636cb6e996286787cad09548333458403824..5806fb4bdace0ce74594b132b26f495454c9ceb2 100644 GIT binary patch delta 813 zcmezKkZaE~t_c=u29uc>K!8Vqfk7EacYruRaPj&3#3RS_Y;_(9q*pU-j9bUZ0b+vi zW)UV0M(zb0#F%uYd+q12FigKah3RUu3`@HV3*&Yf7N#$~vI{0bjD^uTiN&c!@u_(s z`6Y=tlNC8crdLj364r7|D$Xw|D9Oyv(+A5a_<+!K%k*RFbgwB)ZCtQGxiD?|ttm{`m{&|^ znmnK7DwD;G>0Q&9`j}44m@YLPNJq?^z6wMK%$jaBgXtTy$84s_|6RT^In0@^H4Di1 znai~K{Q^!#rigjl_2w{fF*2ph-)=P*#IRW~-EAJzH71dT)8*y^={F0fe*@7+7EM35 z07%bSJbl?hAYHO#dfg%*9kXH;oN0Qw2jfkq11q-2EdzkXOVgtUI_n~IY0{|gi|8M{R delta 390 zcmXwxPbkA-7{`C__xC$6iG!ppO^FrBi6wtZ4$HyGX^C7MII6iUDa9_6%sj^I|I9XK zQeKB82kjsS4#Y@Y<|4Zc#lrh4Pd$CU-%mYHI;Sf>>6T25mCI1%ywoX5y`cTCDaz$@ ze(W^U+HG8pj@K&tBS}^wquhfk7+KjxL-S<0X`wt!8KD?as`Mk&!YwP?KPEPGWItQG9A%NPbD;bioWpk%`};1L$w5~CJAJTswRU1DnPa`C4T$77{*zQ z{4l>W1D*0Eo^i4?Q!jH%0^{WEOs(9|kg$(QWSs2Ie3jWGiE;9LmaEJ>$&8b~vtDI> z0;K)fuQKmRVVu02<0|u%RL05wIj=J3q%lqo=f27ulFm4JJLnMyLJ zSEV!FV&0L(xcR;-P+&^-^jVoefg3rDo9EX81-9f)-<1s%_>srBIesfp;7b1VTe(01 zi9(lise zOc35I!lc2-9>RP*GC=-jvorH{XJ*F8NG6xK?fYUGXEE}_)G-5%x)R4YS(>SrIVGNP z@^+?HZm2GMn*_$m?#x%2MG_e&&u6*H{02yWXT8dNB#CjdKl@eYImwKZmvdZYE=ggW z{GanGvrj7H#?A3tfdX6drvJ(V3jD|in(-DWaHU|nR3YOnCW)f$ S_f#027@58lO}8p$yafQ6-DXMv diff --git a/tests/example_files/0002.sdf b/tests/example_files/0002.sdf index 04438ee9eb25d015fcc0c8cd70944bee7b5df862..7bc3e0b2c8bcfcf986892a6a8d7807989fcc5898 100644 GIT binary patch delta 717 zcmezJly%E1)(IABdtwSb<8V4S?2sg)ZN5HIax5*a7EGhbykNn)HlpXDkuPcq}=@2ppu zp8#op_N&Z$QWz&M=eWu|C6#gVf6lASIcbcO!?~|Ahom!3UeEKCc}51~W_dnGMy8U? z=~d~Bx0rWiF>b!^3KW=V2Srl)-r^4vO$RtuSU8;!jmduh8NPN>WnoLR|cHoI7A8JMu E0H1=?fB*mh delta 292 zcmdn;iuKD=)(IABb7B}7Kp;Ylfk7NdcL1@gAOpkmxmB;f%x19Dn6x}inqgzyIz|o< z6NER5FljKd?RJtXUMbb=#Jt^!nK3Go$t7<4zF5XtjQlWV%s`W_#4%2mX6j{5iD#U= zovD=@qUoi*O#zxm!vRG z{?B=p*(a58aya)@W|K6=$?JKZGFPNCZkFeBWMm4-m_93m@fP!pOvcUkU4a56S<`oA z0R?tsGj5(=3lx}=GyPT$P~b)` Sdn$}hj7(pOrdt&=-U0y3LuH`= diff --git a/tests/example_files/0003.sdf b/tests/example_files/0003.sdf index 9ad70c1ea5554814687a5137ce6cd7917d236a53..30643cbaabd54d2f4ff42cac9c665de257c526ea 100644 GIT binary patch delta 718 zcmezJly%E1)(IABdtwzNf%IyojdAN3IY3NZ zAl@v(q`}C3g6mFCvdiaYXXfqB%#4wdGCEL`V02DmacWU~YFIS&e8 zu7%NwNyYJLMTyBkg%VKX{g8}z14@DHQOHk2wFImt31o??CW8VhK(;R>e*3%_##xN~ zFuyYc9r7ifak4a1FLO%*xLVuQKPPF-{KWzRDbu&Nz8J&r{|Z8H}6d`5YOUN;0Qc zr8C}Q-jT()`MxVqU`qD%S(!kA8##=d=hp%Sw&YIVl?@d5k;k|>ek)MmO8)d)xj+Gl zLZBIMfdXF&r~k@lyv1Zuy#1aEqZ1>ONXc}mBF0-XOG+RSPRnRADTUa9Cz^by8BGA4 CrP6!= delta 293 zcmdn;iuKD=)(IABb7B}7Kp;Ylfk7NdcL4DVNd|`W8qb=nR~&X4la{ARGi;1o$H)O< zY69_Q5he{r_Lq0KFCO0Zso9x%yE8LmWF(VI-1dF3jI$W|Vd|KHMqPqb^y-fn+WOwGP%p!@5ljpNsWqt#szq4LtK9a;Z*`NI?^PFVH$;&yeGMA(< zPX5n%mDwkiadJ5KRc4bk#>wk>o-$XYGj5jWb7W)+$(TMXgYg#gj7-MO_g#SkC0WyV zWdQ|tWHWA_UkenNk~95Q4p87mF5~9-tw4b-dDDO80R?{K1I>5~6u43_U8<1r7L!EL T_IoOfPK-=nil$o?Gu{FKyQ67w diff --git a/tests/example_files/0004.sdf b/tests/example_files/0004.sdf index d80f4df3f34f457904aad0253a3cea68a7d1cc4a..c52ac102f0e8f34f3b214d6089f7226455d70a8d 100644 GIT binary patch delta 717 zcmezJly%E1)(IABdtwu7Ioc4FS{#LO5KDWd~52u9~57N-`)r{;y^mn2RX%wQCmxPW8w z8zvqt$E4!?qJoml{5*Y-T#kYdNTq^nUTRT#rGjT(R%&udYKnq}m*!+fW-%2Gkn5lT z=2#e=m{c5}R+N|wR44&8-Ve!mH=q>A9)VVuRt z5A!-R&>>&q87E6K^)k04Fizgi)XEJBh@bW`iHwuonXfXNBr#5&&vKQSCz)~bch;-S zPk^*P`&H&WDU6esb6jPflFB&wKj&5EoHWMC;oMi5L(&;1ujhHnJR^f~vpk<8BU4G{ z^s02mTg*GM7&qT{1qw{bo<1uRC~zZ(ar69Ipum>g>ASLl0zdK?H^*-U3S7ybek&I! zAW;Z3<1J9&OX2ii`HZ)iEQ+_^Q(<&sWD+TvE>*;MOJ+$4B)(}GO(vxfJMcu44>h9+ E088P}&;S4c delta 292 zcmdn;iuKD=)(IABb7B}7Kp;Ylfk7NdcK~sh5(C5LrB$!L%oecIn6x}inqgzyIz|o< zQxk|ci!f<0vMt)*?f*2W*@=0(6EkB}B$G?r_IA%JY}v(XWT5$=g7zuk}-W&2IDQ}8JUcm@4Es8O0uT! z$^r`P$Y$I;zZNJkC1?7r9H79BT*l4uTY&;w@}~dF0}A}e2b%E~C~&1O12qap=Oh-V7R9ILh2)ncP8ZBz6q&ey zWAYm&9xcbD;{2k5lFa-(eUMy^f)7Zgf@@xCQF^6b!&x>K4 z#mEoyJTuTCU*Z`jOEdK{wSa!e zXPms9sg)b1%_f0yvODutW|2h3$@5vRGQR=R-&wCRA4y`I?9YCcc}_Cp#MkeFt`>sHNlC0^w zvVa0RvKcqeuLTNB$(epD2PkkOmvM9aR-nL^yy?I4fC4}Afo8k~3S23eE>*~Qi%Ftr T`#lv#Cq||(MboW{8E*jq@y}-m diff --git a/tests/example_files/0006.sdf b/tests/example_files/0006.sdf index e2d18748babcf4d21aae1f459c705ffc5c593522..aaf9b82a1b10067663779b67856bce79fedf5c3b 100644 GIT binary patch delta 717 zcmezJly%E1)(IABdtwA9)VVuRt z5A!-R&>>&q87E6K^)k04Fizgi)XEJB2xfwk>o-$XYGj5jWb7W)+$(TMXgYg#gj7-MO_g#SkC0WyV zWdQ|tWHWA_UkenNk~95Q4p87mF5~9-tw4b-dDDO80R?{K1I>5~6u43_U8<1r7L!EL T_IoOfPK-=nil$o?Gu{FKe2Hh9 diff --git a/tests/example_files/0007.sdf b/tests/example_files/0007.sdf index 4f0d5e330bcb21f01cdf7f28cb88fee83643401d..ff3279ceb11f62c923c2a26bf34318de29d89671 100644 GIT binary patch delta 717 zcmezJly%E1)(IABdtwblk3xPLswH4GNgzv9H5n980kVB5@!RLcFwSD+ zhk2bD=#Ve*jFY9AdYM}i7$v^6s&&XihEYIi2$W)R! zy(*pY7W0lQ#?AL#fdW&qr_ag+3f#zH+&sS)D6l1W`mSuCz>hq}&GB1-0$1{<-^v9F zNE8CicncKxQaJrrKI1JWi{kC~R2ZEYnM6vaOBFHRl37v$iEmm)lSwJW4m{E1L(OOc E0G{H}W&i*H delta 291 zcmdn;iuKD=)(IABb7B}7Kp;Ylfk7NdcK~sb4FkjG?D?8ng0glRla{ARGi;1o$H)O< zY60b8ZeP1l&EJl8qGG?GjSK=5aOEdK{r^GW( z-p$Yx=G% zpumo7#?A9j>O8E=6CR|=*}6*Aspk|^4K RPleHmk?Bj(bgN>Sb<8V4S?2sg)ZN5S$J%iHwuonXfXNBr#5&&vKQSCz)~bch;-S zPk^*P`&H&WDU6esb6jPflFB&wKj&5EoHWMC;oMi5L(&;1ujhHnJR^f~vpk<8BU4G{ z^s02mTg*GM7&qT{1qw{bo<1uRC~zZ(ar69Ipum>g>ASLl0zdK?H^*-U3S7ybek&I! zAW;Z3<1J9&OX2ii`HZ)iEQ+_^Q(<&sWD+TvE>*;MOJ+$4B)(}GO(vxfJMcu44>h9+ E00TMFhyVZp delta 292 zcmdn;iuKD=)(IABb7B}7Kp;Ylfk7NdcL1@7D+9yKl~u35%vP||n6x}inqgzyIz|o< zQyYjki!f<0vi(|X@mHy?*@=0(6EkB}B$G?r_Iwk>o-$XYGj5jWb7W)+$(TMXgYg#gj7-MO_g#SkC0WyV zWdQ|tWHWA_UkenNk~95Q4p87mF5~9-tw4b-dDDO80R?{K1I>5~6u43_U8<1r7L!EL T_IoOfPK-=nil$o?Gu{FK^Py)4 diff --git a/tests/example_files/0009.sdf b/tests/example_files/0009.sdf index 8305c3f4ee2a1ba9e022cf04ddb847dbf8693124..16010a81e2a76fff8f5a25d8a3376bd4b68b6fc6 100644 GIT binary patch delta 718 zcmezJly%E1)(IABdtw_Oj~PBbO+HajzKcV=dcjFi!VngpYB5{pxd;#2cN@=Fq@3uZ8iOkBV* z`3)0~mSa+Jeo;Y5W`3SNNG?ag2c%NLH7~U&y;8w5FDo^ycFq#W2od zv^6s&&XihEYIi2$W)R! zy(*pY7W0lQ#?AL#fdW&qr_ag+3f#zH+&sS)D6l1W`mSuCz>hq}&GB1-0$1{<-^v9F zNE8CicncKxQaJrrKI1JWi{kC~R2ZEYnM6vaOBFHRl37v$iEvs*lSwJW4m{E1L(OOc E0ABCW-T(jq delta 293 zcmdn;iuKD=)(IABb7B}7Kp;Ylfk7NdcL4ErUj_!t#C86Y)+^a*Oj@2M&9E_U9U}*b zsSU)NMVK@g*$p^19aEam)9lQ=-Il^d$d!6t!mvODutW|2h3$@5vRGQR=R-&wCRA4y`I?9YCcc}_Cp#MkeFt`>sHNlC0^w zvVa0RvKcqeuLTNB$(epD2PkkOmvM9aR-nL^yy?I4fC4}Afo8k~3S23eE>*~Qi%Ftr T`#lv#Cq||(MboW{8E*jq$`WK# diff --git a/tests/example_files/0010.sdf b/tests/example_files/0010.sdf index fc8c7228a47462fb95b399bc67892a7327d35fe9..05bfcae1cca1b22415604c24bbb436af5373f779 100644 GIT binary patch delta 999 zcmcJNTS!z<6o%*Qy)tj*U}l1r^I$SwK%=bqJt_YTc!G*l@s~pVZ@f$_v+$Gb~ATG@>Z7dRJVheR$94P2lKsY z>iSe}PvsI7qvCQZhnbSFJI3Fs9vbp3dUKxMm}Ro&i_cy`;+a=abV`B6WG=NHH5ua+ zZ_s2YyEHqDdUJ7wW|y%<79U)5SrawZ{ z$Sdg@Q_EHbS=);JGAM{>gC=q0GtEW)L!K@LRs})&c5w zawm_sfOtuKKr99)-(v;wjrfjeU*Y5i8;EsYjcn0|IxDoYmJ>LoFr*BPHfZ8QWgs%W zojkD|#9QJh(d6Ug`zt{FAWjo+_%`x;CqX3pxmaf<_Eo?OZm0tBX_bp@KZUwV>cgr* z^s8NLp&FN}s2@K~xocc(um(q}sjsS~ytOXoUx&Fhbbe1A&MAzo$Ld;Q#2H*&2VZ$u z1BkqAHw!w8OZC+I+bQRkn}Gx88mM1%04A@O+-#u{r|cv?HUY*tNK7~5lSUG6S^&eR zNj!Jr{bmy5t$+cU%*|f4$s~@zHaDF~Qu$1_Taw(7@%L zfUiSH?CHV-f`s-wU~?!56{2q!iCF|p+Q4Uc>;;goh4U1Ndv^o2MeqrpaS`NW`xK?^ z0n9u|%FqjPOq-(8`)Hs}QZ4-;e|V&*BLjd>$M^)hen}o);@c$VxQwkFCfWEExg-Hd X3=N_#2`0l5ul}cZJ-QtHkKgrAADmSr delta 645 zcmXwzT}YE*6vy{I=T!3}C-x6AmvqsiR)vtvZd3x_B!`& zwr6_8Osy`GlEMn!EjPwUUZ@8Szw`gWbIzHV%u`?Fb(I!6 z8qj3C+MOm-IaB4Tt~8l`AFeL^_RPN95x;%hY8pLyPca*9G1tTO!lA6$vbCyqbj%W{ zfKUY}T!IVq3c$rWG40k5y0K5O{Qm>Siqj~qLW8eNT?DiKP1xfo( zLFomQf0*OU`h^Lz-Ye6x}gGb*K37Oy&xcBm}I!}K}K;z=h`!2*j%5qckCF`^+^Tp}eN zNA;;FW-4Mlrq9K=f$1*P3zkHN0>f94iZ()0_~Z04&IccnNbmAllq9Gr!NS~))M;h$ zv75H_u$bsUDzm{NWcQ+ew*dAku+)nbv%@SD_o05O%24KONUj=I&VJO3Vuo^D=YtZg t>IYE&^xRMf29aJ*!mNl4>37nwPZ1|?P!w=ZjNH`gb|DM(79A?XxxfEo>!kny diff --git a/tests/example_files/input.deck b/tests/example_files/input.deck index 56a943e..456ca92 100644 --- a/tests/example_files/input.deck +++ b/tests/example_files/input.deck @@ -98,6 +98,7 @@ begin:output # Extended IO distribution_functions = always + absorption = always end:output diff --git a/tests/test_basic.py b/tests/test_basic.py index 1a1b702..197cb5a 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -24,6 +24,13 @@ def test_basic(): assert x_coord not in df.coords +def test_constant_name_and_units(): + with xr.open_dataset(EXAMPLE_FILES_DIR / "0000.sdf") as df: + name = "Absorption/Total Laser Energy Injected" + assert name in df + assert df[name].units == "J" + + def test_coords(): with xr.open_dataset(EXAMPLE_FILES_DIR / "0010.sdf") as df: px_electron = "dist_fn/x_px/electron" @@ -66,6 +73,10 @@ def test_multiple_files_one_time_dim(): assert sorted(px_protons.coords) == sorted(("X_Particles/proton", "time")) assert px_protons.shape == (11, 1920) + absorption = df["Absorption/Total Laser Energy Injected"] + assert tuple(absorption.coords) == ("time",) + assert absorption.shape == (11,) + def test_multiple_files_multiple_time_dims(): df = open_mfdataset( @@ -77,6 +88,7 @@ def test_multiple_files_multiple_time_dims(): assert df["Electric Field/Ez"].shape == (1, 16) assert df["Particles/Px/proton"].shape == (1, 1920) assert df["Particles/Weight/proton"].shape == (2, 1920) + assert df["Absorption/Total Laser Energy Injected"].shape == (11,) def test_erroring_on_mismatched_jobid_files(): diff --git a/tests/test_cython.py b/tests/test_cython.py index 599e21c..4fde5d3 100644 --- a/tests/test_cython.py +++ b/tests/test_cython.py @@ -17,7 +17,7 @@ def test_sdffile(): assert f.run_info["version"] == "4.19.3" - assert f.variables["Wall-time"].data == 0.0032005560000000002 + assert f.variables["Wall-time"].data == 0.014211990000000008 def test_sdffile_with_more_things(): @@ -29,7 +29,7 @@ def test_sdffile_with_more_things(): assert f.run_info["version"] == "4.19.3" - assert f.variables["Wall-time"].data == 3.968111756 + assert f.variables["Wall-time"].data == 4.068961859 def test_variable_names():