def summarizePinDesign(core): r"""Prints out some information about the pin assembly/duct design. Handles multiple types of dimensions simplistically by taking the average. Parameters ---------- core : armi.reactor.reactors.Core """ designInfo = collections.defaultdict(list) try: for b in core.getBlocks(Flags.FUEL): fuel = b.getComponent(Flags.FUEL) duct = b.getComponent(Flags.DUCT) clad = b.getComponent(Flags.CLAD) wire = b.getComponent(Flags.WIRE) designInfo["hot sd"].append(b.getSmearDensity(cold=False)) designInfo["sd"].append(b.getSmearDensity()) designInfo["ductThick"].append( (duct.getDimension("op") - duct.getDimension("ip")) * 5.0) # convert to mm and divide by 2 designInfo["cladThick"].append( (clad.getDimension("od") - clad.getDimension("id")) * 5.0) pinOD = clad.getDimension("od") * 10.0 wireOD = wire.getDimension("od") * 10.0 pitch = pinOD + wireOD # pitch has half a wire on each side. assemPitch = b.getPitch() * 10 # convert cm to mm. designInfo["pinOD"].append(pinOD) designInfo["wireOD"].append(wireOD) designInfo["pin pitch"].append(pitch) pinToDuctGap = b.getPinToDuctGap() if pinToDuctGap is not None: designInfo["pinToDuct"].append(b.getPinToDuctGap() * 10.0) designInfo["assemPitch"].append(assemPitch) designInfo["duct gap"].append(assemPitch - duct.getDimension("op") * 10.0) designInfo["nPins"].append(b.p.nPins) designInfo["zrFrac"].append(fuel.getMassFrac("ZR")) # assumption made that all lists contain only numerical data designInfo = { key: numpy.average(data) for key, data in designInfo.items() } dimensionless = {"sd", "hot sd", "zrFrac", "nPins"} for key, average_value in designInfo.items(): dim = "{0:10s}".format(key) val = "{0:.4f}".format(average_value) if key not in dimensionless: val += " mm" report.setData(dim, val, report.PIN_ASSEM_DESIGN) a = core.refAssem report.setData( "Fuel Height (cm):", "{0:.2f}".format(a.getHeight(Flags.FUEL)), report.PIN_ASSEM_DESIGN, ) report.setData( "Plenum Height (cm):", "{0:.2f}".format(a.getHeight(Flags.PLENUM)), report.PIN_ASSEM_DESIGN, ) runLog.info(report.ALL[report.PIN_ASSEM_DESIGN]) first_fuel_block = core.getFirstBlock(Flags.FUEL) runLog.info( "Design & component information for first fuel block {}".format( first_fuel_block)) runLog.info(first_fuel_block.setAreaFractionsReport()) for component_ in sorted(first_fuel_block): runLog.info(component_.setDimensionReport()) except Exception as error: # pylint: disable=broad-except runLog.warning("Pin summarization failed to work") runLog.warning(error)
def _setGeneralCoreDesignData(cs, coreDesignTable): report.setData("Case Title", "{}".format(cs.caseTitle), coreDesignTable, report.DESIGN) report.setData("Run Type", "{}".format(cs["runType"]), coreDesignTable, report.DESIGN) report.setData("Geometry File", "{}".format(cs["geomFile"]), coreDesignTable, report.DESIGN) report.setData("Loading File", "{}".format(cs["loadingFile"]), coreDesignTable, report.DESIGN) report.setData( "Fuel Shuffling Logic File", "{}".format(cs["shuffleLogic"]), coreDesignTable, report.DESIGN, ) report.setData( "Reactor State Loading", "{}".format(cs["loadStyle"]), coreDesignTable, report.DESIGN, ) if cs["loadStyle"] == "fromDB": report.setData( "Database File", "{}".format(cs["reloadDBName"]), coreDesignTable, report.DESIGN, ) report.setData( "Starting Cycle", "{}".format(cs["startCycle"]), coreDesignTable, report.DESIGN, ) report.setData( "Starting Node", "{}".format(cs["startNode"]), coreDesignTable, report.DESIGN, )
def setNeutronBalancesReport(core): """Determines the various neutron balances over the full core Parameters ---------- core : armi.reactor.reactors.Core """ if not core.getFirstBlock().p.rateCap: runLog.warning( "No rate information (rateCap, rateAbs, etc.) available " "on the blocks. Skipping balance summary.") return cap = core.calcAvgParam("rateCap", volumeAveraged=False, generationNum=2) absorb = core.calcAvgParam("rateAbs", volumeAveraged=False, generationNum=2) fis = core.calcAvgParam("rateFis", volumeAveraged=False, generationNum=2) n2nProd = core.calcAvgParam("rateProdN2n", volumeAveraged=False, generationNum=2) fisProd = core.calcAvgParam("rateProdFis", volumeAveraged=False, generationNum=2) leak = n2nProd + fisProd - absorb report.setData( "Fission", "{0:.5e} ({1:.2%})".format(fisProd, fisProd / (fisProd + n2nProd)), report.NEUT_PROD, ) report.setData( "n, 2n", "{0:.5e} ({1:.2%})".format(n2nProd, n2nProd / (fisProd + n2nProd)), report.NEUT_PROD, ) report.setData( "Capture", "{0:.5e} ({1:.2%})".format(cap, cap / (absorb + leak)), report.NEUT_LOSS, ) report.setData( "Fission", "{0:.5e} ({1:.2%})".format(fis, fis / (absorb + leak)), report.NEUT_LOSS, ) report.setData( "Absorption", "{0:.5e} ({1:.2%})".format(absorb, absorb / (absorb + leak)), report.NEUT_LOSS, ) report.setData( "Leakage", "{0:.5e} ({1:.2%})".format(leak, leak / (absorb + leak)), report.NEUT_LOSS, ) runLog.info(report.ALL[report.NEUT_PROD]) # TODO: print in "lite" runLog.info(report.ALL[report.NEUT_LOSS])
def makeCoreAndAssemblyMaps(r, cs, generateFullCoreMap=False, showBlockAxMesh=True): r"""Create core and assembly design plots Parameters ---------- r : armi.reactor.reactors.Reactor cs: armi.settings.caseSettings.Settings generateFullCoreMap : bool, default False showBlockAxMesh : bool, default True """ assemsInCore = list(r.blueprints.assemblies.values()) core = r.core for plotNum, assemBatch in enumerate(iterables.chunk( assemsInCore, MAX_ASSEMS_PER_ASSEM_PLOT), start=1): assemPlotImage = copy(report.ASSEM_TYPES) assemPlotImage.title = assemPlotImage.title + " ({})".format(plotNum) report.data.Report.groupsOrderFirst.insert(-1, assemPlotImage) report.data.Report.componentWellGroups.insert(-1, assemPlotImage) assemPlotName = os.path.abspath( f"{core.name}AssemblyTypes{plotNum}.png") plotting.plotAssemblyTypes( core.parent.blueprints, assemPlotName, assemBatch, maxAssems=MAX_ASSEMS_PER_ASSEM_PLOT, showBlockAxMesh=showBlockAxMesh, ) # Create radial core map if generateFullCoreMap: core.growToFullCore(cs) counts = { assemDesign.name: len(core.getChildrenOfType(assemDesign.name)) for assemDesign in r.blueprints.assemDesigns } # assemDesigns.keys is ordered based on input, assemOrder only contains types that are in the core assemOrder = [ aType for aType in r.blueprints.assemDesigns.keys() if counts[aType] > 0 ] data = [assemOrder.index(a.p.type) for a in core] labels = [r.blueprints.assemDesigns[a.p.type].specifier for a in core] legendMap = [( ai, assemDesign.specifier, "{} ({})".format(assemDesign.name, counts[assemDesign.name]), ) for ai, assemDesign in enumerate(r.blueprints.assemDesigns) if counts[assemDesign.name] > 0] fName = "".join( [cs.caseTitle, "RadialCoreMap.", cs["outputFileExtension"]]) plotting.plotFaceMap( core, title="{} Radial Core Map".format(cs.caseTitle), fName=fName, cmapName="RdYlBu", data=data, labels=labels, legendMap=legendMap, axisEqual=True, bare=True, titleSize=10, fontSize=8, ) plotting.close() report.setData("Radial Core Map", os.path.abspath(fName), report.FACE_MAP, report.DESIGN)
def _setGeneralCoreParametersData(core, cs, coreDesignTable): blocks = core.getBlocks() totalMass = sum(b.getMass() for b in blocks) fissileMass = sum(b.getFissileMass() for b in blocks) heavyMetalMass = sum(b.getHMMass() for b in blocks) totalVolume = sum(b.getVolume() for b in blocks) report.setData(" ", "", coreDesignTable, report.DESIGN) report.setData( "Core Power", "{:.2f} MWth".format(cs["power"] / units.WATTS_PER_MW), coreDesignTable, report.DESIGN, ) report.setData( "Base Capacity Factor", "{}".format(cs["availabilityFactor"]), coreDesignTable, report.DESIGN, ) report.setData( "Cycle Length", "{} days".format(cs["cycleLength"]), coreDesignTable, report.DESIGN, ) report.setData("Burnup Cycles", "{}".format(cs["nCycles"]), coreDesignTable, report.DESIGN) report.setData( "Burnup Steps per Cycle", "{}".format(cs["burnSteps"]), coreDesignTable, report.DESIGN, ) corePowerMult = int(core.powerMultiplier) report.setData( "Core Total Volume", "{:.2f} cc".format(totalVolume * corePowerMult), coreDesignTable, report.DESIGN, ) report.setData( "Core Fissile Mass", "{:.2f} kg".format(fissileMass / units.G_PER_KG * corePowerMult), coreDesignTable, report.DESIGN, ) report.setData( "Core Heavy Metal Mass", "{:.2f} kg".format(heavyMetalMass / units.G_PER_KG * corePowerMult), coreDesignTable, report.DESIGN, ) report.setData( "Core Total Mass", "{:.2f} kg".format(totalMass / units.G_PER_KG * corePowerMult), coreDesignTable, report.DESIGN, ) report.setData( "Number of Assembly Rings", "{}".format(core.getNumRings()), coreDesignTable, report.DESIGN, ) report.setData( "Number of Assemblies", "{}".format(len(core.getAssemblies() * corePowerMult)), coreDesignTable, report.DESIGN, ) report.setData( "Number of Fuel Assemblies", "{}".format(len(core.getAssemblies(Flags.FUEL) * corePowerMult)), coreDesignTable, report.DESIGN, ) report.setData( "Number of Control Assemblies", "{}".format(len(core.getAssemblies(Flags.CONTROL) * corePowerMult)), coreDesignTable, report.DESIGN, ) report.setData( "Number of Reflector Assemblies", "{}".format(len(core.getAssemblies(Flags.REFLECTOR) * corePowerMult)), coreDesignTable, report.DESIGN, ) report.setData( "Number of Shield Assemblies", "{}".format(len(core.getAssemblies(Flags.SHIELD) * corePowerMult)), coreDesignTable, report.DESIGN, )
def setDimensionReport(self): """Gives a report of the dimensions of this component.""" reportGroup = None for componentType, componentReport in self._COMP_REPORT_GROUPS.items(): if componentType in self.getName(): reportGroup = componentReport break if not reportGroup: return "No report group designated for {} component.".format( self.getName()) reportGroup.header = [ "", "Tcold ({0})".format(self.inputTemperatureInC), "Thot ({0})".format(self.temperatureInC), ] dimensions = { k: self.p[k] for k in self.DIMENSION_NAMES if k not in ("modArea", "area") and self.p[k] is not None } # py3 cannot format None # Set component name and material report.setData("Name", [self.getName(), ""], reportGroup) report.setData("Material", [self.getProperties().name, ""], reportGroup) for dimName in dimensions: niceName = _NICE_DIM_NAMES.get(dimName, dimName) refVal = self.getDimension(dimName, cold=True) hotVal = self.getDimension(dimName) try: report.setData(niceName, [refVal, hotVal], reportGroup) except ValueError: runLog.warning( "{0} has an invalid dimension for {1}. refVal: {2} hotVal: {3}" .format(self, dimName, refVal, hotVal)) # calculate thickness if applicable. suffix = None if "id" in dimensions: suffix = "d" elif "ip" in dimensions: suffix = "p" if suffix: coldIn = self.getDimension("i{0}".format(suffix), cold=True) hotIn = self.getDimension("i{0}".format(suffix)) coldOut = self.getDimension("o{0}".format(suffix), cold=True) hotOut = self.getDimension("o{0}".format(suffix)) if suffix and coldIn > 0.0: hotThick = (hotOut - hotIn) / 2.0 coldThick = (coldOut - coldIn) / 2.0 vals = ( "Thickness (cm)", "{0:.7f}".format(coldThick), "{0:.7f}".format(hotThick), ) report.setData(vals[0], [vals[1], vals[2]], reportGroup) return report.ALL[reportGroup]
def plotBlockFlux(core, fName=None, bList=None, peak=False, adjoint=False, bList2=[]): """ Produce energy spectrum plot of real and/or adjoint flux in one or more blocks. Parameters ---------- core : Core Core object fName : str, optional the name of the plot file to produce. If none, plot will be shown. A text file with the flux values will also be generated if this is non-empty. bList : iterable, optional is a single block or a list of blocks to average over. If no bList, full core is assumed. peak : bool, optional a flag that will produce the peak as well as the average on the plot. adjoint : bool, optional plot the adjoint as well. bList2 : a separate list of blocks that will also be plotted on a separate axis on the same plot. This is useful for comparing flux in some blocks with flux in some other blocks. Notes ----- This is not a great method. It should be cleand up and migrated into ``utils.plotting``. """ class BlockListFlux(object): def __init__(self, nGroup, blockList=[], adjoint=False, peak=False, primary=False): if blockList: self.blockList = blockList self.nGroup = nGroup self.avgFlux = numpy.zeros(self.nGroup) self.peakFlux = numpy.zeros(self.nGroup) self.peak = peak self.adjoint = adjoint if self.adjoint: self.labelAvg = "Average Adjoint Flux" self.labelPeak = "Peak Adjoint Flux" else: self.labelAvg = "Average Flux" self.labelPeak = "Peak Flux" if primary: self.lineAvg = "-" self.linePeak = "-" else: self.lineAvg = "r--" self.linePeak = "k--" def calcAverage(self): for b in self.blockList: thisFlux = numpy.array(b.getMgFlux(adjoint=self.adjoint)) self.avgFlux += numpy.array(thisFlux) if sum(thisFlux) > sum(self.peakFlux): self.peakFlux = thisFlux self.avgFlux = self.avgFlux / len(bList) def setEnergyStructure(self, upperEnergyBounds): self.E = [eMax / 1e6 for eMax in upperEnergyBounds] def makePlotHistograms(self): self.eHistogram, self.avgHistogram = makeHistogram( self.E, self.avgFlux) if self.peak: _, self.peakHistogram = makeHistogram(self.E, self.peakFlux) def checkSize(self): if not len(self.E) == len(self.avgFlux): runLog.error(self.avgFlux) raise def getTable(self): return enumerate(zip(self.E, self.avgFlux, self.peakFlux)) if bList is None: bList = core.getBlocks() bList = list(bList) if adjoint and bList2: runLog.warning("Cannot plot adjoint flux with bList2 argument") return elif adjoint: bList2 = bList try: G = len(core.lib.neutronEnergyUpperBounds) except: runLog.warning("No ISOTXS library attached so no flux plots.") return BlockListFluxes = set() bf1 = BlockListFlux(G, blockList=bList, peak=peak, primary=True) BlockListFluxes.add(bf1) if bList2: bf2 = BlockListFlux(G, blockList=bList2, adjoint=adjoint, peak=peak) BlockListFluxes.add(bf2) for bf in BlockListFluxes: bf.calcAverage() bf.setEnergyStructure(core.lib.neutronEnergyUpperBounds) bf.checkSize() bf.makePlotHistograms() if fName: # write a little flux text file. txtFileName = os.path.splitext(fName)[0] + ".txt" with open(txtFileName, "w") as f: f.write("{0:16s} {1:16s} {2:16s}\n".format("Energy_Group", "Average_Flux", "Peak_Flux")) for g, (eMax, avgFlux, peakFlux) in bf1.getTable(): f.write("{0:12E} {1:12E} {2:12E}\n".format( eMax, avgFlux, peakFlux)) if max(bf1.avgFlux) <= 0.0: runLog.warning("Cannot plot flux with maxval=={0} in {1}".format( maxVal, bList[0])) return plt.figure() plt.plot(bf1.eHistogram, bf1.avgHistogram, bf1.lineAvg, label=bf1.labelAvg) if peak: plt.plot(bf1.eHistogram, bf1.peakHistogram, bf1.linePeak, label=bf1.labelPeak) ax = plt.gca() ax.set_xscale("log") ax.set_yscale("log") plt.xlabel("Energy (MeV)") plt.ylabel("Flux (n/cm$^2$/s)") if peak or bList2: plt.legend(loc="lower right") plt.grid(color="0.70") if bList2: if adjoint: plt.twinx() plt.ylabel("Adjoint Flux (n/cm$^2$/s)", rotation=270) ax2 = plt.gca() ax2.set_yscale("log") plt.plot(bf2.eHistogram, bf2.avgHistogram, bf2.lineAvg, label=bf2.labelAvg) if peak and not adjoint: plt.plot(bf2.eHistogram, bf2.peakHistogram, bf2.linePeak, label=bf2.labelPeak) plt.legend(loc="lower left") plt.title("Group flux") if fName: plt.savefig(fName) report.setData( "Flux Plot {}".format(os.path.split(fName)[1]), os.path.abspath(fName), report.FLUX_PLOT, ) else: plt.show()