22
33import numpy as np
44
5+ __all__ = ['Box' ]
56
67class Box (object ):
78 """A box representing the bounds of the system.
@@ -24,23 +25,34 @@ def __init__(self, lengths=None, mins=None, maxs=None, angles=None):
2425 "You provided: "
2526 "lengths={} mins={} maxs={}" .format (lengths , mins , maxs )
2627 )
27- self ._mins = np .array (mins , dtype = np .float )
28- self ._maxs = np .array (maxs , dtype = np .float )
29- self ._lengths = self .maxs - self .mins
28+ mins = np .array (mins , dtype = np .float )
29+ maxs = np .array (maxs , dtype = np .float )
30+ assert mins .shape == (3 , ), "Given mins have wrong dimensions"
31+ assert maxs .shape == (3 , ), "Given maxs have wrong dimensions"
32+ assert all (mins <= maxs ), "Given mins are greater than maxs"
33+ self ._mins = _BoxArray (array = mins , var = "mins" , box = self )
34+ self ._maxs = _BoxArray (array = maxs , var = "maxs" , box = self )
35+ self ._lengths = _BoxArray (array = (self .maxs - self .mins ), var = "lengths" , box = self )
3036 else :
3137 if mins is not None or maxs is not None :
3238 warn (
3339 "Provided `lengths` and `mins` and/or `maxs`. Only `lengths` "
3440 "is being used. You provided: "
3541 "lengths={} mins={} maxs={}" .format (lengths , mins , maxs )
3642 )
37- self ._mins = np .array ([0.0 , 0.0 , 0.0 ])
38- self ._maxs = np .array (lengths , dtype = np .float )
39- self ._lengths = np .array (lengths , dtype = np .float )
43+ if isinstance (lengths , int ) or isinstance (lengths , float ):
44+ lengths = np .array (lengths * np .ones (3 ), dtype = np .float )
45+ else :
46+ lengths = np .array (lengths , dtype = np .float )
47+ assert lengths .shape == (3 , )
48+ assert all (lengths >= 0 ), "Given lengths are negative"
49+ self ._mins = _BoxArray (array = (0 ,0 ,0 ), var = "mins" , box = self )
50+ self ._maxs = _BoxArray (array = lengths , var = "maxs" , box = self )
51+ self ._lengths = _BoxArray (array = lengths , var = "lengths" , box = self )
4052 if angles is None :
41- angles = np . array ([ 90.0 , 90.0 , 90.0 ] )
53+ angles = _BoxArray ( array = ( 90.0 , 90.0 , 90.0 ), var = "angles" , box = self )
4254 elif isinstance (angles , (list , np .ndarray )):
43- angles = np . array ( angles , dtype = np . float )
55+ angles = _BoxArray ( array = angles , var = "angles" , box = self )
4456 self ._angles = angles
4557
4658 @property
@@ -61,35 +73,78 @@ def angles(self):
6173
6274 @mins .setter
6375 def mins (self , mins ):
64- if isinstance (mins , list ):
65- mins = np .array (mins , dtype = np .float )
76+ mins = np .array (mins , dtype = np .float )
6677 assert mins .shape == (3 , )
67- self ._mins = mins
68- self ._lengths = self .maxs - self .mins
78+ assert all (mins <= self .maxs ), "Given mins is greater than maxs"
79+ self ._mins = _BoxArray (array = mins , var = "mins" , box = self )
80+ self ._lengths = _BoxArray (array = (self .maxs - self .mins ), var = "lengths" , box = self )
6981
7082 @maxs .setter
71- def maxs (self , maxes ):
72- if isinstance ( maxes , list ):
73- maxes = np . array ( maxes , dtype = np . float )
74- assert maxes . shape == ( 3 , )
75- self ._maxs = maxes
76- self ._lengths = self .maxs - self .mins
83+ def maxs (self , maxs ):
84+ maxs = np . array ( maxs , dtype = np . float )
85+ assert maxs . shape == ( 3 , )
86+ assert all ( maxs >= self . mins ), "Given maxs is less than mins"
87+ self ._maxs = _BoxArray ( array = maxs , var = "maxs" , box = self )
88+ self ._lengths = _BoxArray ( array = ( self .maxs - self .mins ), var = "lengths" , box = self )
7789
7890 @lengths .setter
7991 def lengths (self , lengths ):
80- if isinstance (lengths , list ):
92+ if isinstance (lengths , int ) or isinstance (lengths , float ):
93+ lengths = np .array (lengths * np .ones (3 ), dtype = np .float )
94+ else :
8195 lengths = np .array (lengths , dtype = np .float )
8296 assert lengths .shape == (3 , )
83- self ._maxs += 0.5 * lengths - 0.5 * self .lengths
84- self ._mins -= 0.5 * lengths - 0.5 * self .lengths
85- self ._lengths = lengths
97+ assert all (lengths >= 0 ), "Given lengths are negative"
98+ self ._maxs = _BoxArray (array = (self .maxs + (0.5 * lengths - 0.5 * self .lengths )), var = "maxs" , box = self )
99+ self ._mins = _BoxArray (array = (self .mins - (0.5 * lengths - 0.5 * self .lengths )), var = "mins" , box = self )
100+ self ._lengths = _BoxArray (array = lengths , var = "lengths" , box = self , dtype = np .float )
86101
87102 @angles .setter
88103 def angles (self , angles ):
89- if isinstance (angles , list ):
90- angles = np .array (angles , dtype = np .float )
104+ angles = np .array (angles , dtype = np .float )
91105 assert angles .shape == (3 , )
92- self ._angles = angles
106+ self ._angles = _BoxArray ( array = angles , var = "angles" , box = self , dtype = np . float )
93107
94108 def __repr__ (self ):
95109 return "Box(mins={}, maxs={}, angles={})" .format (self .mins , self .maxs , self .angles )
110+
111+ class _BoxArray (np .ndarray ):
112+ """Subclass of np.ndarry specifically for mb.Box
113+
114+ This subclass is meant to be used internally to store Box attribute array.
115+ This subclass is modified so that its __setitem__ method is reroute to the
116+ corresponding setter method.
117+
118+ Parameters
119+ ----------
120+ array : array-like object
121+ This can be tuple, list, or any array-like object that can be usually
122+ passed to np.array
123+ var : str
124+ Corresponding Box's attributes like "maxs", "mins", "lengths", "angles"
125+ box : mb.Box
126+ This is the Box contains this attribute (one level up of this array)
127+ """
128+ def __new__ (cls , array , var = None , box = None , dtype = np .float ):
129+ _array = np .asarray (array , dtype ).view (cls )
130+ _array .var = var
131+ _array .box = box
132+ return _array
133+
134+ def __setitem__ (self , key , val ):
135+ array = list (self )
136+ array [key ] = val
137+ if self .var == "maxs" :
138+ msg = "Given max value is less than box's min value"
139+ assert val >= self .box .mins [key ], msg
140+ self .box .maxs = array
141+ elif self .var == "mins" :
142+ msg = "Given min value is more than box's max value"
143+ assert val <= self .box .maxs [key ], msg
144+ self .box .mins = array
145+ elif self .var == "lengths" :
146+ msg = "Given length value is negative"
147+ assert val >= 0 , msg
148+ self .box .lengths = array
149+ else :
150+ self .box .angles = array
0 commit comments