Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Photon Transport Simulation with Two Layers of Material & Unstructured Mesh #27

Draft
wants to merge 68 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
23c1469
Add files via upload
anu1217 Oct 4, 2024
8d1f88b
Deleted unused Energy_List lines
anu1217 Oct 4, 2024
d7e58a3
Update OpenMC_PhotonTransport.py
anu1217 Oct 10, 2024
39e2d4b
Delete unused 'Energy_List.txt' line
anu1217 Oct 10, 2024
3dfb567
Delete print statement
anu1217 Oct 10, 2024
5bb02ff
Remove summed strengths text file as output
anu1217 Oct 10, 2024
1fdb1b1
Read summed photon strengths from Source_Mesh_Reader
anu1217 Oct 10, 2024
569eb28
Add source strengths for each individual mesh element
anu1217 Oct 10, 2024
1008270
Update OpenMC_PhotonTransport.py
anu1217 Oct 15, 2024
19e86ef
Adding materials/geometry files
anu1217 Oct 15, 2024
6fa1367
Import materials/geometry from TwoLayers_Materials & TwoLayers_Geometry
anu1217 Oct 15, 2024
c3cf5db
Delete materials and geometry functions from current script
anu1217 Oct 15, 2024
e217e00
Update OpenMC_PhotonTransport.py
anu1217 Oct 25, 2024
94519f9
Update TwoLayers_Materials.py
anu1217 Nov 8, 2024
3854660
Update TwoLayers_Materials.py
anu1217 Nov 11, 2024
a3db102
Update references to some variables
anu1217 Nov 11, 2024
4199c1b
Update TwoLayers_Geometry.py
anu1217 Nov 11, 2024
fdcb11f
Import variables from TwoLayers_Materials
anu1217 Nov 12, 2024
2e209a6
Update directory of OpenMC_PhotonTransport
anu1217 Nov 14, 2024
e8a305a
Update directory of Photon_Tallytovtk
anu1217 Nov 14, 2024
3aa85b3
Update directory of TwoLayers_Geometry
anu1217 Nov 14, 2024
1cff395
Update directory of TwoLayers_Materials
anu1217 Nov 14, 2024
64d14d5
Update directory of Source_Mesh_Reader
anu1217 Nov 14, 2024
88a9e83
Update TwoLayers_Geometry.py
anu1217 Nov 14, 2024
2259f79
Add functions to read, plot, and save data
anu1217 Nov 14, 2024
84bac07
Fix indentation of return statement
anu1217 Nov 15, 2024
0713d73
Created functions to extract and write relevant data
anu1217 Nov 15, 2024
5c03a21
Define arrange_source_strengths function
anu1217 Nov 15, 2024
d761b1c
Add main function that calls all helper functions
anu1217 Nov 15, 2024
fd3cb83
Rewrite make_source in terms of data_extractor from Source_Mesh_Reader
anu1217 Nov 15, 2024
ed0e05d
Fix summation in make_source loop
anu1217 Nov 15, 2024
c7a0c03
Changed variable name in function definition
anu1217 Nov 15, 2024
4568353
Updated main function that calls helper functions
anu1217 Nov 15, 2024
f91820f
Rename write_source_density
anu1217 Nov 19, 2024
2347332
Rewrite extract_source_data
anu1217 Nov 19, 2024
d3b612e
Change extract_source_data docstring
anu1217 Nov 19, 2024
99af15a
Moved Particle_Filter to tallies
anu1217 Nov 19, 2024
bc4e8d3
Add inner_radius as input
anu1217 Nov 19, 2024
e06fba5
Change inputs required for make_source
anu1217 Nov 19, 2024
7afd568
Remove Mesh_Dist as output of make_source
anu1217 Nov 19, 2024
2b5e67b
Change inputs required for tallies & fix capitalization
anu1217 Nov 20, 2024
4fb3156
Change variable names from uppercase to lowercase
anu1217 Nov 20, 2024
c81e21f
Rewrite export_to_xml as create_openmc_model
anu1217 Nov 20, 2024
1ae47ce
Deleted comments describing self-explanatory code
anu1217 Nov 20, 2024
c3509d6
Add docstrings to functions
anu1217 Nov 21, 2024
6676191
Add input to tallies function
anu1217 Nov 21, 2024
e6c9518
Changed bounds to numpy array
anu1217 Nov 25, 2024
3a09bf8
Change the way upper & lower energy bounds are accessed
anu1217 Nov 26, 2024
0d0418b
Fixed typo in make_source
anu1217 Nov 26, 2024
93d5b56
Add inputs to yaml file
anu1217 Nov 26, 2024
80382a4
Update and rename PhotonTransport_Inputs.yaml to WC_Layers/PhotonTran…
anu1217 Nov 26, 2024
ab1b09b
Remove hard-coded inputs that have been moved to yaml
anu1217 Nov 26, 2024
43270df
Merge branch 'cnerg:main' into mesh_photontransport
anu1217 Nov 27, 2024
8aff672
Add information to calculate effective dose
anu1217 Dec 16, 2024
0679a68
Redefine inputs for make_spherical_shells
anu1217 Dec 16, 2024
fc583b1
Edit function docstring
anu1217 Dec 16, 2024
785511e
Changed inputs for extract_source_data
anu1217 Dec 16, 2024
ee8df5b
Add main function to Source_Mesh_Reader
anu1217 Dec 16, 2024
790db58
Add default to argparse input
anu1217 Dec 16, 2024
3b808c1
Add files via upload
anu1217 Dec 16, 2024
052b312
Merge branch 'cnerg:main' into mesh_photontransport
anu1217 Dec 16, 2024
771b147
Add input to function
anu1217 Dec 16, 2024
7f76500
Add docstrings to functions
anu1217 Dec 16, 2024
2a7d52c
Change variable names and inputs
anu1217 Dec 16, 2024
6b18246
Change variable key
anu1217 Dec 16, 2024
a3a9921
Update PhotonTransport_Inputs.yaml
anu1217 Dec 16, 2024
608adba
Fix typo in run mode
anu1217 Dec 16, 2024
15f64ef
Update and rename Source_Mesh_Reader_Inputs.yaml to WC_Layers/Source_…
anu1217 Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions OpenMC_PhotonTransport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 7 10:07:33 2024

@author: Anupama Rajendra
"""

import openmc
import argparse
import numpy as np
from Source_Mesh_Reader import summed_strengths, strengths_list
from TwoLayers_Materials import *
from TwoLayers_Geometry import *

Mesh_File = 'OpenMC_Mesh.h5m'

parser = argparse.ArgumentParser(description="Specify required inputs: file path to ALARA Element Library, element name, inner radius [cm], outer_radius [cm]")

#Required user inputs:
parser.add_argument('--filepath', type=str, required=True)
parser.add_argument('--element_1', type=str, required=True)
parser.add_argument('--element_2', type=str, required=True)
#Shell radii are left as user inputs, but the mesh is specific to W_inner_radius = 1000, W_outer_radius = 1005, C_inner_radius = 995
parser.add_argument('--W_inner_radius', type=float, required=True)
parser.add_argument('--W_outer_radius', type=float, required=True)
parser.add_argument('--C_inner_radius', type=float, required=True)

args = parser.parse_args()

fp = args.filepath
E_1 = args.element_1
E_2 = args.element_2
R_W_1 = args.W_inner_radius
R_W_2 = args.W_outer_radius
R_C_1 = args.C_inner_radius

# fp = '../../ALARA/data/elelib.std'
# E_1 = 'W'
# E_2 = 'C'
# R_W_1 = 1000
# R_W_2 = 1005
# R_C_1 = 995
bounds = [0,
1.00e+4,
2.00e+4,
5.00e+4,
1.00e+5,
2.00e+5,
3.00e+5,
4.00e+5,
6.00e+5,
8.00e+5,
1.00e+6,
1.22e+6,
1.44e+6,
1.66e+6,
2.00e+6,
2.50e+6,
3.00e+6,
4.00e+6,
5.00e+6,
6.50e+6,
8.00e+6,
1.00e+7,
1.20e+7,
1.40e+7,
2.00e+7]

#Define source:
def make_source(W_Shell, C_Shell, Cells):
Source_List = []
Particle_Filter = openmc.ParticleFilter('photon')
Total_Mesh = openmc.UnstructuredMesh(Mesh_File, library='moab')
for index, bound in enumerate(bounds[:-1]):
Mesh_Dist = openmc.stats.MeshSpatial(Total_Mesh, strengths=strengths_list[index], volume_normalized=False)
Energy_Dist = openmc.stats.Uniform(a=bounds[index], b=bounds[index + 1])
#Source strengths given by strengths_list created by Source_Mesh_Reader
Source = Source_List.append(openmc.IndependentSource(space=Mesh_Dist, energy=Energy_Dist, strength=summed_strengths[index], particle='photon', domains=Cells))
return Source_List, Particle_Filter, Total_Mesh, Mesh_Dist

# Define tallies
def tallies(W_Shell, C_Shell, Particle_Filter, Total_Mesh):
Total_Filter = openmc.MeshFilter(Total_Mesh)

neutron_tally = openmc.Tally(tally_id=1, name="Neutron tally")
neutron_tally.scores = ['flux', 'elastic', 'absorption']

# Implementing filter for neutron tally through W shell
cell_filter = openmc.CellFilter([W_Shell, C_Shell])
neutron_tally.filters = [cell_filter, Total_Filter]

# Creating a tally to get the flux energy spectrum.
# An energy filter is created to assign to the flux tally.
energy_filter_flux = openmc.EnergyFilter.from_group_structure("VITAMIN-J-42")

spectrum_tally = openmc.Tally(tally_id=2, name="Flux spectrum")
# Implementing energy and cell filters for flux spectrum tally
spectrum_tally.filters = [cell_filter, Total_Filter, energy_filter_flux, Particle_Filter]
spectrum_tally.scores = ['flux']

tall = openmc.Tallies([neutron_tally, spectrum_tally])
tall.export_to_xml()
return tall, neutron_tally, spectrum_tally, Total_Filter, cell_filter

# Assign simulation settings
def settings(Source_List):
sets = openmc.Settings()
sets.batches = 10
sets.inactive = 1
sets.particles = 100000
sets.source = Source_List
sets.run_mode = 'fixed source'
sets.export_to_xml()
return sets

def plot_universe(Void, W_Shell, C_Shell, Cells):
universe_ss = openmc.Universe(cells=Cells)
Plot = universe_ss.plot(width=(2500.0, 2500.0), basis='xz',
colors={Void: 'blue', W_Shell: 'red', C_Shell: 'green'}, legend=True)
Plot.figure.savefig('Universe')
return universe_ss

# Exporting materials, geometry, and tallies to .xml
def export_to_xml(filepath, element_1, element_2, inner_radius_W, outer_radius_W, inner_radius_C):
OpenMC_SF = mat_lib(filepath)
materials = []
for material_id, element in enumerate(elements):
materials.append(make_element(element, material_id+1, OpenMC_SF))
OpenMC_Mat = all_mat(OpenMC_W, OpenMC_C)
OpenMC_Geometry = make_spherical_shell(inner_radius_W, outer_radius_W, inner_radius_C, OpenMC_W, OpenMC_C)
OpenMC_Source = make_source(OpenMC_Geometry[2], OpenMC_Geometry[3], OpenMC_Geometry[4])
OpenMC_Settings = settings(OpenMC_Source[0])
OpenMC_Tallies = tallies(OpenMC_Geometry[2], OpenMC_Geometry[3], OpenMC_Source[1], OpenMC_Source[2])
OpenMC_Universe = plot_universe(OpenMC_Geometry[1], OpenMC_Geometry[2], OpenMC_Geometry[3], OpenMC_Geometry[4])
#return OpenMC_Materials, OpenMC_Geometry, OpenMC_Source, OpenMC_Settings, OpenMC_Tallies, OpenMC_Universe
return OpenMC_SF, OpenMC_W, OpenMC_C, *OpenMC_Geometry, *OpenMC_Source, OpenMC_Settings, *OpenMC_Tallies

Lib_Lines, M_1, M_2, geometry, Void, W_Shell, C_Shell, Cells, Source_List, Particle_Filter, Total_Mesh, Mesh_Dist, tall, neutron_tally, spectrum_tally, Total_Filter, cell_filter, sets = export_to_xml(fp, E_1, E_2, R_W_1, R_W_2, R_C_1)
32 changes: 32 additions & 0 deletions Photon_TallytoVtk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
"""
Created on Thu Oct 3 23:18:20 2024

@author: Anupama Rajendra
"""
import openmc
import matplotlib.pyplot as plt

with openmc.StatePoint("statepoint.10.h5") as sp:
flux_spectrum = sp.get_tally(id=2)
mesh=sp.meshes[1]
# Get the reshaped tally data
tally_data_reshaped = flux_spectrum.get_reshaped_data(value='mean')

flux_sum_en = tally_data_reshaped.sum(axis=(0,1,3,4,5))
#Vitamin-J energy filter:
e_filter = flux_spectrum.filters[2]
#Lower bounds of the energy bins
e_filter_lower = e_filter.bins[:, 0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of magic numbers here including tally ID, mesh number, axes for sum, etc... more readable if these are named as variables, and might make sense if they lived in a function that took some of these as arguments.


# Plot flux spectrum
fix, ax = plt.subplots()
ax.loglog(e_filter_lower, flux_sum_en, drawstyle='steps-post')
ax.set_xlabel('Energy [eV]')
ax.set_ylabel('Flux [photon-cm/source]')
ax.grid(True, which='both')
plt.savefig('Photon_flux_vs_energy.png')
plt.show()

mesh_data = tally_data_reshaped.sum(axis=(0,2,3,4,5))
vtk = mesh.write_data_to_vtk(filename="Photon_Flux.vtk", datasets={"mean":mesh_data.flatten()})
anu1217 marked this conversation as resolved.
Show resolved Hide resolved
41 changes: 41 additions & 0 deletions Source_Mesh_Reader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
"""
Created on Mon Sep 30 15:16:44 2024

@author: Anupama Rajendra
"""

import h5py
import numpy as np

# Open the HDF5 files
File_1 = h5py.File("source_mesh_1.h5m", 'r')
File_2 = h5py.File("source_mesh_2.h5m", 'r')
Files = [File_1, File_2]

# Number of photon groups
photon_groups = 24

# Process each file
for file_index, file in enumerate(Files):
# Extract data from the current file
tstt = file['tstt']
elements = tstt['elements']
tet_four = elements['Tet4']
tags = tet_four['tags']
sd = tags['source_density'][:]

# Write source density to a separate file for each mesh
with open(f'source_density_{file_index + 1}.txt', 'w') as source:
for tet_element in sd:
source.write(' '.join(map(str, tet_element)) + '\n')

# Calculate summed (and individual mesh) strengths for each photon group
summed_strengths = []
strengths_list = []
anu1217 marked this conversation as resolved.
Show resolved Hide resolved
for group in range(photon_groups):
total_strengths = np.sum(sd[:, group]) # Sum over all mesh elements
summed_strengths.append(total_strengths)
strengths = sd[:,group]
strengths_list.append(strengths)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend putting this in a method that does everything you need for a single file. That kind of modularity enhances readability. You might also want to separate the functions of reading from a file, processing the data, and writing to files, so methods like extract_source_data, arrange_data, write_source_density

anu1217 marked this conversation as resolved.
Show resolved Hide resolved

33 changes: 33 additions & 0 deletions TwoLayers_Geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
"""
Created on Fri Oct 11 15:56:57 2024

@author: Anupama Rajendra
"""

import openmc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear that we need to import openmc here, if this method is only ever used when importing into another script that also imports openmc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something here, but OpenMC_PhotonTransport attempts to access openmc inside TwoLayers_Geometry even when only make_spherical_shells is imported


# Create geometry
#Spherical shell:
def make_spherical_shell(inner_radius_W, outer_radius_W, inner_radius_C, M_1, M_2):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstrings here would help be clear about what everything is doing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes multiple shells, so maybe change the name.

Could also generalize this to take an inner radius plus layers:

Suggested change
def make_spherical_shell(inner_radius_W, outer_radius_W, inner_radius_C, M_1, M_2):
def make_spherical_shells(inner_radius_W, layers):
'''
Builds a set of nested spherical shells, with an inner_radius and
multiple layers each with a material definition and a thickness
inputs
=====
inner_radius : the radius of the inner surface of the first shell
layers : list of tuples where each tuple is (material, thickness)
'''

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think materials will need to be an input as this function is also assigning materials to the geometry. I'm wondering if this can be done with one set of radii/thickness inputs and the materials from TwoLayers_Materials.

Suggested change
def make_spherical_shell(inner_radius_W, outer_radius_W, inner_radius_C, M_1, M_2):
def make_spherical_shell(radii, materials):
'''
Takes a list of materials and radii (outermost to innermost)
and creates an OpenMC Geometry object.
element_list = list of elements (str) in order from radially outermost to innermost
radii = list of the radii (outermost to innermost) that serve as the boundaries of the
spherical shells; must be in the same order as the specified materials
'''
cells = []
for index, material in enumerate(materials):
outer_sphere = openmc.Sphere(r = radii[index]) # add boundary_type here
inner_sphere = openmc.Sphere(r = radii[index + 1])
element_region = +inner_sphere & -outer_sphere
element_shell = openmc.Cell(fill = materials[index], region = element_region)
cells.append(element_shell)
if material == materials[-1]:
void = openmc.Cell(fill = None, region = -inner_sphere)
cells.append(void)
geometry = openmc.Geometry(cells)
return geometry

Copy link
Member

@gonuke gonuke Nov 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the materials are necessary. My version had them bound in the layers. This kind of data structure can lead to clarity because the data gets bound together in instructive ways. An example of calling it might be:

inner_radius = 10.0
# material_a and material_b are OpenMC Materials
layers = [(material_a, 5.0), (material_b, 4.0)]
geometry = make_spherical_shells(inner_radius, layers)

The function may be:

def make_spherical_shells(inner_radius, layers):

    inner_sphere = openmc.Sphere(inner_radius)
    cells = [openmc.Cell(region=-inner_sphere)]
    for (material, thickness) in layers:
        outer_radius = inner_radius + thickness
        outer_sphere = openmc.Sphere(outer_radius)
        cells.append(openmc.Cell(fill = material, region = inner_sphere & -outer_sphere))
        inner_radius = outer_radius
        inner_sphere = outer_sphere
    cells.append(openmc.Cell(region = outer_sphere)
    geometry = openmc.Geometry(cells)

    return geometry

(There may be minor syntax errors, but this is the basic idea)

S_W_1= openmc.Sphere(r=inner_radius_W) #sphere of radius 1000cm
inside_W_sphere_1 = -S_W_1
outside_W_sphere_1 = +S_W_1
S_W_2 = openmc.Sphere(r=outer_radius_W, boundary_type='vacuum')
inside_W_sphere_2 = -S_W_2
outside_W_sphere_2 = +S_W_2
S_W_3 = outside_W_sphere_1 & inside_W_sphere_2 #filled with specified material
anu1217 marked this conversation as resolved.
Show resolved Hide resolved

S_C_1= openmc.Sphere(r=inner_radius_C) #sphere of radius 995cm
inside_C_sphere_1 = -S_C_1
outside_C_sphere_1 = +S_C_1
S_C_3 = outside_C_sphere_1 & inside_W_sphere_1 #filled with specified material

# Mapping materials to geometry:
Void = openmc.Cell(fill=None, region = inside_C_sphere_1)
W_Shell = openmc.Cell(fill=M_1, region=S_W_3)
C_Shell = openmc.Cell(fill=M_2, region=S_C_3)
Cells = [Void, W_Shell, C_Shell]
geometry = openmc.Geometry(Cells)
geometry.export_to_xml()
anu1217 marked this conversation as resolved.
Show resolved Hide resolved
return geometry, Void, W_Shell, C_Shell, Cells
anu1217 marked this conversation as resolved.
Show resolved Hide resolved
32 changes: 32 additions & 0 deletions TwoLayers_Materials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
"""
Created on Fri Oct 11 13:49:23 2024

@author: Anupama Rajendra
"""
import openmc

#ALARA Element Library to read density for specified element:
def mat_lib(filepath):
with open(""+filepath+"") as ALARA_Lib:
Lib_Lines = ALARA_Lib.readlines()
anu1217 marked this conversation as resolved.
Show resolved Hide resolved
return Lib_Lines

def create_densities(elements, Lib_Lines):
density_dict = {}
for element in elements:
for line in Lib_Lines:
if line.split()[0].lower() == element.lower():
density_dict[element] = float(line.strip().split()[3])
return density_dict

# Create materials & export to XML:
#Simulating tungsten shell:
def make_element(elements, density_dict):
mats = openmc.Materials([])
for element_id, element in enumerate(elements):
mat = openmc.Material(material_id=element_id+1, name=element)
mat.add_element(element, 1.00)
mat.set_density('g/cm3', density_dict.get(f'{element}'))
mats.append(mat)
return mats
anu1217 marked this conversation as resolved.
Show resolved Hide resolved