Skip to content

Commit 0a6cc14

Browse files
committed
🚀 New data containers
xtl.common.labels:Label - Refactored into a pydantic.BaseModel xtl.common.data - New pydantic.BaseModels for storing 0D, 1D, 2D and 3D numpy data - Additional validation is performed on the shape of the stored arrays pyproject.toml & requirements.txt - Added pydantic and numpydantic on the list of dependencies
1 parent a0904a0 commit 0a6cc14

File tree

4 files changed

+110
-6
lines changed

4 files changed

+110
-6
lines changed

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ dependencies = [
3838
"hdf5plugin>=4.2.0",
3939
"matplotlib>=3.8.0",
4040
"numpy>=1.26.0",
41+
"numpydantic>=1.6.7",
4142
"pandas>=2.2.3",
4243
"pint>=0.19.2",
44+
"pydantic>=2.10.6",
4345
"pyfai>=2023.9.0",
4446
"pyxray>=1.7.0",
4547
"requests>=2.31.0",

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ gemmi==0.5.8
88
hdf5plugin==4.2.0
99
matplotlib==3.8.0
1010
numpy==1.26.0
11+
numpydantic==1.6.7
1112
pandas==2.2.3
1213
pint==0.19.2
14+
pydantic==2.10.6
1315
pyfai==2023.9.0
1416
pyxray==1.7.0
1517
requests==2.31.0

src/xtl/common/data.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from typing import Optional
2+
from typing_extensions import Self
3+
4+
from numpydantic import NDArray, Shape
5+
from numpydantic.dtype import Number
6+
from pydantic import BaseModel, ConfigDict, model_validator
7+
8+
from xtl.common.labels import Label
9+
10+
11+
class Data0D(BaseModel):
12+
"""
13+
A class to represent 0D data, i.e. one series of values.
14+
"""
15+
model_config = ConfigDict(validate_assignment=True, extra='forbid')
16+
17+
data: NDArray[Shape['*'], Number]
18+
label: Label
19+
units: Optional[Label] = None
20+
21+
22+
class Data1D(BaseModel):
23+
"""
24+
A class to represent 1D data, i.e. y = f(x).
25+
"""
26+
model_config = ConfigDict(validate_assignment=True, extra='forbid')
27+
28+
x: Data0D
29+
y: Data0D
30+
31+
@model_validator(mode='after')
32+
def check_array_shapes(self) -> Self:
33+
if self.x.data.shape != self.y.data.shape:
34+
raise ValueError('x and y shapes must be equal')
35+
return self
36+
37+
38+
class Data2D(BaseModel):
39+
"""
40+
A class to represent 2D data, i.e. z = f(x, y).
41+
42+
"""
43+
model_config = ConfigDict(validate_assignment=True, extra='forbid')
44+
45+
x: Data0D
46+
y: Data0D
47+
z: Data0D
48+
49+
@model_validator(mode='after')
50+
def check_array_shapes(self) -> Self:
51+
if self.x.data.shape != self.y.data.shape:
52+
raise ValueError('x and y shapes must be equal')
53+
if self.x.data.shape != self.z.data.shape:
54+
raise ValueError('x and z shapes must be equal')
55+
return self
56+
57+
58+
class Data3D(BaseModel):
59+
"""
60+
A class to represent 3D data, i.e. w = f(x, y, z).
61+
"""
62+
model_config = ConfigDict(validate_assignment=True, extra='forbid')
63+
64+
x: Data0D
65+
y: Data0D
66+
z: Data0D
67+
w: Data0D
68+
69+
@model_validator(mode='after')
70+
def check_array_shapes(self) -> Self:
71+
if self.x.data.shape != self.y.data.shape:
72+
raise ValueError('x and y shapes must be equal')
73+
if self.x.data.shape != self.z.data.shape:
74+
raise ValueError('x and z shapes must be equal')
75+
if self.x.data.shape != self.w.data.shape:
76+
raise ValueError('x and w shapes must be equal')
77+
return self
78+
79+
80+
class DataGrid2D(BaseModel):
81+
"""
82+
A class to represent 2D data in a grid format, i.e. z = f(x, y), where x and y are
83+
series of coordinates. Useful for calculating 2D functions on a grid.
84+
"""
85+
model_config = ConfigDict(validate_assignment=True, extra='forbid')
86+
87+
x: Data0D
88+
y: Data0D
89+
z: NDArray[Shape['*', '*'], Number]
90+
91+
@model_validator(mode='after')
92+
def check_array_shapes(self) -> Self:
93+
if self.x.data.shape[0] != self.z.shape[0]:
94+
raise ValueError('x and z shapes must be equal')
95+
if self.y.data.shape[0] != self.z.shape[1]:
96+
raise ValueError('y and z shapes must be equal')
97+
return self
98+

src/xtl/common/labels.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
from dataclasses import dataclass
21
from typing import Optional
32

3+
from pydantic import BaseModel, ConfigDict, Field
4+
5+
6+
class Label(BaseModel):
7+
__pydantic_config__ = ConfigDict(validate_assignment=True, extra='forbid')
48

5-
@dataclass
6-
class Label:
79
value: str
8-
description: Optional[str] = None
9-
repr: Optional[str] = None
10-
latex: Optional[str] = None
10+
desc: Optional[str] = Field(default=None, repr=False)
11+
repr: Optional[str] = Field(default=None, repr=False)
12+
latex: Optional[str] = Field(default=None, repr=False)

0 commit comments

Comments
 (0)