Skip to content

Starting structure for OM refactor - #92

Open
elenya-grant wants to merge 41 commits into
NatLabRockies:developfrom
elenya-grant:om_refactor/structure
Open

Starting structure for OM refactor#92
elenya-grant wants to merge 41 commits into
NatLabRockies:developfrom
elenya-grant:om_refactor/structure

Conversation

@elenya-grant

@elenya-grant elenya-grant commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Starting structure for OM refactor

This PR requires PR #90 to be merged in!

This PR introduces a different structure of the functionality within the electrolyzer repo to accommodate an Open-MDAO structure. This is in a somewhat intermediate state so that future development has smaller PRs and can have more rigorous testing and a focus on individual component development. This PR really focuses on the structure for the "cluster" and the connection from a cluster to a controller. The "controller" right now is just an IndepVarComp because the structure is only set-up to handle one cluster at the moment. The cluster is comprised of 3 main subgroups:

  1. converter: perhaps this should be renamed to "pre_translator". This defines the current operational bounds and also is used to get curve coefficients for the power-to-current conversion.
  2. translator: this is used to take a cluster input power signal and convert it to the cell-level current input
  3. simulation: this takes the cell-level current input and simulates cell degradation, includes dynamic operating constraints, and simulates the cell performance.

Plans for follow-on PRs:

Files to Remove (were helper/drafts):

  • electrolyzer/translators/power_translator_baseclass.py
  • electrolyzer/core/simulate_structure.py
  • electrolyzer/core/pre_processing_structure_v2.py

Related issue, if one exists

Impacted areas of the software

Additional supporting information

n2_diagram

Test results, if applicable

@elenya-grant
elenya-grant marked this pull request as ready for review July 27, 2026 17:35

@johnjasa johnjasa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Some initial thoughts and comments from me! I'm liking what I'm seeing, especially with the care you've put to introducing base classes and then having smart inheritance. The similarities between the structure you've chosen here and H2I is fun to see, as well as the differences since we're tackling slightly different problems.

Nothing blocking, though I am treating this PR as part of a larger refactor effort so I'm not being picky about docstrings, etc. Glad you're able to push through some great progress here!

Comment on lines +103 to +116
def initialize(self):
super().initialize()
# self.options.declare("mode", types=str, default="normal")

def setup(self):
# self.n = 2 # number of electrons transferred in reaction

self.config = PEMCellConfig.from_dict(self.options["tech_config"]["cell_parameters"])

super().setup()

# self.add_input("H2_demand", val=0.0, shape_by_conn=True, units="g/s")
# self.add_output("I_demand", val=0.0, copy_shape="H2_demand", units="A")
# self.add_output("J_demand", val=0.0, copy_shape="H2_demand", units="A/(cm**2)")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

remove commented code? or is useful for later?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this is removed in the follow-on PR I think?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

maybe rename to cell_baseclass.py? not necessary

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm guessing you're going to be fleshing this out more later on, but I still think it'd be worthwhile to have a docstring or two in here explaining the very basics of the simple dynamics class, what it does, potential applications for it

outputs[o_name] = inputs[in_name] / inputs["n_stacks"]


class ScaleUp(om.ExplicitComponent):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These two classes are named very broadly; is this on purpose? Will these be used in multiple different contexts, or could we name them more clearly to say at what level they'd be used, e.g. "stack to cluster" or whatever it is?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

removed the building blocks file



class ScaleUp(om.ExplicitComponent):
"""Scale things down"""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

docstring flipped

Comment on lines +260 to +268
class Draft(om.ExplicitComponent):
def initialize(self):
self.options.declare("tech_config", types=dict, default={})

def setup(self):
pass

def compute(self, inputs, outputs):
pass

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

delete?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

sorry I meant to delete the entire building_blocks.py file!

A_cell: float = field(validator=(validators.gt(0.0)))


class CurrentBoundsBase(om.ExplicitComponent):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tell me about this component; its name/docstring/filename doesn't make it immediately clear to me what it's doing or where it's used

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It supplies the min/max current bounds to the dynamics component (and would likely send these to the controller). It also calculates a bunch of current reference points from min to max for the curve-fitting.

Comment thread electrolyzer/core/bert.py
Comment on lines +93 to +104
# Step 1: Create controller cluster connector components
pre_translator = self.create_controller_cluster_connector()
# Translator has scale down + power to current conversion
translator = self.create_controller_translator()
# Step 2: Create the simulate block of a cluster
simulator = self.create_cluster_simulation_block()

cluster_group.add_subsystem(
"converter", pre_translator, promotes=["A_cell", "I_min", "I_max"]
)
cluster_group.add_subsystem("translator", translator, promotes=["n_stacks", "n_cells"])
cluster_group.add_subsystem("simulation", simulator, promotes=["I_min", "I_max", "A_cell"])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not necessarily sold on the translator and converter terms. Is there something where we could call it static_design or pre_simulation or something else? It could be more clear what those names mean

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sold on them either. I did have it the "converter" called the "pre-translator" at one point. I don't think I'm sold on the static_design name. I did make an issue about the naming in general and started brainstorming some new names for the classes here #100

Comment thread electrolyzer/core/bert.py
Comment on lines +146 to +162
cell_scale_down = self.create_scale_down_component(
scale_comp="cells", n_comps=self.config["stack"]["n_cells"]
)
stack_scale_down = self.create_scale_down_component(
scale_comp="stacks", n_comps=self.config["cluster"]["n_stacks"]
)

translator.add_subsystem(
"cluster_to_stack",
stack_scale_down,
promotes_inputs=["n_stacks"],
)
translator.add_subsystem(
"stack_to_cell",
cell_scale_down,
promotes_inputs=["n_cells"],
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Right on, I see how the scale components are used now, please mostly disregard my prior comment. I do still think when you get to documenting the components it'd be useful to include a few examples of where they'd be used in the docstring so folks understand where you'd be scaling up or down

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.

2 participants