terrapower / armi

An open-source nuclear reactor analysis automation framework that helps design teams increase efficiency and quality
https://terrapower.github.io/armi/
Apache License 2.0
216 stars 87 forks source link

Request: Defining `Custom` material properties in the blueprints #535

Open jakehader opened 2 years ago

jakehader commented 2 years ago

When using the custom isotopics within the blueprints file it allows a user to specify number densities or mass fractions with some specified density for defining the initial composition of the material. In this case, a user can choose to use material: Custom or choose to use material: X, where X can be something like SS316 or UO2 as an example.

When specifying material: X, the ARMI framework will initialize the component to have material X and then it will default to using the Python methods for getting different material properties that are implemented (e.g., density, linear expansion coefficient, heat capacity, etc.). Although when a user specifies Custom as the material then there is limited functionality to specify these types of material properties.

blocks:
    inner fuel:
        clad: &component_fuel_clad
            shape: Circle
            material: SS316
            Tinput: 20.0
            Thot: 20.0
            id: 0.64
            od: 0.77
            mult: 271
        fuel:
            shape: Circle
            material: Custom
            isotopics: InnerFuel
            Tinput: 20.0
            Thot: 100.0
            id: 0.0
            mult: clad.mult
            od: clad.id

Sometimes when doing benchmark analyses it is useful to specify the number densities of a material using custom isotopics while still maintaining the use of the Custom material so that the density is not inherited from a given material, but instead use the number densities that are specified by the user. In this case, it would be nice if a user could inherit some methods from an existing material (i.e., linear expansion coefficient), but not all of them.

I could imagine a section in the blueprints that could allow a Custom material object to be built. Maybe this means allowing the user to import custom material definitions from a separate Python script (similar to shuffleLogic.py) or maybe this means just providing something like:

materials:
    CustomFuel:
        isotopics: InnerFuel
        linearExpansion: UZr.linearExpansion
        heatCapacity: SS316.heatCapacity
blocks:
    inner fuel:
        clad: &component_fuel_clad
            shape: Circle
            material: SS316
            Tinput: 20.0
            Thot: 20.0
            id: 0.64
            od: 0.77
            mult: 271
        fuel:
            shape: Circle
            material: CustomFuel
            Tinput: 20.0
            Thot: 100.0
            id: 0.0
            mult: clad.mult
            od: clad.id

In this scenario, the isotopics are now a child of the CustomFuel material that is defined in the materials section (rather than it being its own section on the component definition) and then the linearExpansion and heatCapacity functionalities are tied to the pre-existing UZr and SS316 methods that are already implemented in the code base.

This would allow me as the user to circumvent the warning in https://github.com/terrapower/armi/pull/496 and have more control over this behavior without having to implement a new Material class in the application I am working off of to handle both the correct treatment of a custom number density input along with the allowing the material to thermally expand, etc.

Please let me know what you think about this @ntouran / @john-science. The specific use case for this is when a benchmark specifies the number densities of a component/block and when we would like to have linear expansion defined to support a reactivity coefficient calculation.

ntouran commented 2 years ago

Hmm. I'm sympathetic but want to hear more. We do allow users to provide fresh or subclass materials in plugins that can be named whatever they want. For a benchmark, it might be most expressive and safest and easiest to maintain to have the specific materials of the benchmark defined as regular old ARMI materials in Python code. Making more ways to make or point to material properties in blueprints doesn't seem necessary at first glance given plugin materials.

But clearly you have a request, and that's legit. I guess I want to hear more about why just doing materials in a plugin isn't better. It seems like you want to have user input rather than developer input (kinda like shuffleLogic, as you say). But maybe we could allow "user input" to be used using the plugin framework? Then the user would just bring a my-materials.py and we'd point to it during app configuration.

jakehader commented 2 years ago

I think the challenge that I am trying to find a balance with is being able to implement "one-off" materials for benchmark/validation efforts without extending the code base of the ARMI application unnecessarily (i.e., trading user input verification vs custom code verification). In a sense I am trying to see how much of the existing framework/material plugin(s) can be leveraged to get the capabilities of still using a Custom material but with user-defined (or predefined) material properties to treat thermal expansion.

Using a material class that is not Custom with number densities set does not seem to be working as I would like (as in the number densities that are specified are seemly being scaled(?) by the actual density of the material class that is set) and if I switch to Custom to get the intended behavior that I am looking for then the ability to run calculations that rely on thermal expansion sort of go out the door.

One thing that could be done is to implement a base material class that accepts number densities and derives the mass density from the custom isotopics that are provided and then just subclass specific benchmark materials from that. I guess what I am really looking for with this issue/ticket is a discussion on what the best approach could be for treating benchmarks when number densities are a given and when I want to leverage material properties on a material class (not something that Custom currently offers). Totally up to suggestions from you though!

keckler commented 2 years ago

I personally have taken the approach of piece-mealing a custom material class for situations like this.

I am working off of some documents that specify the isotopics in MOX fuel in a very specific way that is different than how the MOX material class expects them. So I made a new material class of my own that accepts the isotopics in the way that aligns with my references, but then just derives all the material properties from existing material classes in ARMI.

I have found this to be an acceptable approach, but depending on how many times you need to do this for a given modeling effort, it could easily be quite annoying.

jakehader commented 2 years ago

@keckler are you planning on implementing these custom material classes as a separate material plugin to the application for the benchmarks to run or do you have a different approach?

keckler commented 2 years ago

I guess I am cheating a little bit because what I am doing will only work for programmatically-defined models...

I have given my model its own python entry point. So Python actually runs from the directory in which my input files live. I put my custom material classes in this same directory, so when Python runs it recognizes the modules. And ARMI automatically registers any material classes in the run directory, so their names are recognized from within the blueprints file.

This is a little different than if you were to run your model through the ARMI entry point directly. In that case, you would have to put your custom material classes into the ARMI directory for them to be recognized and registered. That's probably not something you'd want to do, though, because then the material class would likely be outside the repo that your are using to version control your model.

This does bring up an alternative option, though. We could have ARMI look in the directory of the blueprints file for any custom material classes to register, in the same way that it already does in the ARMI directory. Then you could just plop your custom material classes in a module that sites next to your blueprints file, and everything would work great.

Eh?

jakehader commented 2 years ago

I like the idea of importing custom materials from a custom Python script, if @ntouran is OK with that.

jakehader commented 2 years ago

After talking with @ntouran offline, it's not unreasonable to expand the API functionality of the framework to allow for discovery of a material plugin. I am thinking that it makes sense for this discovery feature to be optional for applications and in order to enable it, the application will have to explicitly provide a directory path to search for these custom scripts.

This differs a bit from the fuel management / shuffle logic because we probably need the materials data to be configured with the app rather than being loaded based on case settings.

I like the idea of by default (if this feature is enabled) that the app will always just search in the current working directory for a python file that meets the criteria (e.g., implements a plugin class that inherits from the ARMI plugin and the file meets some application-defined naming convention).

If a custom materials plugin is configured then it will be added to the materials namespace list. Likely, if a custom material library is being loaded in, then it should probably take priority over the other material packages, but maybe this can also be configurable for the application? We either need to prevent custom materials from having the same name as another material that is imported or make it clear which one is taking priority and where the use is from.

Additionally, we should notify the user when any custom material library is discovered and imported.

john-science commented 2 years ago

Sorry, @jakehader let me just check in to make sure I understand.

...discovery of a material plugin...

What do you mean by "discovery" here? So far, the user always carefully defines which plugins they want in their run in the ARMI ecosystem. This is how they gain fine control over their simulations.

Also, and not for nothing, I am not a fan of "just searching in a folder for a Python file that includes a subclass of something". If every single thing in an ARMI simulation is carefully defined by the user except this one thing, that'll be hard to explain to the user who will want to just know "Why can't I define everything in my YAML file? Why is this one thing different?" It breaks the paradigm users will have for how to define a run.

Also also, right now you can look at a settings file and a database file and you know everything about the run that created the database file. So now your settings file won't full define the run? You will also need a full copy of whatever folder includes these input files?

I don't want to be a wet blank, but I am not entirely convinced.

jakehader commented 2 years ago

Thanks for the feedback @john-science. I definitely think that this warrants further discussion between you, me, and @ntouran. Let's take it offline and report back here with a proposed solution or maybe alternate way to frame the problem I am looking to solve.

jakehader commented 2 years ago

@john-science, would you be able to schedule some time to talk about this with me? I am looking for something to help our benchmark modeling where not just density is important, but when we also need to consider temperature effects on material properties, but using a custom material definition for number densities.

keckler commented 2 years ago

@john-science I assume you closed this by accident?

john-science commented 2 years ago

@keckler Hmm... I didn't close this. Yet. I just referenced it in a PR and GitHub decided they must be connected and closed this ticket.

But @ntouran and I aren't hugely interested in this PR. We already have three ways to define materials for ARMI, and I'm not sure this one adds a lot of value.

ntouran commented 2 years ago

The linked issue above will be how we actually solve this issue.

ntouran commented 2 years ago

Hmm, while implementing that it occurred to me that plugins don't really do too much in terms of registering materials that they bring in other than setting the material resolution order to know about them (as @john-science was trying to remind me earlier).

This leads me to the supported path.to.module:MaterialClassName syntax supported by ARMI already. If you specified your material name in blueprints as, e.g. jakesCustomMaterials:Milkshake then the system would try to import jakesCustomMaterials.py and look for the Milkshake material class in there.

This can and does currently provide user-supply-able material classes in the run's input directory, provided that the input directory is in the module resolution path (e.g. PYTHONPATH).

I tried this out and it works. But it's kind of a pain to always be updating your PYTHONPATH. One thing we could consider doing to make this happen is having ARMI add the input directory directly to the sys.path variable at runtime. Then imports by name would work no problem.

Thoughts on that?

jakehader commented 2 years ago

Oh that is interesting! I think we need to consider the PYTHONPATH to make this easier to work with. Not sure how ARMI would auto detect where the materials are without some configuration though. Could we just by default add the settings file directory to the PYTHONPATH and then it "works" if the code is somewhere within that directory tree?