LCOV - code coverage report
Current view: top level - src/materials - NeuralNetFreeEnergyBase.C (source / functions) Hit Total Coverage
Test: idaholab/magpie: 5710af Lines: 127 154 82.5 %
Date: 2025-07-21 23:34:39 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /**********************************************************************/
       2             : /*                     DO NOT MODIFY THIS HEADER                      */
       3             : /* MAGPIE - Mesoscale Atomistic Glue Program for Integrated Execution */
       4             : /*                                                                    */
       5             : /*            Copyright 2017 Battelle Energy Alliance, LLC            */
       6             : /*                        ALL RIGHTS RESERVED                         */
       7             : /**********************************************************************/
       8             : 
       9             : #include "NeuralNetFreeEnergyBase.h"
      10             : #include "MooseEnum.h"
      11             : #include <fstream>
      12             : 
      13             : InputParameters
      14          38 : NeuralNetFreeEnergyBase::validParams()
      15             : {
      16          38 :   auto params = ADMaterial::validParams();
      17          38 :   params.addClassDescription(
      18             :       "Evaluates a fitted deep neural network to obtain a free energy and its derivatives.");
      19             : 
      20          76 :   MooseEnum fileFormatEnum("MAGPIE GENANN");
      21          76 :   params.template addParam<MooseEnum>(
      22             :       "file_format", fileFormatEnum, "Weights and biases file format");
      23             : 
      24          76 :   params.template addParam<FileName>(
      25             :       "file_name",
      26             :       "Data file containing the weights and biasses for a fully connected deep neural network");
      27          76 :   params.addCoupledVar("inputs", "Coupled Variables that are inputs for the neural network");
      28          76 :   params.template addParam<std::vector<MaterialPropertyName>>(
      29             :       "prop_names", {}, "list of material properties fed from the outputs of the neural network");
      30          76 :   params.template addParam<bool>(
      31          76 :       "debug", false, "Tabulate the NN to a file for debugging purposes");
      32          38 :   return params;
      33          38 : }
      34             : 
      35          30 : NeuralNetFreeEnergyBase::NeuralNetFreeEnergyBase(const InputParameters & parameters)
      36             :   : ADMaterial(parameters),
      37          60 :     _file_format(getParam<MooseEnum>("file_format").template getEnum<FileFormat>()),
      38          30 :     _file_name(getParam<FileName>("file_name")),
      39          90 :     _output_name(getParam<std::vector<MaterialPropertyName>>("prop_names")),
      40          30 :     _n_output(_output_name.size()),
      41          30 :     _output(_n_output),
      42          30 :     _n_input(coupledComponents("inputs")),
      43          30 :     _input(_n_input),
      44          60 :     _d_output(_n_input * _n_output)
      45             : {
      46             :   // open the NN data file
      47          30 :   std::ifstream ifile;
      48          30 :   ifile.open(_file_name);
      49          30 :   if (!ifile)
      50           0 :     paramError("file_name", "Unable to open file");
      51             : 
      52             :   // call the reader for the requested format
      53          30 :   switch (_file_format)
      54             :   {
      55          15 :     case FileFormat::MAGPIE:
      56          15 :       loadMagpieNet(ifile);
      57             :       break;
      58             : 
      59          15 :     case FileFormat::GENANN:
      60          15 :       loadGenANN(ifile);
      61             :       break;
      62             : 
      63           0 :     default:
      64           0 :       paramError("file_format", "Unknown file format");
      65             :   }
      66             : 
      67             :   // close parameter file
      68          30 :   ifile.close();
      69             : 
      70             :   // validate network properties
      71          30 :   if (_weight.size() != _bias.size())
      72           0 :     paramError("file_name", "Inconsistent layer numbers in datafile");
      73          30 :   if (_z[_n_layer - 1].size() != _n_output)
      74           0 :     paramError("prop_names",
      75             :                "Number of supplied property names must match the number of output nodes ",
      76             :                _z[_n_layer - 1].size(),
      77             :                " of the neural net");
      78          30 :   if (_n_input != _activation[0].size())
      79           0 :     paramError("inputs",
      80             :                "Number of supplied variables must match the number of input nodes ",
      81             :                _activation[0].size(),
      82             :                " of the neural net");
      83             : 
      84             :   // initialize storage for derivatives
      85          30 :   _prod.resize(_n_layer);
      86          30 :   _diff.resize(_n_layer);
      87          30 :   _diff[0] = _weight[0]; // _prod[0] is not used
      88         105 :   for (std::size_t i = 1; i < _n_layer; ++i)
      89             :   {
      90          75 :     _prod[i] = _weight[i];
      91          75 :     _diff[i] = DenseMatrix<ADReal>(_z[i].size(), _n_input);
      92             :   }
      93             : 
      94             :   // get coupled variables for the inputs
      95          90 :   for (std::size_t j = 0; j < _n_input; ++j)
      96          60 :     _input[j] = &adCoupledValue("inputs", j);
      97             : 
      98             :   // create material properties
      99             :   std::size_t k = 0;
     100          60 :   for (std::size_t i = 0; i < _n_output; ++i)
     101             :   {
     102          30 :     _output[i] = &declareADProperty<Real>(_output_name[i]);
     103          90 :     for (std::size_t j = 0; j < _n_input; ++j)
     104          60 :       _d_output[k++] = &declareADProperty<Real>(
     105         120 :           derivativePropertyNameFirst(_output_name[i], this->getVar("inputs", j)->name()));
     106             :   }
     107          30 : }
     108             : 
     109             : void
     110          30 : NeuralNetFreeEnergyBase::initialSetup()
     111             : {
     112          60 :   if (getParam<bool>("debug"))
     113           0 :     debugDump();
     114          30 : }
     115             : 
     116             : void
     117           0 : NeuralNetFreeEnergyBase::debugDump()
     118             : {
     119           0 :   std::ofstream ofile;
     120           0 :   ofile.open(_name + "_debug.dat");
     121           0 :   for (Real T = 0.0; T <= 1.0; T += 0.01)
     122           0 :     for (Real c = 0.0; c <= 1.0; c += 0.01)
     123             :     {
     124           0 :       ofile << T << ' ' << c;
     125             : 
     126             :       // set inputs
     127           0 :       _activation[0](0) = T;
     128           0 :       _activation[0](1) = c;
     129             : 
     130             :       // evaluate network
     131           0 :       evaluate();
     132             : 
     133             :       // output values
     134           0 :       for (std::size_t i = 0; i < _n_output; ++i)
     135           0 :         ofile << ' ' << _z[_n_layer - 1](i);
     136             : 
     137           0 :       ofile << '\n';
     138             :     }
     139           0 : }
     140             : 
     141             : void
     142          15 : NeuralNetFreeEnergyBase::loadGenANN(std::ifstream & ifile)
     143             : {
     144             :   std::size_t x, y;
     145             : 
     146             :   // read layer layout
     147             :   unsigned int n_inputs, n_hidden_layers, n_hidden, n_outputs;
     148          30 :   if (!(ifile >> n_inputs >> n_hidden_layers >> n_hidden >> n_outputs))
     149           0 :     mooseError("Error reading genann file header from file ", _file_name);
     150          15 :   _n_layer = n_hidden_layers + 1;
     151          15 :   _bias.resize(_n_layer);
     152          15 :   _weight.resize(_n_layer);
     153          15 :   _z.resize(_n_layer);
     154          15 :   _activation.resize(_n_layer);
     155          15 :   _d_activation.resize(_n_layer);
     156             : 
     157             :   // read weights and biases
     158          45 :   for (std::size_t i = 0; i < _n_layer; ++i)
     159             :   {
     160             :     // negative biases come first
     161          30 :     x = i < _n_layer - 1 ? n_hidden : n_outputs;
     162             : 
     163          30 :     _z[i] = DenseVector<ADReal>(x);
     164          30 :     _bias[i] = DenseVector<Real>(x);
     165         420 :     for (std::size_t j = 0; j < x; ++j)
     166         390 :       if (!(ifile >> _bias[i](j)))
     167           0 :         mooseError("Error reading biases from file ", _file_name);
     168             :       else
     169         390 :         _bias[i](j) *= -1.0;
     170             : 
     171             :     // next come the weights
     172          30 :     y = i > 0 ? n_hidden : n_inputs;
     173             :     // initialize weight matrix
     174          30 :     _weight[i] = DenseMatrix<Real>(x, y);
     175             : 
     176             :     // initialize computation buffers (including input and output)
     177          30 :     _activation[i] = DenseVector<ADReal>(y);
     178          30 :     _d_activation[i] = DenseVector<ADReal>(y);
     179          30 :     _z[i] = DenseVector<ADReal>(x);
     180             : 
     181         435 :     for (std::size_t k = 0; k < y; ++k)
     182        1530 :       for (std::size_t j = 0; j < x; ++j)
     183        1125 :         if (!(ifile >> _weight[i](j, k)))
     184           0 :           mooseError("Error reading weights from file ", _file_name);
     185             :   }
     186          15 : }
     187             : 
     188             : void
     189          15 : NeuralNetFreeEnergyBase::loadMagpieNet(std::ifstream & ifile)
     190             : {
     191             :   std::size_t x, y;
     192             : 
     193             :   // read weights (first the number of layers)
     194          15 :   ifile >> _n_layer;
     195          15 :   _weight.resize(_n_layer);
     196          15 :   _z.resize(_n_layer);
     197          15 :   _activation.resize(_n_layer);
     198          15 :   _d_activation.resize(_n_layer);
     199          90 :   for (std::size_t i = 0; i < _n_layer; ++i)
     200             :   {
     201          75 :     if (!(ifile >> y >> x))
     202           0 :       mooseError("Error reading file ", _file_name);
     203             : 
     204             :     // initialize weight matrix
     205          75 :     _weight[i] = DenseMatrix<Real>(x, y);
     206             : 
     207             :     // initialize computation buffers (including input and output)
     208          75 :     _activation[i] = DenseVector<ADReal>(y);
     209          75 :     _d_activation[i] = DenseVector<ADReal>(y);
     210          75 :     _z[i] = DenseVector<ADReal>(x);
     211             : 
     212        1305 :     for (std::size_t k = 0; k < y; ++k)
     213       20130 :       for (std::size_t j = 0; j < x; ++j)
     214       18900 :         if (!(ifile >> _weight[i](j, k)))
     215           0 :           mooseError("Error reading weights from file ", _file_name);
     216             :   }
     217             : 
     218             :   // read biases
     219          15 :   _bias.resize(_n_layer);
     220          90 :   for (std::size_t i = 0; i < _n_layer; ++i)
     221             :   {
     222          75 :     if (!(ifile >> x))
     223           0 :       mooseError("Error reading file ", _file_name);
     224             : 
     225          75 :     _z[i] = DenseVector<ADReal>(x);
     226          75 :     _bias[i] = DenseVector<Real>(x);
     227             : 
     228        1290 :     for (std::size_t j = 0; j < x; ++j)
     229        1215 :       if (!(ifile >> _bias[i](j)))
     230           0 :         mooseError("Error reading biases from file ", _file_name);
     231             :   }
     232          15 : }
     233             : 
     234             : void
     235        4800 : NeuralNetFreeEnergyBase::computeQpProperties()
     236             : {
     237             :   // set input nodes
     238       14400 :   for (std::size_t j = 0; j < _n_input; ++j)
     239        9600 :     _activation[0](j) = (*_input[j])[_qp];
     240             : 
     241             :   // evaluate network
     242        4800 :   evaluate();
     243             : 
     244             :   // copy back output values
     245             :   std::size_t k = 0;
     246        9600 :   for (std::size_t i = 0; i < _n_output; ++i)
     247             :   {
     248        4800 :     (*_output[i])[_qp] = _z[_n_layer - 1](i);
     249       14400 :     for (std::size_t j = 0; j < _n_input; ++j)
     250        9600 :       (*_d_output[k++])[_qp] = _diff[_n_layer - 1](i, j);
     251             :   }
     252        4800 : }
     253             : 
     254             : void
     255       12000 : NeuralNetFreeEnergyBase::multiply(DenseMatrix<ADReal> & M1,
     256             :                                   const DenseMatrix<ADReal> & M2,
     257             :                                   const DenseMatrix<ADReal> & M3)
     258             : {
     259             :   // Assertions to make sure we have been
     260             :   // passed matrices of the correct dimension.
     261             :   libmesh_assert_equal_to(M1.m(), M2.m());
     262             :   libmesh_assert_equal_to(M1.n(), M3.n());
     263             :   libmesh_assert_equal_to(M2.n(), M3.m());
     264             : 
     265             :   const unsigned int m_s = M2.m();
     266             :   const unsigned int p_s = M2.n();
     267             :   const unsigned int n_s = M1.n();
     268             : 
     269       36000 :   for (unsigned int j = 0; j < n_s; j++)
     270      321600 :     for (unsigned int i = 0; i < m_s; i++)
     271             :     {
     272      297600 :       M1.el(i, j) = 0.0;
     273     6273600 :       for (unsigned int k = 0; k < p_s; k++)
     274     5976000 :         M1.el(i, j) += M2.el(i, k) * M3.el(k, j);
     275             :     }
     276       12000 : }
     277             : 
     278             : void
     279        4800 : NeuralNetFreeEnergyBase::evaluate()
     280             : {
     281        4800 :   _layer = 0;
     282             :   while (true)
     283             :   {
     284             :     // apply weights and biases
     285       16800 :     _weight[_layer].vector_mult(_z[_layer], _activation[_layer]);
     286       16800 :     _z[_layer] += _bias[_layer];
     287             : 
     288             :     // derivatives
     289       16800 :     if (_layer > 0)
     290             :     {
     291             :       // prepare product of weights and activation function derivative (previous layer)
     292      160800 :       for (std::size_t j = 0; j < _weight[_layer].m(); ++j)
     293     3136800 :         for (std::size_t k = 0; k < _weight[_layer].n(); ++k)
     294     5976000 :           _prod[_layer](j, k) = _weight[_layer](j, k) * _d_activation[_layer](k);
     295             : 
     296             :       // multiply progressive Jacobian
     297       12000 :       multiply(_diff[_layer], _prod[_layer], _diff[_layer - 1]);
     298             :     }
     299             : 
     300             :     // bail to avoid applying activation function to the output
     301       16800 :     if (_layer + 1 == _n_layer)
     302             :       break;
     303             : 
     304             :     // apply activation function
     305       12000 :     applyLayerActivation();
     306             : 
     307             :     // next layer
     308       12000 :     ++_layer;
     309       12000 :   }
     310        4800 : }

Generated by: LCOV version 1.14