1- """Module datamodel.py
2- Basic Model for all pyRadPlan Datastructures.
3- """
1+ """Basic Model for all pyRadPlan Datastructures."""
42
53from typing import Any , Union
4+ import numpy as np
65from pydantic import (
76 AliasGenerator ,
87 BaseModel ,
1413
1514class PyRadPlanBaseModel (BaseModel ):
1615 """
17- Base class for all pyRadPlan data structures, especially the ones that
18- should be matRad
19- compatible
20- This class extends Pydantic's BaseModel.
16+ Base class for all pyRadPlan data structures.
17+
18+ Especially useful for structures that should be matRad compatible.
19+ Extends Pydantic's BaseModel to use pydantic validation and serialization .
2120
2221 Attributes
2322 ----------
2423 model_config : ConfigDict
25- Configuration for the model, including alias generation, population by name, arbitrary
26- types allowed, assignment validation, and attribute creation from dictionary.
24+ Configuration for the model, including alias generation, population by
25+ name, arbitrary types allowed, assignment validation, and attribute
26+ creation from dictionary.
2727 """
2828
2929 model_config = ConfigDict (
@@ -35,15 +35,56 @@ class PyRadPlanBaseModel(BaseModel):
3535 from_attributes = True , # Allows to create a model from a dictionary
3636 )
3737
38+ def __eq__ (self , other : Any ) -> bool :
39+ """
40+ Specialized __eq__ method to compare two pyRadPlanBaseModel instances.
41+
42+ It first tries to compare the instances using the super().__eq__ method.
43+ If this fails, it compares the dictionaries. This is due to some issues
44+ comparing numpy arrays within the models.
45+ """
46+ try :
47+ return super ().__eq__ (other )
48+ except ValueError :
49+ if self .__dict__ .keys () != other .__dict__ .keys ():
50+ return False
51+ stack = [(self .__dict__ , other .__dict__ )]
52+ while stack :
53+ dict_a , dict_b = stack .pop ()
54+ if dict_a .keys () != dict_b .keys ():
55+ return False
56+ for key in dict_a :
57+ if isinstance (dict_a [key ], dict ) and isinstance (dict_b [key ], dict ):
58+ stack .append ((dict_a [key ], dict_b [key ]))
59+ elif isinstance (dict_a [key ], np .ndarray ) and isinstance (
60+ dict_b [key ], np .ndarray
61+ ):
62+ if not np .array_equal (dict_a [key ], dict_b [key ]):
63+ return False
64+ elif dict_a [key ] != dict_b [key ]:
65+ return False
66+ return True
67+
68+ def __ne__ (self , other : Any ) -> bool :
69+ """
70+ Specialized __ne__ method to compare two PyRadPlanBaseModel instances.
71+
72+ This method returns the negation of the __eq__ method.
73+ """
74+ if isinstance (other , self .__class__ ):
75+ return not self .__eq__ (other )
76+ else :
77+ return True
78+
3879 def to_matrad (self , context : Union [str , dict ] = "mat-file" ) -> Any :
3980 """
40- Interface method to serialize a pyradplan datastructure to be matRad
41- compatible.
81+ Perform matRad compatible serialization.
4282
4383 Parameters
4484 ----------
4585 context : str, optional
46- The context in which the datastructure should be serialized, by default 'mat-file'.
86+ The context in which the datastructure should be serialized,
87+ by default 'mat-file'.
4788
4889 Returns
4990 -------
@@ -52,9 +93,9 @@ def to_matrad(self, context: Union[str, dict] = "mat-file") -> Any:
5293
5394 Notes
5495 -----
55- Currently, the only supported context is 'mat-file'. In the future, this could be
56- extended to support other contexts, such as direct calling via the matlab engine or
57- oct2py.
96+ Currently, the only supported context is 'mat-file'. In the future,
97+ this could be extended to support other contexts, such as direct
98+ calling via the matlab engine or oct2py.
5899 """
59100 self_copy = deepcopy (self )
60101 if isinstance (context , dict ):
0 commit comments