|
| 1 | +""" |
| 2 | +This script builds a simple geometry |
| 3 | +""" |
| 4 | + |
| 5 | +import argparse |
| 6 | +from math import log10 |
| 7 | +from mgga import MGGA |
| 8 | + |
| 9 | +import numpy as np |
| 10 | +import openmc |
| 11 | + |
| 12 | +import sys |
| 13 | +import json |
| 14 | + |
| 15 | +def add_element(element,material,fraction,fraction_type): |
| 16 | + to_add = openmc.Element(element) |
| 17 | + to_add = to_add.expand(fraction,fraction_type) |
| 18 | + |
| 19 | + for nuclide in to_add: |
| 20 | + material.add_nuclide(nuclide[0],nuclide[1],percent_type=nuclide[2]) |
| 21 | + |
| 22 | +class openmc_problem(): |
| 23 | + def __init__(self): |
| 24 | + self.materials = {} |
| 25 | + self.model = None |
| 26 | + self.num_x = 0 |
| 27 | + self.num_y = 0 |
| 28 | + self.x_bounds = [] |
| 29 | + self.y_bounds = [] |
| 30 | + self.x_planes = [] |
| 31 | + self.y_planes = [] |
| 32 | + self.cells = [] |
| 33 | + self.tallies = {} |
| 34 | + |
| 35 | + def setup(self, num_x, x_bounds, num_y, y_bounds): |
| 36 | + self.num_x = num_x |
| 37 | + self.num_y = num_y |
| 38 | + |
| 39 | + self.x_bounds = np.linspace(x_bounds[0], x_bounds[1], num_x) |
| 40 | + self.y_bounds = np.linspace(y_bounds[0], y_bounds[1], num_y) |
| 41 | + |
| 42 | + for i,x in enumerate(self.x_bounds): |
| 43 | + plane = openmc.XPlane(x0=x, name = 'xplane_' + str(i)) |
| 44 | + self.x_planes.append(plane) |
| 45 | + |
| 46 | + for i,y in enumerate(self.y_bounds): |
| 47 | + plane = openmc.YPlane(y0=y, name = 'yplane_' + str(i)) |
| 48 | + self.y_planes.append(plane) |
| 49 | + |
| 50 | + self.__build_model() |
| 51 | + |
| 52 | + def __build_materials(self): |
| 53 | + breeder = openmc.Material(name='Lithium') |
| 54 | + breeder.set_density('g/cm3', 0.534) |
| 55 | + add_element('Li',breeder,100,'ao') |
| 56 | + self.materials[0] = breeder |
| 57 | + |
| 58 | + multiplier = openmc.Material(name="Lead") |
| 59 | + multiplier.set_density('g/cm3', 12.3) |
| 60 | + add_element('Pb', multiplier, 100, 'ao') |
| 61 | + self.materials[1] = multiplier |
| 62 | + |
| 63 | + be = openmc.Material(name="Beryllium") |
| 64 | + be.set_density('g/cm3', 1.85) |
| 65 | + add_element('Be', be, 100, 'ao') |
| 66 | + self.materials[2] = be |
| 67 | + |
| 68 | + lithium6 = openmc.Material(name="lithium-6") |
| 69 | + lithium6.set_density('g/cm3', 0.534) |
| 70 | + lithium6.add_nuclide('Li6',100,'ao') |
| 71 | + self.materials[3] = lithium6 |
| 72 | + |
| 73 | + lithium7 = openmc.Material(name="lithium-7") |
| 74 | + lithium7.set_density('g/cm3', 0.534) |
| 75 | + lithium7.add_nuclide('Li7',100,'ao') |
| 76 | + self.materials[4] = lithium7 |
| 77 | + |
| 78 | + bismuth = openmc.Material(name="Bismuth") |
| 79 | + bismuth.set_density('g/cm3', 9.747) |
| 80 | + add_element('Bi',bismuth,100,'ao') |
| 81 | + self.materials[5] = bismuth |
| 82 | + |
| 83 | + def __build_tallies(self, flux_cells, tbr_cells): |
| 84 | + |
| 85 | + neutron_filter = openmc.ParticleFilter('neutron', filter_id=1) |
| 86 | + cell_filter = openmc.CellFilter(flux_cells, filter_id=2) |
| 87 | + tbr_cell_filter = openmc.CellFilter(tbr_cells, filter_id=3) |
| 88 | + |
| 89 | + tbr_tally = openmc.Tally(tally_id = 1, name="tbr") |
| 90 | + tbr_tally.scores = ['(n,t)'] |
| 91 | + tbr_tally.estimator = 'tracklength' |
| 92 | + tbr_tally.filters = [tbr_cell_filter, neutron_filter] |
| 93 | + |
| 94 | + flux_tally = openmc.Tally(tally_id = 2, name="flux") |
| 95 | + flux_tally.scores = ['flux'] |
| 96 | + flux_tally.estimator = 'tracklength' |
| 97 | + flux_tally.filters = [cell_filter, neutron_filter] |
| 98 | + flux_tally.triggers = [openmc.Trigger('rel_err',0.05)] |
| 99 | + flux_tally.triggers[0].scores = ['flux'] |
| 100 | + |
| 101 | + tallies = openmc.Tallies([tbr_tally,flux_tally]) |
| 102 | + self.model.tallies = tallies |
| 103 | + |
| 104 | + # generate the fitness for the current generation and |
| 105 | + # index |
| 106 | + def generate_fitness(self, directory, sp_name = "statepoint.10.h5"): |
| 107 | + sp = openmc.StatePoint(directory + '/' + sp_name) |
| 108 | + tbr = sp.get_tally(name = 'tbr') |
| 109 | + cells = [] |
| 110 | + [cells.append(x.id) for x in self.cells] |
| 111 | + tbr_data = tbr.get_slice(scores=['(n,t)'],filters=[openmc.CellFilter], filter_bins = [tuple(cells)]) |
| 112 | + tbr_ave = tbr_data.mean |
| 113 | + |
| 114 | + # maximise tbr |
| 115 | + fitness = sum(tbr_ave)[0][0] |
| 116 | + sp.close() |
| 117 | + return fitness |
| 118 | + |
| 119 | + def assign_genome(self, genome): |
| 120 | + idx = 0 |
| 121 | + for x in range(self.num_x-1): |
| 122 | + for y in range(self.num_y-1): |
| 123 | + # set the material given the position in genome |
| 124 | + mat = self.materials[genome[idx]] |
| 125 | + self.cells[idx].fill = mat |
| 126 | + idx = idx + 1 |
| 127 | + |
| 128 | + # given the genome build the region of geometry |
| 129 | + # to optimise |
| 130 | + def build_geometry(self): |
| 131 | + |
| 132 | + univ = openmc.Universe(name='optimisation') |
| 133 | + cells = [] |
| 134 | + idx = 0 |
| 135 | + for x in range(self.num_x-1): |
| 136 | + for y in range(self.num_y-1): |
| 137 | + # set the material given the position in genome |
| 138 | + #mat = self.materials[genome[idx]] |
| 139 | + cell = openmc.Cell(region = +self.x_planes[x] & -self.x_planes[x+1] & +self.y_planes[y] & -self.y_planes[y+1]) |
| 140 | + cells.append(cell) |
| 141 | + # increment index |
| 142 | + idx = idx + 1 |
| 143 | + |
| 144 | + univ.add_cells(cells) |
| 145 | + |
| 146 | + self.cells = cells |
| 147 | + |
| 148 | + # tally the cells in the last x row |
| 149 | + tally_cells = cells[-self.num_y:] |
| 150 | + |
| 151 | + self.__build_tallies(tally_cells, cells) |
| 152 | + |
| 153 | + return univ |
| 154 | + |
| 155 | + # run specific settings |
| 156 | + def __set_settings(self): |
| 157 | + self.model.settings.run_mode = 'fixed source' |
| 158 | + self.model.settings.batches = 10 |
| 159 | + self.model.settings.particles = 100000 |
| 160 | + |
| 161 | + # make the source spatial dist |
| 162 | + x_dist = openmc.stats.Discrete([0.0],[1.0]) |
| 163 | + y_dist = openmc.stats.Uniform(a=self.y_planes[0].y0, b=self.y_planes[-1].y0) |
| 164 | + z_dist = openmc.stats.Discrete([0.0],[1.0]) |
| 165 | + spatial = openmc.stats.multivariate.CartesianIndependent(x_dist, y_dist, z_dist) |
| 166 | + # make the angular dist |
| 167 | + angle_dist = openmc.stats.Monodirectional(reference_uvw = [1,0,0]) |
| 168 | + # make the energy dist |
| 169 | + energy_dist = openmc.stats.Discrete([14.06e6],[1.0]) |
| 170 | + source = openmc.Source(space = spatial, angle = angle_dist, energy = energy_dist) |
| 171 | + self.model.settings.source = source |
| 172 | + |
| 173 | + # main build function |
| 174 | + def __build_model(self): |
| 175 | + |
| 176 | + self.model = openmc.model.Model() |
| 177 | + |
| 178 | + self.__build_materials() |
| 179 | + |
| 180 | + # build the region to optimise |
| 181 | + optimisation = self.build_geometry() |
| 182 | + |
| 183 | + # Create a cell filled with the lattice |
| 184 | + inside_boundary = -self.y_planes[-1] & +self.y_planes[0] & -self.x_planes[-1] & +self.x_planes[0] |
| 185 | + outside_boundary = +self.y_planes[-1] | -self.y_planes[0] | +self.x_planes[-1] | -self.x_planes[0] |
| 186 | + main_cell = openmc.Cell(fill=optimisation, region=inside_boundary) |
| 187 | + eou = openmc.Cell(region = outside_boundary) |
| 188 | + # Finally, create geometry by providing a list of cells that fill the root |
| 189 | + # universe |
| 190 | + self.model.geometry = openmc.Geometry([main_cell,eou]) |
| 191 | + |
| 192 | + self.x_planes[0].boundary_type = 'vacuum' |
| 193 | + self.x_planes[-1].boundary_type = 'vacuum' |
| 194 | + |
| 195 | + self.y_planes[0].boundary_type = 'vacuum' |
| 196 | + self.y_planes[-1].boundary_type = 'vacuum' |
| 197 | + |
| 198 | + self.__set_settings() |
| 199 | + |
| 200 | +# using slurm array job build description |
| 201 | +def build_slurm(generation): |
| 202 | + contents = [] |
| 203 | + contents.append('#!/bin/bash') |
| 204 | + contents.append('#') |
| 205 | + contents.append('#SBATCH --job-name=mgga') |
| 206 | + contents.append('#SBATCH -A UKAEA-AP001-CPU') |
| 207 | + contents.append('#SBATCH -p cclake') |
| 208 | + contents.append('#SBATCH --nodes=1') |
| 209 | + contents.append('#SBATCH --ntasks=56') |
| 210 | + contents.append('#SBATCH --time=36:00:00') |
| 211 | + contents.append('#SBATCH --output=array_%A-%a.out') |
| 212 | + contents.append('#SBATCH --array=1-1000') |
| 213 | + contents.append(' ') |
| 214 | + contents.append('module purge') |
| 215 | + contents.append('module load rhel7/default-ccl') |
| 216 | + contents.append('module load openmpi/gcc/9.2/4.0.1') |
| 217 | + contents.append(' ') |
| 218 | + contents.append('cd $WORKDIR') |
| 219 | + contents.append('# IDX should match the folders inside generation') |
| 220 | + contents.append('IDX=$(($SLURM_ARRAY_TASK_ID - 1))') |
| 221 | + contents.append('cd ' + str(generation) + '/$IDX') |
| 222 | + contents.append('export OPENMC_CROSS_SECTIONS=/home/dc-davi4/openmc-data/fendl-3.1d-hdf5/cross_sections.xml') |
| 223 | + contents.append('openmc') |
| 224 | + contents.append('cd ..') |
| 225 | + |
| 226 | + with open('mgga_openmc.slurm','w') as f: |
| 227 | + f.writelines(s + '\n' for s in contents) |
| 228 | + |
| 229 | +def write_population(population, generation): |
| 230 | + data = {"population": [population]} |
| 231 | + json_string = json.dumps(data) |
| 232 | + jsonfile = open("population_" + str(generation) + ".json",'w') |
| 233 | + jsonfile.write(json_string) |
| 234 | + jsonfile.close() |
| 235 | + |
| 236 | +def read_population(generation): |
| 237 | + data = None |
| 238 | + |
| 239 | + fileObject = open("population_" + str(generation) + ".json", "r") |
| 240 | + jsonContent = fileObject.read() |
| 241 | + data = json.loads(jsonContent) |
| 242 | + return data["population"][0] |
| 243 | + |
| 244 | +if __name__ == '__main__': |
| 245 | + |
| 246 | + # Set up command-line arguments for generating/running the model |
| 247 | + parser = argparse.ArgumentParser() |
| 248 | + parser.add_argument('--generate') |
| 249 | + parser.add_argument('--run', action='store_true') |
| 250 | + parser.add_argument('--initialise', action='store_true') |
| 251 | + parser.add_argument('--input') |
| 252 | + args = parser.parse_args() |
| 253 | + # MGGA class |
| 254 | + mgga = MGGA() |
| 255 | + |
| 256 | + if args.input: |
| 257 | + f = open(args.input) |
| 258 | + data = json.load(f) |
| 259 | + mgga.seed = data["mgga_settings"]["seed"] |
| 260 | + mgga.population_size = data["mgga_settings"]["population"] |
| 261 | + mgga.generations = data["mgga_settings"]["generations"] |
| 262 | + mgga.crossover_prob = data["mgga_settings"]["crossover_prob"] |
| 263 | + mgga.mutation_prob = data["mgga_settings"]["mutation_prob"] |
| 264 | + mgga.copy_prob = data["mgga_settings"]["copy_prob"] |
| 265 | + mgga.chromosome_length = data["mgga_settings"]["chromosome_length"] |
| 266 | + mgga.num_genes = data["mgga_settings"]["num_of_states"] |
| 267 | + mgga.percentage_worst = data["mgga_settings"]["percentage_worst"] |
| 268 | + mgga.tornament_size = data["mgga_settings"]["tornament_size"] |
| 269 | + mgga.initialise() |
| 270 | + f.close() |
| 271 | + else: |
| 272 | + print('No input specified, use --input') |
| 273 | + sys.exit() |
| 274 | + |
| 275 | + # initialise the first generation |
| 276 | + if args.initialise: |
| 277 | + mgga.fill_population() |
| 278 | + openmc_problem = openmc_problem() |
| 279 | + openmc_problem.setup(10,[0,100],10,[0,200]) |
| 280 | + for idx,i in enumerate(mgga.population): |
| 281 | + openmc_problem.assign_genome(i) |
| 282 | + openmc_problem.model.export_to_xml(directory='0/'+str(idx)) |
| 283 | + build_slurm(0) |
| 284 | + write_population(mgga.population, 0) |
| 285 | + |
| 286 | + # need to write a filename dependent population file!! |
| 287 | + |
| 288 | + # generate the next generation |
| 289 | + if args.generate: |
| 290 | + generation = int(args.generate) |
| 291 | + population = read_population(generation-1) |
| 292 | + mgga.population = population |
| 293 | + genomes = mgga.population |
| 294 | + openmc_problem = openmc_problem() |
| 295 | + openmc_problem.setup(10,[0,100],10,[0,200]) |
| 296 | + # loop over each of the genomes |
| 297 | + fitness = [] |
| 298 | + for idx,i in enumerate(genomes): |
| 299 | + # folder for the current problem |
| 300 | + directory = str(generation-1) + '/' + str(idx) |
| 301 | + fit = openmc_problem.generate_fitness(directory) |
| 302 | + fitness.append(fit) |
| 303 | + # set the fitness |
| 304 | + mgga.fitness = fitness |
| 305 | + print('max fitness: ' + str(max(fitness))) |
| 306 | + print('min fitness: ' + str(min(fitness))) |
| 307 | + |
| 308 | + mgga.sample_population() |
| 309 | + for idx,i in enumerate(mgga.children): |
| 310 | + openmc_problem.assign_genome(i) |
| 311 | + openmc_problem.model.export_to_xml(directory=str(generation) + '/'+str(idx)) |
| 312 | + write_population(mgga.children,generation) |
| 313 | + |
| 314 | +""" |
| 315 | + # translate to a higher resolution |
| 316 | + if args.translate: |
| 317 | + genomes = mgga.population |
| 318 | +""" |
0 commit comments