Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
8103262
Add source modifications to the trainer to be hable to handle stochas…
grmnptr Jan 8, 2025
5b72067
Move examples to stochastic reporters.
grmnptr Jan 8, 2025
67231ed
Fix indexing issue in reward to go.
grmnptr Jan 14, 2025
bfa7fa6
Add sampler transfer.
grmnptr Jan 14, 2025
74f5e12
Transition toward sampler and change seeds per application in this case.
grmnptr Jan 14, 2025
4442115
Fix normalization problem.
grmnptr Jan 15, 2025
8ad6850
Check sensitivity to inputs, understand multi worker behavior.
grmnptr Jan 22, 2025
ace272b
Add reward pp
grmnptr Feb 2, 2025
abe70b9
Add files for vortex shedding example.
grmnptr Feb 2, 2025
a45d92c
Add option for smoothing signal.
grmnptr Feb 2, 2025
4f06dcd
Adopt training for vortex shedding.
grmnptr Feb 2, 2025
3a0a78d
Add run-ready files.
grmnptr Feb 5, 2025
b17f831
Add actor network
grmnptr Feb 16, 2025
e950f37
Add different distributions.
grmnptr Feb 19, 2025
2abba0d
Add control option without sampling.
grmnptr Feb 28, 2025
fbac7cd
Extend reward PP
grmnptr Mar 1, 2025
e16279b
Add plotting script.
grmnptr Mar 2, 2025
d6990d0
Save what happens.
grmnptr Nov 26, 2025
b0a9231
Adopt to new changes.
grmnptr Apr 21, 2026
77baa1b
Apply formatting, add beta logprob fix.
grmnptr Apr 21, 2026
5226140
Split out action distribution (close to policy) from the drltrainer.
grmnptr Apr 21, 2026
4f75b66
Split out observation history from the drl trainer.
grmnptr Apr 21, 2026
6ac3663
Simplify the actor neural net.
grmnptr Apr 21, 2026
baf2a56
Simplify the control neural net in the framework.
grmnptr Apr 21, 2026
68da054
Add loss object, minibatch selector, and buffer for organized data co…
grmnptr Apr 21, 2026
5dcb70b
Simplify the drl control object.
grmnptr Apr 21, 2026
10b2974
Simplify the trainer.
grmnptr Apr 21, 2026
559a99d
Add unit tests for the controller.
grmnptr Apr 21, 2026
661d053
Move the scaling to the network itself instead of always doing it on …
grmnptr Apr 21, 2026
36c4eeb
Fix transfer update and shift.
grmnptr Apr 22, 2026
10ef125
Add state-independent variance. Fix restart.
grmnptr Apr 22, 2026
d0fc79a
Separate Beta and Gaussian distributions.
grmnptr Apr 22, 2026
38db960
Move action distribution to STM.
grmnptr Apr 22, 2026
563c6c2
Remove old framework implementation for distribution heads.
grmnptr Apr 23, 2026
c8f445c
Remove meltpool example, remove unused postprocessors.
grmnptr Apr 23, 2026
5e28ce3
remive min max parameters from the basic ann.
grmnptr Apr 23, 2026
408dc12
Remove the vortex control example.
grmnptr Apr 23, 2026
c9c637a
Make loading nicer, move setup to initial setup.
grmnptr Apr 23, 2026
30b7146
Remove normalization for the initialization.
grmnptr Apr 24, 2026
3007e7d
Add docstrings.
grmnptr Apr 24, 2026
2c32e39
Make the random number generation consistent in DRL.
grmnptr Apr 24, 2026
0af111c
Restrict sampler controller transfer.
grmnptr Apr 24, 2026
b2e8646
Rename response to observation, make sure we can recover a DRL contro…
grmnptr Apr 24, 2026
c295c2f
Add more docstrings.
grmnptr Apr 25, 2026
199386e
Simplify observationhistory.
grmnptr Apr 25, 2026
5458d59
Rename some object to make more sense.
grmnptr Apr 28, 2026
1dad4e1
Remove LiftDragRewardPostprocessor from drl-mods
grmnptr Apr 28, 2026
e17d7f8
More docstrings, cleanup remove testing PPs.
grmnptr Apr 28, 2026
3df5d51
Finish first round of cleanup.
grmnptr Apr 28, 2026
62dbc5e
Add modification for the documentation as well.
grmnptr Apr 28, 2026
ef3ee6e
Add proper format.
grmnptr Apr 28, 2026
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
7 changes: 0 additions & 7 deletions framework/doc/content/bib/moose.bib
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,6 @@ @book{muller1995neural
publisher={Springer Science \& Business Media}
}

@article{schulman2017proximal,
title={Proximal policy optimization algorithms},
author={Schulman, John and Wolski, Filip and Dhariwal, Prafulla and Radford, Alec and Klimov, Oleg},
journal={arXiv preprint arXiv:1707.06347},
year={2017}
}

@article{tonks2012object,
title={An object-oriented finite element framework for multiphysics phase field simulations},
author={Tonks, Michael R and Gaston, Derek and Millett, Paul C and Andr\v{s}, David and Talbot, Paul},
Expand Down
46 changes: 29 additions & 17 deletions framework/include/libtorch/controls/LibtorchNeuralNetControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#pragma once

#include "LibtorchArtificialNeuralNet.h"
#include "LibtorchObservationHistoryHelper.h"
#include "Control.h"

/**
Expand All @@ -30,6 +31,9 @@ class LibtorchNeuralNetControl : public Control
/// Construct using input parameters
LibtorchNeuralNetControl(const InputParameters & parameters);

/// Load any file-backed controller state after full object construction
virtual void initialSetup() override;

/// Execute neural network to determine the controllable parameter values
virtual void execute() override;

Expand All @@ -48,13 +52,18 @@ class LibtorchNeuralNetControl : public Control
* when copying the neural network from a main app which trains it.
* @param input_nn Reference to a neural network which will be copied into this object
*/
void loadControlNeuralNet(const Moose::LibtorchArtificialNeuralNet & input_nn);
virtual void loadControlNeuralNet(const Moose::LibtorchArtificialNeuralNet & input_nn);

/**
* Load the controller neural network from the configured checkpoint file.
*/
virtual void loadControlNeuralNetFromFile();

/// Return a reference to the stored neural network
const Moose::LibtorchNeuralNetBase & controlNeuralNet() const;

/// Return true if the object already has a neural netwok
bool hasControlNeuralNet() const { return (_nn != NULL); };
bool hasControlNeuralNet() const { return _nn != nullptr; };

protected:
/**
Expand All @@ -68,41 +77,44 @@ class LibtorchNeuralNetControl : public Control
const std::vector<std::string> & conditional_param,
bool should_be_defined = true);

/// Function that updates the values of the current response
void updateCurrentResponse();
/// Refresh the current observation values from the linked postprocessors.
void updateCurrentObservation();

/// Function that prepares the input tensor for the controller neural network
torch::Tensor prepareInputTensor();

/// The values of the current observed postprocessor values
std::vector<Real> _current_response;
/// This variable is populated if the controller needs acess to older values of the
std::vector<Real> _current_observation;
/// This variable is populated if the controller needs access to older values of the
/// observed postprocessor values
std::vector<std::vector<Real>> & _old_responses;
std::vector<std::vector<Real>> & _old_observations;

/// The names of the controllable parameters
const std::vector<std::string> & _control_names;
/// The control signals from the last evaluation of the controller
std::vector<Real> _current_control_signals;
/// The control signals from the last evaluation of the controller, saved for recover/restart.
std::vector<Real> & _current_control_signals;

/// Names of the postprocessors which contain the observations of the system
const std::vector<PostprocessorName> & _response_names;
const std::vector<PostprocessorName> & _observation_names;

/// Links to the current response postprocessor values. This is necessary so that we can check
/// Links to the current observation postprocessor values. This is necessary so that we can check
/// if the postprocessors exist.
std::vector<const Real *> _response_values;
std::vector<const Real *> _observation_values;

/// Number of timesteps to use as input data from the reporters (this influences how many past
/// results are used, e.g. the size of _old_responses)
/// results are used, e.g. the size of _old_observations)
const unsigned int _input_timesteps;

/// Shifting constants for the responses
const std::vector<Real> _response_shift_factors;
/// Scaling constants (multipliers) for the responses
const std::vector<Real> _response_scaling_factors;
/// Shifting constants for the observations
const std::vector<Real> _observation_shift_factors;
/// Scaling constants (multipliers) for the observations
const std::vector<Real> _observation_scaling_factors;
/// Multipliers for the actions
const std::vector<Real> _action_scaling_factors;

/// Shared observation history stacking and factor-expansion helper
const LibtorchObservationHistoryHelper _observation_history;

/// Pointer to the neural net object which is supposed to be used to control
/// the parameter values. The controller owns this object, but it can be read
/// from file or copied by a transfer.
Expand Down
112 changes: 94 additions & 18 deletions framework/include/libtorch/utils/LibtorchArtificialNeuralNet.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <torch/torch.h>
#include <torch/script.h>
#include <torch/serialize/archive.h>
#include "LibtorchNeuralNetBase.h"
#include "MooseError.h"
#include "DataIO.h"
Expand All @@ -22,44 +23,57 @@
namespace Moose
{

// A class that describes a simple feed-forward neural net.
/**
* Simple feed-forward neural net with optional affine input and output scaling.
*/
class LibtorchArtificialNeuralNet : public torch::nn::Module, public LibtorchNeuralNetBase
{
public:
/**
* Construct using input parameters
* @param name Name of the neural network
* @param num_inputs The number of input neurons/parameters
* @param num_neurons_per_layer Number of neurons per hidden layer
* @param num_outputs The number of output neurons
* Build a plain feed-forward neural network.
* @param name Name of the neural network module.
* @param num_inputs Number of input neurons or parameters.
* @param num_outputs Number of output neurons.
* @param num_neurons_per_layer Hidden-layer widths.
* @param activation_function Hidden-layer activation names.
* @param device_type Torch device used by the module.
* @param scalar_type Torch scalar type used by the module.
* @param build_on_construct Whether to build the torch modules right away.
* @param input_shift_factors Optional affine input shifts.
* @param input_scaling_factors Optional affine input scales.
* @param output_scaling_factors Optional output scaling factors.
*/
LibtorchArtificialNeuralNet(const std::string name,
const unsigned int num_inputs,
const unsigned int num_outputs,
const std::vector<unsigned int> & num_neurons_per_layer,
const std::vector<std::string> & activation_function = {"relu"},
const torch::DeviceType device_type = torch::kCPU,
const torch::ScalarType scalar_type = torch::kDouble);
const torch::ScalarType scalar_type = torch::kDouble,
const bool build_on_construct = true,
const std::vector<Real> & input_shift_factors = {},
const std::vector<Real> & input_scaling_factors = {},
const std::vector<Real> & output_scaling_factors = {});

/**
* Copy construct an artificial neural network
* @param nn The neural network which needs to be copied
* Copy-construct a feed-forward neural network.
* @param nn Neural network to copy.
* @param build_on_construct Whether to rebuild the module structure during the copy.
*/
LibtorchArtificialNeuralNet(const Moose::LibtorchArtificialNeuralNet & nn);
LibtorchArtificialNeuralNet(const Moose::LibtorchArtificialNeuralNet & nn,
const bool build_on_construct = true);

/**
* Add layers to the neural network
* @param layer_name The name of the layer to be added
* @param parameters A map of parameter names and the corresponding values which
* describe the neural net layer architecture
* Add one linear layer to the network.
* @param layer_name Name of the layer to add.
* @param parameters Small parameter map that describes the layer shape.
*/
virtual void addLayer(const std::string & layer_name,
const std::unordered_map<std::string, unsigned int> & parameters);

/**
* Overriding the forward substitution function for the neural network, unfortunately
* this cannot be const since it creates a graph in the background
* @param x Input tensor for the evaluation
* Run a forward pass through the network.
* @param x Input tensor for the evaluation.
*/
virtual torch::Tensor forward(const torch::Tensor & x) override;

Expand All @@ -79,13 +93,61 @@ class LibtorchArtificialNeuralNet : public torch::nn::Module, public LibtorchNeu
torch::DeviceType deviceType() const { return _device_type; }
/// Return the data type which is used by this neural network
torch::ScalarType dataType() const { return _data_type; }
/// Return the affine input shift factors used before evaluation
const std::vector<Real> & inputShiftFactors() const { return _input_shift_factors; }
/// Return the affine input scaling factors used before evaluation
const std::vector<Real> & inputScalingFactors() const { return _input_scaling_factors; }
/// Return the output scaling factors applied after evaluation
const std::vector<Real> & outputScalingFactors() const { return _output_scaling_factors; }
/// Construct the neural network
void constructNeuralNetwork();
virtual void constructNeuralNetwork();

/// Update cached affine metadata vectors from the registered libtorch buffers.
void synchronizeAffineFactorsFromBuffers();

/**
* Map an activation name to the orthogonal-initialization gain we want to use.
* @param activation Activation name to look up.
*/
Comment on lines +105 to +111
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's the wild-west for doxygen comment structure. We should get something in our style guide about this at some point

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In general, I try to do the slashes for short comments and the asterisk for longer ones. But I never thought about defining what is short and what is long.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll make this a bit more uniform.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't blame you. It's a reasonable heuristic and maybe that's the one we'll end up putting in the style guide. Generally I've always done the block comment structure for methods and then /// for data. But as this is not in the style guide, I can't say what I do is the right way

Real determineGain(const std::string & activation);

/**
* Initialize the trainable weights and biases.
* @param generator Optional torch random-number generator used for reproducible initialization.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@zachmprince should we just use libtorch for random number generation? This is in reference to your recent PR. The cost would be a no-longer-optional dependency. Possible gain would be reduced code maintenance and overall less code duplication across the OSS ecosystem? I defer to you two on this. I'm not an expert in this area

*/
virtual void initializeNeuralNetwork(c10::optional<at::Generator> generator = c10::nullopt);

/// Store the network architecture in a json file (for debugging, visualization)
void store(nlohmann::json & json) const;

protected:
/**
* Set affine metadata by either accepting the user values or filling defaults.
* @param factors User-provided affine factors.
* @param expected_size Expected number of entries.
* @param default_value Default value used when the vector is empty.
* @param factor_name Name used in error messages.
*/
static std::vector<Real> setAffineFactors(const std::vector<Real> & factors,
unsigned int expected_size,
Real default_value,
const std::string & factor_name);

/// Initialize the registered affine metadata buffers used by serialization.
void initializeAffineBuffers();

/**
* Apply affine preprocessing to the raw input tensor.
* @param x Raw input tensor.
*/
virtual torch::Tensor preprocessInput(const torch::Tensor & x) const;

/**
* Apply the configured output scaling to a network output tensor.
* @param y Raw network output tensor.
*/
virtual torch::Tensor scaleOutput(const torch::Tensor & y) const;

/// Name of the neural network
const std::string _name;
/// Submodules that hold linear operations and the corresponding
Expand All @@ -104,10 +166,24 @@ class LibtorchArtificialNeuralNet : public torch::nn::Module, public LibtorchNeu
const torch::DeviceType _device_type;
/// The data type used in this neural network
const torch::ScalarType _data_type;
/// Affine preprocessing applied to the flattened input
std::vector<Real> _input_shift_factors;
/// Multiplicative affine preprocessing applied after shifting the input
std::vector<Real> _input_scaling_factors;
/// Multiplicative scaling applied after the network output is formed
std::vector<Real> _output_scaling_factors;
/// Registered libtorch buffer holding the affine input shifts
torch::Tensor _input_shift_tensor;
/// Registered libtorch buffer holding the affine input scaling factors
torch::Tensor _input_scale_tensor;
/// Registered libtorch buffer holding the output scaling factors
torch::Tensor _output_scale_tensor;
};

void to_json(nlohmann::json & json, const Moose::LibtorchArtificialNeuralNet * const & network);

void loadLibtorchArtificialNeuralNetState(Moose::LibtorchArtificialNeuralNet & nn,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

doxygen please

const std::string & filename);
}

template <>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//* This file is part of the MOOSE framework
//* https://mooseframework.inl.gov
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#ifdef MOOSE_LIBTORCH_ENABLED

#pragma once

#include "MooseTypes.h"

#include <vector>

/**
* Shared observation history stacking and factor-expansion logic for libtorch-based controls and
* trainers.
*/
class LibtorchObservationHistoryHelper
{
public:
/**
* Build an observation-history helper for libtorch inputs.
* @param input_timesteps Number of timesteps to stack into each flattened input.
*/
LibtorchObservationHistoryHelper(unsigned int input_timesteps);

/// Return the number of timesteps stacked into each flattened input.
unsigned int inputTimesteps() const { return _input_timesteps; }

/**
* Fill the history buffer with copies of the current observation.
* @param observation Current observation.
* @param old_observations History buffer that stores previous observations.
*/
void initializeHistory(const std::vector<Real> & observation,
std::vector<std::vector<Real>> & old_observations) const;

/**
* Advance the history buffer by inserting the latest observation.
* @param observation Current observation.
* @param old_observations History buffer ordered from newest to oldest.
*/
void advanceHistory(const std::vector<Real> & observation,
std::vector<std::vector<Real>> & old_observations) const;

/**
* Repeat per-observation-entry factors across all stacked timesteps.
* @param observation_factors Per-entry factors for one observation vector.
*/
std::vector<Real> expandObservationFactors(const std::vector<Real> & observation_factors) const;

/**
* Flatten the current observation together with its stored history.
* @param observation Current observation.
* @param old_observations History buffer ordered from newest to oldest.
*/
std::vector<Real>
stackCurrentObservation(const std::vector<Real> & observation,
const std::vector<std::vector<Real>> & old_observations) const;

/**
* Flatten one time slice of observation-component trajectories with causal history.
* This uses [component][time] because the trainer receives reporter data one observation
* component at a time, so keeping that layout avoids building an extra transposed
* [time][component] container before stacking.
* @param component_trajectories Observation trajectories indexed as [component][time].
* @param time_index Time index to stack.
*/
std::vector<Real>
stackTrajectoryObservation(const std::vector<std::vector<Real>> & component_trajectories,
unsigned int time_index) const;

private:
/// Check that all observation-component trajectories have a consistent shape.
void validateTrajectoryShape(const std::vector<std::vector<Real>> & component_trajectories) const;

/// Number of timesteps stacked into each flattened observation.
const unsigned int _input_timesteps;
};

#endif
Loading