|
2 | 2 | from ast import literal_eval |
3 | 3 | from collections import defaultdict |
4 | 4 | import copy |
5 | | -from ctypes import c_int32, c_char_p |
| 5 | +from ctypes import c_int32, c_char_p, Structure, c_int, c_size_t, c_bool, c_double |
6 | 6 | from dataclasses import dataclass |
7 | 7 | import hashlib |
8 | 8 | import itertools |
@@ -1072,7 +1072,238 @@ def mat_ids(self): |
1072 | 1072 | return self.geom_data[:, :, 2] |
1073 | 1073 |
|
1074 | 1074 |
|
1075 | | -class ViewParam(openmc.lib.plot._PlotBase): |
| 1075 | +class _Position(Structure): |
| 1076 | + """Definition of an xyz location in space with underlying c-types |
| 1077 | +
|
| 1078 | + C-type Attributes |
| 1079 | + ----------------- |
| 1080 | + x : c_double |
| 1081 | + Position's x value (default: 0.0) |
| 1082 | + y : c_double |
| 1083 | + Position's y value (default: 0.0) |
| 1084 | + z : c_double |
| 1085 | + Position's z value (default: 0.0) |
| 1086 | + """ |
| 1087 | + _fields_ = [('x', c_double), |
| 1088 | + ('y', c_double), |
| 1089 | + ('z', c_double)] |
| 1090 | + |
| 1091 | + def __getitem__(self, idx): |
| 1092 | + if idx == 0: |
| 1093 | + return self.x |
| 1094 | + elif idx == 1: |
| 1095 | + return self.y |
| 1096 | + elif idx == 2: |
| 1097 | + return self.z |
| 1098 | + else: |
| 1099 | + raise IndexError(f"{idx} index is invalid for _Position") |
| 1100 | + |
| 1101 | + def __setitem__(self, idx, val): |
| 1102 | + if idx == 0: |
| 1103 | + self.x = val |
| 1104 | + elif idx == 1: |
| 1105 | + self.y = val |
| 1106 | + elif idx == 2: |
| 1107 | + self.z = val |
| 1108 | + else: |
| 1109 | + raise IndexError(f"{idx} index is invalid for _Position") |
| 1110 | + |
| 1111 | + def __repr__(self): |
| 1112 | + return f"({self.x}, {self.y}, {self.z})" |
| 1113 | + |
| 1114 | + |
| 1115 | +class _PlotBase(Structure): |
| 1116 | + """A structure defining a 2-D geometry slice with underlying c-types |
| 1117 | +
|
| 1118 | + C-Type Attributes |
| 1119 | + ----------------- |
| 1120 | + origin_ : openmc.lib.plot._Position |
| 1121 | + A position defining the origin of the plot. |
| 1122 | + u_span_ : openmc.lib.plot._Position |
| 1123 | + Full-width span vector defining the plot's horizontal axis. |
| 1124 | + v_span_ : openmc.lib.plot._Position |
| 1125 | + Full-height span vector defining the plot's vertical axis. |
| 1126 | + width_ : openmc.lib.plot._Position |
| 1127 | + The width of the plot along the x, y, and z axes, respectively |
| 1128 | + basis_ : c_int |
| 1129 | + The axes basis of the plot view. |
| 1130 | + pixels_ : c_size_t[3] |
| 1131 | + The resolution of the plot in the horizontal and vertical dimensions |
| 1132 | + color_overlaps_ : c_bool |
| 1133 | + Whether to assign unique IDs (-3) to overlapping regions. |
| 1134 | + level_ : c_int |
| 1135 | + The universe level for the plot view |
| 1136 | +
|
| 1137 | + Attributes |
| 1138 | + ---------- |
| 1139 | + origin : tuple or list of ndarray |
| 1140 | + Origin (center) of the plot |
| 1141 | + width : float |
| 1142 | + The horizontal dimension of the plot in geometry units (cm) |
| 1143 | + height : float |
| 1144 | + The vertical dimension of the plot in geometry units (cm) |
| 1145 | + basis : string |
| 1146 | + One of {'xy', 'xz', 'yz'} indicating the horizontal and vertical |
| 1147 | + axes of the plot. |
| 1148 | + h_res : int |
| 1149 | + The horizontal resolution of the plot in pixels |
| 1150 | + v_res : int |
| 1151 | + The vertical resolution of the plot in pixels |
| 1152 | + level : int |
| 1153 | + The universe level for the plot (default: -1 -> all universes shown) |
| 1154 | + """ |
| 1155 | + _fields_ = [('origin_', _Position), |
| 1156 | + ('u_span_', _Position), |
| 1157 | + ('v_span_', _Position), |
| 1158 | + ('width_', _Position), |
| 1159 | + ('basis_', c_int), |
| 1160 | + ('pixels_', 3*c_size_t), |
| 1161 | + ('color_overlaps_', c_bool), |
| 1162 | + ('level_', c_int)] |
| 1163 | + |
| 1164 | + def __init__(self): |
| 1165 | + self.level_ = -1 |
| 1166 | + self.basis_ = 1 |
| 1167 | + self.color_overlaps_ = False |
| 1168 | + self._update_spans() |
| 1169 | + |
| 1170 | + def _update_spans(self): |
| 1171 | + if self.basis_ == 1: |
| 1172 | + self.u_span_.x = self.width_.x |
| 1173 | + self.u_span_.y = 0.0 |
| 1174 | + self.u_span_.z = 0.0 |
| 1175 | + self.v_span_.x = 0.0 |
| 1176 | + self.v_span_.y = self.width_.y |
| 1177 | + self.v_span_.z = 0.0 |
| 1178 | + elif self.basis_ == 2: |
| 1179 | + self.u_span_.x = self.width_.x |
| 1180 | + self.u_span_.y = 0.0 |
| 1181 | + self.u_span_.z = 0.0 |
| 1182 | + self.v_span_.x = 0.0 |
| 1183 | + self.v_span_.y = 0.0 |
| 1184 | + self.v_span_.z = self.width_.y |
| 1185 | + elif self.basis_ == 3: |
| 1186 | + self.u_span_.x = 0.0 |
| 1187 | + self.u_span_.y = self.width_.x |
| 1188 | + self.u_span_.z = 0.0 |
| 1189 | + self.v_span_.x = 0.0 |
| 1190 | + self.v_span_.y = 0.0 |
| 1191 | + self.v_span_.z = self.width_.y |
| 1192 | + |
| 1193 | + @property |
| 1194 | + def origin(self): |
| 1195 | + return self.origin_ |
| 1196 | + |
| 1197 | + @origin.setter |
| 1198 | + def origin(self, origin): |
| 1199 | + self.origin_.x = origin[0] |
| 1200 | + self.origin_.y = origin[1] |
| 1201 | + self.origin_.z = origin[2] |
| 1202 | + |
| 1203 | + @property |
| 1204 | + def width(self): |
| 1205 | + return self.width_.x |
| 1206 | + |
| 1207 | + @width.setter |
| 1208 | + def width(self, width): |
| 1209 | + self.width_.x = width |
| 1210 | + self._update_spans() |
| 1211 | + |
| 1212 | + @property |
| 1213 | + def height(self): |
| 1214 | + return self.width_.y |
| 1215 | + |
| 1216 | + @height.setter |
| 1217 | + def height(self, height): |
| 1218 | + self.width_.y = height |
| 1219 | + self._update_spans() |
| 1220 | + |
| 1221 | + @property |
| 1222 | + def basis(self): |
| 1223 | + if self.basis_ == 1: |
| 1224 | + return 'xy' |
| 1225 | + elif self.basis_ == 2: |
| 1226 | + return 'xz' |
| 1227 | + elif self.basis_ == 3: |
| 1228 | + return 'yz' |
| 1229 | + |
| 1230 | + raise ValueError(f"Plot basis {self.basis_} is invalid") |
| 1231 | + |
| 1232 | + @basis.setter |
| 1233 | + def basis(self, basis): |
| 1234 | + if isinstance(basis, str): |
| 1235 | + valid_bases = ('xy', 'xz', 'yz') |
| 1236 | + basis = basis.lower() |
| 1237 | + if basis not in valid_bases: |
| 1238 | + raise ValueError(f"{basis} is not a valid plot basis.") |
| 1239 | + |
| 1240 | + if basis == 'xy': |
| 1241 | + self.basis_ = 1 |
| 1242 | + elif basis == 'xz': |
| 1243 | + self.basis_ = 2 |
| 1244 | + elif basis == 'yz': |
| 1245 | + self.basis_ = 3 |
| 1246 | + self._update_spans() |
| 1247 | + return |
| 1248 | + |
| 1249 | + if isinstance(basis, int): |
| 1250 | + valid_bases = (1, 2, 3) |
| 1251 | + if basis not in valid_bases: |
| 1252 | + raise ValueError(f"{basis} is not a valid plot basis.") |
| 1253 | + self.basis_ = basis |
| 1254 | + self._update_spans() |
| 1255 | + return |
| 1256 | + |
| 1257 | + raise ValueError(f"{basis} of type {type(basis)} is an invalid plot basis") |
| 1258 | + |
| 1259 | + @property |
| 1260 | + def h_res(self): |
| 1261 | + return self.pixels_[0] |
| 1262 | + |
| 1263 | + @h_res.setter |
| 1264 | + def h_res(self, h_res): |
| 1265 | + self.pixels_[0] = h_res |
| 1266 | + |
| 1267 | + @property |
| 1268 | + def v_res(self): |
| 1269 | + return self.pixels_[1] |
| 1270 | + |
| 1271 | + @v_res.setter |
| 1272 | + def v_res(self, v_res): |
| 1273 | + self.pixels_[1] = v_res |
| 1274 | + |
| 1275 | + @property |
| 1276 | + def level(self): |
| 1277 | + return int(self.level_) |
| 1278 | + |
| 1279 | + @level.setter |
| 1280 | + def level(self, level): |
| 1281 | + self.level_ = level |
| 1282 | + |
| 1283 | + @property |
| 1284 | + def color_overlaps(self): |
| 1285 | + return self.color_overlaps_ |
| 1286 | + |
| 1287 | + @color_overlaps.setter |
| 1288 | + def color_overlaps(self, color_overlaps): |
| 1289 | + self.color_overlaps_ = color_overlaps |
| 1290 | + |
| 1291 | + def __repr__(self): |
| 1292 | + out_str = ["-----", |
| 1293 | + "Plot:", |
| 1294 | + "-----", |
| 1295 | + f"Origin: {self.origin}", |
| 1296 | + f"Width: {self.width}", |
| 1297 | + f"Height: {self.height}", |
| 1298 | + f"Basis: {self.basis}", |
| 1299 | + f"HRes: {self.h_res}", |
| 1300 | + f"VRes: {self.v_res}", |
| 1301 | + f"Color Overlaps: {self.color_overlaps}", |
| 1302 | + f"Level: {self.level}"] |
| 1303 | + return '\n'.join(out_str) |
| 1304 | + |
| 1305 | + |
| 1306 | +class ViewParam(_PlotBase): |
1076 | 1307 | """Viewer settings that are needed for _PlotBase and are independent |
1077 | 1308 | of all other plotter/model settings. |
1078 | 1309 |
|
@@ -1167,6 +1398,7 @@ def urc(self): |
1167 | 1398 | def __eq__(self, other): |
1168 | 1399 | return repr(self) == repr(other) |
1169 | 1400 |
|
| 1401 | + |
1170 | 1402 | class PlotViewIndependent: |
1171 | 1403 | """View settings for OpenMC plot, independent of the model. |
1172 | 1404 |
|
|
0 commit comments