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

"Added enrich function to material-db-material_db_tools" #36

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

FusionSandwich
Copy link
Contributor

It is different from the original enrich function from pr 19. This is to account for a rounding issue found during a few pytests I ran.

@FusionSandwich FusionSandwich linked an issue Aug 6, 2024 that may be closed by this pull request
2 tasks
@FusionSandwich FusionSandwich changed the title "Added enrich fucntion to material-db-material_db_tools" "Added enrich function to material-db-material_db_tools" Aug 6, 2024
Copy link
Member

@gonuke gonuke left a comment

Choose a reason for hiding this comment

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

Thanks @FusionSandwich - a few design ideas to discuss

"""
# Convert all keys to PyNE nuclide IDs
material_composition = {
nucname.id(k): Decimal(str(v)) for k, v in material_composition.items()
Copy link
Member

Choose a reason for hiding this comment

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

Decimal is new to me. I think I see some of the benefits, but it probably makes sense to introduce it with a complete overhaul - and PyNE's just going to convert things back to floats.

You also change this back to a float below anyay.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Converting material_composition and isotope_enrichments allows the enrichment function to take either letters or numbers for the isotopes and elements as input. With how Pyne likes to switch between letter and number names for materials, it makes the function a lot more flexible. In regards to returning it as a float, I had left it like that for the pytests. But I'm thinking of rewriting them to all work with decimal and/or using the pytest.approx. That should remove the issues of pytests failing from testing two floats together.

Enrich a specific element in a material composition by replacing it with its isotopes.

Args:
material_composition (Dict[Union[int, str], float]): The original material composition.
Copy link
Member

Choose a reason for hiding this comment

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

Does this method only work for mass fractions? Or maybe it works for both as long as the fraction in material_composition are the same basis as the fractions in isotope_enrichments?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right that they both need to be on the same basis for the current version of the enrich function. With the logic currently in the make_mat_from_atom you'd have to make your function that converts the mass fraction to an atomic fraction.

Copy link
Member

Choose a reason for hiding this comment

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

Might be good to add this to the docstring - that if the material composition is defined as atom fractions, the enrichment must be as well, and vice versa.

if element_to_enrich_id in material_composition:
fract = material_composition.pop(element_to_enrich_id)
for nuclide, enrich_frac in isotope_enrichments.items():
material_composition[nuclide] = enrich_frac * fract
Copy link
Member

Choose a reason for hiding this comment

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

One of the lingering concerns I have is about assumptions regarding elemental mass fractions when we change enrichment. We should discuss. I think it may depend on how materials are manufactured and/or specified, and may be outside the scope of our work, but we should probably be clear about our assumptions.

Here is an example of what I mean: I could imagine a material that is specified as being 10% Li by mass, where that really represents an atom ratio that is determiend by the manufacturing process. If we change the Li enrichment substantially, the molar mass of Li changes, and the mass ratio for a given atom ratio changes.

In most cases, this will be a small effect, but we need to be sure we understand exactly what the impact is and how to document it for users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the example you provided, a user could specify their original material which is 10% Li by mass, then convert the material to atomic fraction. Then if they enriched either material using make_mat_from_atom that would allow them to easily maintain their atomic ratio while enriching the element. I ran a test based on your example using Li2O3Ti with 10% mass lithium and enriching it to 0 and 100%. The elemental mass fraction of lithium stayed at 10%. The atomic mass fraction of lithium changed by 3.5% from the 0% to 100% lithium 6 enrichment case, or roughly +-5% relative to the overall atomic mass. We could tell them that the enrichment function does not currently take into consideration changes in density that result from enrichment and that the density provided will be the final material density. The user-specified fractions for either case are what the material's final composition is.

Comment on lines +83 to +85
isotope_enrichments = {
nucname.id(k): Decimal(str(v)) for k, v in isotope_enrichments.items()
}
Copy link
Member

Choose a reason for hiding this comment

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

Do you need this step? It seems that it's just converting the key from potentially being a valid string representation to being a canonical nucname ID. The string representation will work below, won't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this provides protection for the cases where the isotope_enrichment is provided with letters such as "Li6"

Copy link
Member

Choose a reason for hiding this comment

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

Given the way you use this data below, I don't think it matters what form is uses. You don't compare it to anything, but just use it to define a composition. PyNE doesn't care if you mix integers and strings for this.

}
element_to_enrich_id = nucname.id(element_to_enrich)

if element_to_enrich_id in material_composition:
Copy link
Member

Choose a reason for hiding this comment

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

Since you have to loop through the materials above to convert the keys to the nucname ID, perhaps this whole method could be inside that loop.

For each material:
     convert to ID
     if ID is the element_to_enrich_id
         replace with enrichment stuff
    else
        fill in composition

Comment on lines +80 to +94
material_composition = {
nucname.id(k): Decimal(str(v)) for k, v in material_composition.items()
}
isotope_enrichments = {
nucname.id(k): Decimal(str(v)) for k, v in isotope_enrichments.items()
}
element_to_enrich_id = nucname.id(element_to_enrich)

if element_to_enrich_id in material_composition:
fract = material_composition.pop(element_to_enrich_id)
for nuclide, enrich_frac in isotope_enrichments.items():
material_composition[nuclide] = enrich_frac * fract

# Convert back to float for consistency with the original function signature
return {k: float(v) for k, v in material_composition.items()}
Copy link
Member

Choose a reason for hiding this comment

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

I think this will do the same thing with less converting things back and forth, fewer loops, and valid with PyNE's interface.

Suggested change
material_composition = {
nucname.id(k): Decimal(str(v)) for k, v in material_composition.items()
}
isotope_enrichments = {
nucname.id(k): Decimal(str(v)) for k, v in isotope_enrichments.items()
}
element_to_enrich_id = nucname.id(element_to_enrich)
if element_to_enrich_id in material_composition:
fract = material_composition.pop(element_to_enrich_id)
for nuclide, enrich_frac in isotope_enrichments.items():
material_composition[nuclide] = enrich_frac * fract
# Convert back to float for consistency with the original function signature
return {k: float(v) for k, v in material_composition.items()}
enriched_composition = {}
element_to_enrich_id = nucname.id(element_to_enrich)
for element, fract in material_composition.items():
if nucname.id(element) == element_to_enrich_id:
for nuclide, enrich_frac in isotope_enrichments.items():
enriched_composition[nuclide] = enrich_frac * fract
else:
enriched_composition[element] = fract
return enriched_composition

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Move standard methods to a single module and separate from standard input
2 participants