-
Notifications
You must be signed in to change notification settings - Fork 81
/
fuelHandlerInterface.py
183 lines (160 loc) · 6.74 KB
/
fuelHandlerInterface.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# Copyright 2022 TerraPower, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A place for the FuelHandler's Interface"""
from armi import interfaces, runLog
from armi.utils import plotting
from armi.physics.fuelCycle import fuelHandlerFactory
class FuelHandlerInterface(interfaces.Interface):
"""
Moves and/or processes fuel in a Standard Operator.
Fuel management traditionally runs at the beginning of a cycle, before
power or temperatures have been updated. This allows pre-run fuel management
steps for highly customized fuel loadings. In typical runs, no fuel management
occurs at the beginning of the first cycle and the as-input state is left as is.
"""
name = "fuelHandler"
def __init__(self, r, cs):
interfaces.Interface.__init__(self, r, cs)
self.cycle = 0
# assembly name key, (x, y) values. used for making shuffle arrows.
self.oldLocations = {}
# need order due to nature of moves but with fast membership tests
self.moved = []
# filled during summary of EOC time in years of each cycle (time at which shuffling occurs)
self.cycleTime = {}
@staticmethod
def specifyInputs(cs):
files = {
cs.getSetting(settingName): [
cs[settingName],
]
for settingName in ["shuffleLogic", "explicitRepeatShuffles"]
if cs[settingName]
}
return files
def interactBOC(self, cycle=None):
"""
Beginning of cycle hook. Initiate all cyclical fuel management processes.
If requested, first have the lattice physics system update XS.
"""
# if requested, compute lattice physics here instead of after fuel management.
# This enables XS to exist for branch searching, etc.
mc2 = self.o.getInterface(function="latticePhysics")
if mc2 and self.cs["runLatticePhysicsBeforeShuffling"]:
runLog.extra(
'Running {0} lattice physics before fuel management due to the "runLatticePhysicsBeforeShuffling"'
" setting being activated.".format(mc2)
)
mc2.interactBOC(cycle=cycle)
if self.enabled():
self.manageFuel(cycle)
self.validateLocations()
def interactEOC(self, cycle=None):
"""
End of cycle hook. Record cycle time and report number of assemblies in SFP.
"""
# keep track of the EOC time in years.
self.cycleTime[cycle] = self.r.p.time
runLog.extra(
"There are {} assemblies in the Spent Fuel Pool".format(
len(self.r.core.sfp)
)
)
def interactEOL(self):
"""
End of life hook. Generate operator life shuffle report.
"""
self.makeShuffleReport()
def manageFuel(self, cycle):
"""
Perform the fuel management for a given cycle.
"""
fh = fuelHandlerFactory.fuelHandlerFactory(self.o)
fh.preoutage()
## is the factor list useful at this point?
shuffleFactors = fh.getFactorList(cycle)
fh.outage(shuffleFactors)
if self.cs["plotShuffleArrows"]:
arrows = fh.makeShuffleArrows()
plotting.plotFaceMap(
self.r.core,
"percentBu",
labelFmt=None,
fName="{}.shuffles_{}.png".format(self.cs.caseTitle, self.r.p.cycle),
shuffleArrows=arrows,
)
plotting.close()
def validateLocations(self):
"""
Check that all assemblies have a unique location in the core.
"""
locations = [
assembly.getLocation()
for assembly in self.r.core.getAssemblies(includeAll=True)
if assembly.getLocation()[:3].isnumeric()
]
if len(locations) != len(set(locations)):
raise ValueError("Two or more assemblies share the same core location")
def makeShuffleReport(self):
"""
Create a data file listing all the shuffles that occurred in a case.
This can be used to export shuffling to an external code or to perform
explicit repeat shuffling in a restart. It creates a *SHUFFLES.txt file
based on the Reactor.moveList structure
See Also
--------
readMoves : reads this file and parses it.
"""
fname = self.cs.caseTitle + "-SHUFFLES.txt"
out = open(fname, "w")
for cycle in range(self.cs["nCycles"]):
# Write cycle header to the report
out.write("Before cycle {0}:\n".format(cycle + 1))
# Pull move list for the cycle
movesThisCycle = self.r.core.moveList.get(cycle)
if movesThisCycle is not None:
for (
fromLoc,
toLoc,
chargeEnrich,
assemblyType,
movingAssemName,
) in movesThisCycle:
enrichLine = " ".join(
["{0:.8f}".format(enrich) for enrich in chargeEnrich]
)
if fromLoc in ["ExCore", "SFP"]:
# this is a re-entering assembly. Give extra info so repeat shuffles can handle it
out.write(
"{0} moved to {1} with assembly type {2} ANAME={4} with enrich list: {3}\n"
"".format(
fromLoc,
toLoc,
assemblyType,
enrichLine,
movingAssemName,
)
)
else:
# skip extra info. regular expression in readMoves will handle it just fine.
out.write(
"{0} moved to {1} with assembly type {2} with enrich list: {3}\n"
"".format(fromLoc, toLoc, assemblyType, enrichLine)
)
out.write("\n")
out.close()
def workerOperate(self, cmd):
"""Delegate mpi command to the fuel handler object."""
fh = fuelHandlerFactory.fuelHandlerFactory(self.o)
return fh.workerOperate(cmd)