2323from openmc .executor import _process_CLI_arguments
2424from openmc .checkvalue import check_type , check_value , PathLike
2525from openmc .exceptions import InvalidIDError
26- from openmc .plots import add_plot_params , _BASIS_INDICES
26+ from openmc .plots import add_plot_params , _BASIS_INDICES , id_map_to_rgb
2727from openmc .utility_funcs import change_directory
2828
2929
@@ -1114,13 +1114,12 @@ def plot(
11141114 color_by : str = 'cell' ,
11151115 colors : dict | None = None ,
11161116 seed : int | None = None ,
1117- openmc_exec : PathLike = 'openmc' ,
11181117 axes = None ,
11191118 legend : bool = False ,
11201119 axis_units : str = 'cm' ,
11211120 outline : bool | str = False ,
11221121 show_overlaps : bool = False ,
1123- overlap_color : Sequence [int ] | str | None = None ,
1122+ overlap_color : Sequence [int ] | str = ( 255 , 0 , 0 ) ,
11241123 n_samples : int | None = None ,
11251124 plane_tolerance : float = 1. ,
11261125 legend_kwargs : dict | None = None ,
@@ -1132,7 +1131,6 @@ def plot(
11321131
11331132 .. versionadded:: 0.15.1
11341133 """
1135- import matplotlib .image as mpimg
11361134 import matplotlib .patches as mpatches
11371135 import matplotlib .pyplot as plt
11381136
@@ -1162,125 +1160,108 @@ def plot(
11621160 y_min = (origin [y ] - 0.5 * width [1 ]) * axis_scaling_factor [axis_units ]
11631161 y_max = (origin [y ] + 0.5 * width [1 ]) * axis_scaling_factor [axis_units ]
11641162
1165- # Determine whether any materials contains macroscopic data and if so,
1166- # set energy mode accordingly
1167- _energy_mode = self .settings ._energy_mode
1168- for mat in self .geometry .get_all_materials ().values ():
1169- if mat ._macroscopic is not None :
1170- self .settings .energy_mode = 'multi-group'
1171- break
1172-
1173- with TemporaryDirectory () as tmpdir :
1174- _plot_seed = self .settings .plot_seed
1175- if seed is not None :
1176- self .settings .plot_seed = seed
1163+ # Get ID map from the C API
1164+ id_map = self .id_map (
1165+ origin = origin ,
1166+ width = width ,
1167+ pixels = pixels ,
1168+ basis = basis ,
1169+ color_overlaps = show_overlaps
1170+ )
11771171
1178- # Create plot object matching passed arguments
1172+ # Generate colors if not provided
1173+ if colors is None and seed is not None :
1174+ # Use the colorize method to generate random colors
11791175 plot = openmc .SlicePlot ()
1180- plot .origin = origin
1181- plot .width = width
1182- plot .pixels = pixels
1183- plot .basis = basis
11841176 plot .color_by = color_by
1185- plot .show_overlaps = show_overlaps
1186- if overlap_color is not None :
1187- plot .overlap_color = overlap_color
1188- if colors is not None :
1189- plot .colors = colors
1190- self .plots .append (plot )
1191-
1192- # Run OpenMC in geometry plotting mode
1193- self .plot_geometry (False , cwd = tmpdir , openmc_exec = openmc_exec )
1194-
1195- # Undo changes to model
1196- self .plots .pop ()
1197- self .settings ._plot_seed = _plot_seed
1198- self .settings ._energy_mode = _energy_mode
1199-
1200- # Read image from file
1201- img_path = Path (tmpdir ) / f'plot_{ plot .id } .png'
1202- if not img_path .is_file ():
1203- img_path = img_path .with_suffix ('.ppm' )
1204- img = mpimg .imread (str (img_path ))
1205-
1206- # Create a figure sized such that the size of the axes within
1207- # exactly matches the number of pixels specified
1208- if axes is None :
1209- px = 1 / plt .rcParams ['figure.dpi' ]
1210- fig , axes = plt .subplots ()
1211- axes .set_xlabel (xlabel )
1212- axes .set_ylabel (ylabel )
1213- params = fig .subplotpars
1214- width = pixels [0 ]* px / (params .right - params .left )
1215- height = pixels [1 ]* px / (params .top - params .bottom )
1216- fig .set_size_inches (width , height )
1217-
1218- if outline :
1219- # Combine R, G, B values into a single int
1220- rgb = (img * 256 ).astype (int )
1221- image_value = (rgb [..., 0 ] << 16 ) + \
1222- (rgb [..., 1 ] << 8 ) + (rgb [..., 2 ])
1223-
1224- # Set default arguments for contour()
1225- if contour_kwargs is None :
1226- contour_kwargs = {}
1227- contour_kwargs .setdefault ('colors' , 'k' )
1228- contour_kwargs .setdefault ('linestyles' , 'solid' )
1229- contour_kwargs .setdefault ('algorithm' , 'serial' )
1230-
1231- axes .contour (
1232- image_value ,
1233- origin = "upper" ,
1234- levels = np .unique (image_value ),
1235- extent = (x_min , x_max , y_min , y_max ),
1236- ** contour_kwargs
1237- )
1238-
1239- # add legend showing which colors represent which material
1240- # or cell if that was requested
1241- if legend :
1242- if plot .colors == {}:
1243- raise ValueError ("Must pass 'colors' dictionary if you "
1244- "are adding a legend via legend=True." )
1177+ plot .colorize (self .geometry , seed = seed )
1178+ colors = plot .colors
1179+
1180+ # Convert ID map to RGB image
1181+ img = id_map_to_rgb (
1182+ id_map = id_map ,
1183+ color_by = color_by ,
1184+ colors = colors ,
1185+ overlap_color = overlap_color
1186+ )
12451187
1246- if color_by == "cell" :
1247- expected_key_type = openmc .Cell
1188+ # Create a figure sized such that the size of the axes within
1189+ # exactly matches the number of pixels specified
1190+ if axes is None :
1191+ px = 1 / plt .rcParams ['figure.dpi' ]
1192+ fig , axes = plt .subplots ()
1193+ axes .set_xlabel (xlabel )
1194+ axes .set_ylabel (ylabel )
1195+ params = fig .subplotpars
1196+ width_px = pixels [0 ]* px / (params .right - params .left )
1197+ height_px = pixels [1 ]* px / (params .top - params .bottom )
1198+ fig .set_size_inches (width_px , height_px )
1199+
1200+ if outline :
1201+ # Combine R, G, B values into a single int for contour detection
1202+ rgb = (img * 256 ).astype (int )
1203+ image_value = (rgb [..., 0 ] << 16 ) + \
1204+ (rgb [..., 1 ] << 8 ) + (rgb [..., 2 ])
1205+
1206+ # Set default arguments for contour()
1207+ if contour_kwargs is None :
1208+ contour_kwargs = {}
1209+ contour_kwargs .setdefault ('colors' , 'k' )
1210+ contour_kwargs .setdefault ('linestyles' , 'solid' )
1211+ contour_kwargs .setdefault ('algorithm' , 'serial' )
1212+
1213+ axes .contour (
1214+ image_value ,
1215+ origin = "upper" ,
1216+ levels = np .unique (image_value ),
1217+ extent = (x_min , x_max , y_min , y_max ),
1218+ ** contour_kwargs
1219+ )
1220+
1221+ # If only showing outline, set the axis limits and aspect explicitly
1222+ if outline == 'only' :
1223+ axes .set_xlim (x_min , x_max )
1224+ axes .set_ylim (y_min , y_max )
1225+ axes .set_aspect ('equal' )
1226+
1227+ # Add legend showing which colors represent which material or cell
1228+ if legend :
1229+ if colors is None or len (colors ) == 0 :
1230+ raise ValueError ("Must pass 'colors' dictionary if you "
1231+ "are adding a legend via legend=True." )
1232+
1233+ if color_by == "cell" :
1234+ expected_key_type = openmc .Cell
1235+ else :
1236+ expected_key_type = openmc .Material
1237+
1238+ patches = []
1239+ for key , color in colors .items ():
1240+ if isinstance (key , int ):
1241+ raise TypeError (
1242+ "Cannot use IDs in colors dict for auto legend." )
1243+ elif not isinstance (key , expected_key_type ):
1244+ raise TypeError (
1245+ "Color dict key type does not match color_by" )
1246+
1247+ # this works whether we're doing cells or materials
1248+ label = key .name if key .name != '' else key .id
1249+
1250+ # matplotlib takes RGB on 0-1 scale rather than 0-255
1251+ if len (color ) == 3 and not isinstance (color , str ):
1252+ scaled_color = (
1253+ color [0 ]/ 255 , color [1 ]/ 255 , color [2 ]/ 255 )
12481254 else :
1249- expected_key_type = openmc .Material
1250-
1251- patches = []
1252- for key , color in plot .colors .items ():
1253-
1254- if isinstance (key , int ):
1255- raise TypeError (
1256- "Cannot use IDs in colors dict for auto legend." )
1257- elif not isinstance (key , expected_key_type ):
1258- raise TypeError (
1259- "Color dict key type does not match color_by" )
1260-
1261- # this works whether we're doing cells or materials
1262- label = key .name if key .name != '' else key .id
1263-
1264- # matplotlib takes RGB on 0-1 scale rather than 0-255. at
1265- # this point PlotBase has already checked that 3-tuple
1266- # based colors are already valid, so if the length is three
1267- # then we know it just needs to be converted to the 0-1
1268- # format.
1269- if len (color ) == 3 and not isinstance (color , str ):
1270- scaled_color = (
1271- color [0 ]/ 255 , color [1 ]/ 255 , color [2 ]/ 255 )
1272- else :
1273- scaled_color = color
1274-
1275- key_patch = mpatches .Patch (color = scaled_color , label = label )
1276- patches .append (key_patch )
1255+ scaled_color = color
12771256
1278- axes .legend (handles = patches , ** legend_kwargs )
1257+ key_patch = mpatches .Patch (color = scaled_color , label = label )
1258+ patches .append (key_patch )
12791259
1280- # Plot image and return the axes
1281- if outline != 'only' :
1282- axes .imshow (img , extent = (x_min , x_max , y_min , y_max ), ** kwargs )
1260+ axes .legend (handles = patches , ** legend_kwargs )
12831261
1262+ # Plot image and return the axes
1263+ if outline != 'only' :
1264+ axes .imshow (img , extent = (x_min , x_max , y_min , y_max ), ** kwargs )
12841265
12851266 if n_samples :
12861267 # Sample external source particles
0 commit comments