<commit_before>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* 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

#include "PiecewiseBilinear.h"
#include "ColumnMajorMatrix.h"
#include "BilinearInterpolation.h"

#include <fstream>

registerMooseObject("MooseApp", PiecewiseBilinear);

defineLegacyParams(PiecewiseBilinear);

InputParameters
PiecewiseBilinear::validParams()
{
  InputParameters params = Function::validParams();
  params.addParam<FileName>(
      "data_file", "", "File holding csv data for use with PiecewiseBilinear");
  params.addParam<std::vector<Real>>("x", "The x abscissa values");
  params.addParam<std::vector<Real>>("y", "The y abscissa values");
  params.addParam<std::vector<Real>>("z", "The ordinate values");
  params.addParam<int>("axis", -1, "The axis used (0, 1, or 2 for x, y, or z).");
  params.addParam<int>(
      "xaxis", -1, "The coordinate used for x-axis data (0, 1, or 2 for x, y, or z).");
  params.addParam<int>(
      "yaxis", -1, "The coordinate used for y-axis data (0, 1, or 2 for x, y, or z).");
  params.addParam<Real>(
      "scale_factor", 1.0, "Scale factor to be applied to the axis, yaxis, or xaxis values");
  params.addParam<bool>("radial",
                        false,
                        "Set to true if you want to interpolate along a radius "
                        "rather that along a specific axis, and note that you "
                        "have to define xaxis and yaxis in the input file");
  params.addClassDescription("Interpolates values from a csv file");
  return params;
}

PiecewiseBilinear::PiecewiseBilinear(const InputParameters & parameters)
  : Function(parameters),
    _data_file_name(getParam<FileName>("data_file")),
    _axis(getParam<int>("axis")),
    _yaxis(getParam<int>("yaxis")),
    _xaxis(getParam<int>("xaxis")),
    _axisValid(_axis > -1 && _axis < 3),
    _yaxisValid(_yaxis > -1 && _yaxis < 3),
    _xaxisValid(_xaxis > -1 && _xaxis < 3),
    _scale_factor(getParam<Real>("scale_factor")),
    _radial(getParam<bool>("radial"))
{

  if (!_axisValid && !_yaxisValid && !_xaxisValid)
    mooseError("In PiecewiseBilinear ",
               _name,
               ": None of axis, yaxis, or xaxis properly defined.  Allowable range is 0-2");

  if (_axisValid && (_yaxisValid || _xaxisValid))
    mooseError("In PiecewiseBilinear ", _name, ": Cannot define axis with either yaxis or xaxis");

  if (_radial && (!_yaxisValid || !_xaxisValid))
    mooseError(
        "In PiecewiseBilinear ", _name, ": yaxis and xaxis must be defined when radial = true");

  std::vector<Real> x;
  std::vector<Real> y;
  ColumnMajorMatrix z;
  std::vector<Real> z_vec;

  if (!_data_file_name.empty())
  {
    if (parameters.isParamValid("x") || parameters.isParamValid("y") ||
        parameters.isParamValid("z"))
      mooseError("In PiecewiseBilinear: Cannot specify 'data_file' and 'x', 'y', or 'z' together.");
    else
      parse(x, y, z);
  }

  else if (!(parameters.isParamValid("x") && parameters.isParamValid("y") &&
             parameters.isParamValid("z")))
    mooseError(
        "In PiecewiseBilinear: 'x' and 'y' and 'z' must be specified if any one is specified.");

  else
  {
    x = getParam<std::vector<Real>>("x");
    y = getParam<std::vector<Real>>("y");
    z_vec = getParam<std::vector<Real>>("z");

    // check that size of z = (size of x)*(size of y)
    if (z_vec.size() != x.size() * y.size())
      mooseError("In PiecewiseBilinear: Size of z should be the size of x times the size of y.");

    // reshape and populate z matrix
    z.reshape(y.size(), x.size());
    int idx = 0;
    for (unsigned int i = 0; i < y.size(); i++)
      for (unsigned int j = 0; j < x.size(); j++)
      {
        z(i, j) = z_vec[idx];
        idx += 1;
      }
  }

  _bilinear_interp = libmesh_make_unique<BilinearInterpolation>(x, y, z);
}

PiecewiseBilinear::~PiecewiseBilinear() {}

Real
PiecewiseBilinear::value(Real t, const Point & p) const
{
  Real retVal(0);
  if (_yaxisValid && _xaxisValid && _radial)
  {
    Real rx = p(_xaxis) * p(_xaxis);
    Real ry = p(_yaxis) * p(_yaxis);
    Real r = std::sqrt(rx + ry);
    retVal = _bilinear_interp->sample(r, t);
  }
  else if (_axisValid)
    retVal = _bilinear_interp->sample(p(_axis), t);
  else if (_yaxisValid && !_radial)
  {
    if (_xaxisValid)
      retVal = _bilinear_interp->sample(p(_xaxis), p(_yaxis));
    else
      retVal = _bilinear_interp->sample(t, p(_yaxis));
  }
  else
    retVal = _bilinear_interp->sample(p(_xaxis), t);

  return retVal * _scale_factor;
}

void
PiecewiseBilinear::parse(std::vector<Real> & x, std::vector<Real> & y, ColumnMajorMatrix & z)
{
  std::ifstream file(_data_file_name.c_str());
  if (!file.good())
    mooseError("In PiecewiseBilinear ", _name, ": Error opening file '" + _data_file_name + "'.");
  std::string line;
  unsigned int linenum = 0;
  unsigned int itemnum = 0;
  unsigned int num_cols = 0;
  std::vector<Real> data;

  while (getline(file, line))
  {
    linenum++;
    std::istringstream linestream(line);
    std::string item;
    itemnum = 0;

    while (getline(linestream, item, ','))
    {
      itemnum++;
      std::istringstream i(item);
      Real d;
      i >> d;
      data.push_back(d);
    }

    if (linenum == 1)
      num_cols = itemnum;
    else if (num_cols + 1 != itemnum)
      mooseError("In PiecewiseBilinear ",
                 _name,
                 ": Read ",
                 itemnum,
                 " columns of data but expected ",
                 num_cols + 1,
                 " columns while reading line ",
                 linenum,
                 " of '",
                 _data_file_name,
                 "'.");
  }

  x.resize(itemnum - 1);
  y.resize(linenum - 1);
  z.reshape(linenum - 1, itemnum - 1);
  unsigned int offset(0);
  // Extract the first line's data (the x axis data)
  for (unsigned int j = 0; j < itemnum - 1; ++j)
  {
    x[j] = data[offset];
    ++offset;
  }
  for (unsigned int i = 0; i < linenum - 1; ++i)
  {
    // Extract the y axis entry for this line
    y[i] = data[offset];
    ++offset;

    // Extract the function values for this row in the matrix
    for (unsigned int j = 0; j < itemnum - 1; ++j)
    {
      z(i, j) = data[offset];
      ++offset;
    }
  }

  if (data.size() != offset)
    mooseError("ERROR! Inconsistency in data read from '" + _data_file_name +
               "' for PiecewiseBilinear function.");
}
<commit_msg>Clean up piecewise bilinear file reading (#19173)<commit_after>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* 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

#include "PiecewiseBilinear.h"
#include "ColumnMajorMatrix.h"
#include "BilinearInterpolation.h"
#include "MooseUtils.h"

#include <fstream>

registerMooseObject("MooseApp", PiecewiseBilinear);

defineLegacyParams(PiecewiseBilinear);

InputParameters
PiecewiseBilinear::validParams()
{
  InputParameters params = Function::validParams();
  params.addParam<FileName>(
      "data_file", "", "File holding csv data for use with PiecewiseBilinear");
  params.addParam<std::vector<Real>>("x", "The x abscissa values");
  params.addParam<std::vector<Real>>("y", "The y abscissa values");
  params.addParam<std::vector<Real>>("z", "The ordinate values");
  params.addParam<int>("axis", -1, "The axis used (0, 1, or 2 for x, y, or z).");
  params.addParam<int>(
      "xaxis", -1, "The coordinate used for x-axis data (0, 1, or 2 for x, y, or z).");
  params.addParam<int>(
      "yaxis", -1, "The coordinate used for y-axis data (0, 1, or 2 for x, y, or z).");
  params.addParam<Real>(
      "scale_factor", 1.0, "Scale factor to be applied to the axis, yaxis, or xaxis values");
  params.addParam<bool>("radial",
                        false,
                        "Set to true if you want to interpolate along a radius "
                        "rather that along a specific axis, and note that you "
                        "have to define xaxis and yaxis in the input file");
  params.addClassDescription("Interpolates values from a csv file");
  return params;
}

PiecewiseBilinear::PiecewiseBilinear(const InputParameters & parameters)
  : Function(parameters),
    _data_file_name(getParam<FileName>("data_file")),
    _axis(getParam<int>("axis")),
    _yaxis(getParam<int>("yaxis")),
    _xaxis(getParam<int>("xaxis")),
    _axisValid(_axis > -1 && _axis < 3),
    _yaxisValid(_yaxis > -1 && _yaxis < 3),
    _xaxisValid(_xaxis > -1 && _xaxis < 3),
    _scale_factor(getParam<Real>("scale_factor")),
    _radial(getParam<bool>("radial"))
{

  if (!_axisValid && !_yaxisValid && !_xaxisValid)
    mooseError("In PiecewiseBilinear ",
               _name,
               ": None of axis, yaxis, or xaxis properly defined.  Allowable range is 0-2");

  if (_axisValid && (_yaxisValid || _xaxisValid))
    mooseError("In PiecewiseBilinear ", _name, ": Cannot define axis with either yaxis or xaxis");

  if (_radial && (!_yaxisValid || !_xaxisValid))
    mooseError(
        "In PiecewiseBilinear ", _name, ": yaxis and xaxis must be defined when radial = true");

  std::vector<Real> x;
  std::vector<Real> y;
  ColumnMajorMatrix z;
  std::vector<Real> z_vec;

  if (!_data_file_name.empty())
  {
    if (parameters.isParamValid("x") || parameters.isParamValid("y") ||
        parameters.isParamValid("z"))
      mooseError("In PiecewiseBilinear: Cannot specify 'data_file' and 'x', 'y', or 'z' together.");
    else
      parse(x, y, z);
  }

  else if (!(parameters.isParamValid("x") && parameters.isParamValid("y") &&
             parameters.isParamValid("z")))
    mooseError("In PiecewiseBilinear: 'x' and 'y' and 'z' must be specified if any one is "
               "specified or no 'data_file' is specified.");

  else
  {
    x = getParam<std::vector<Real>>("x");
    y = getParam<std::vector<Real>>("y");
    z_vec = getParam<std::vector<Real>>("z");

    // check that size of z = (size of x)*(size of y)
    if (z_vec.size() != x.size() * y.size())
      mooseError("In PiecewiseBilinear: Size of z should be the size of x times the size of y.");

    // reshape and populate z matrix
    z.reshape(y.size(), x.size());
    int idx = 0;
    for (unsigned int i = 0; i < y.size(); i++)
      for (unsigned int j = 0; j < x.size(); j++)
      {
        z(i, j) = z_vec[idx];
        idx += 1;
      }
  }

  _bilinear_interp = libmesh_make_unique<BilinearInterpolation>(x, y, z);
}

PiecewiseBilinear::~PiecewiseBilinear() {}

Real
PiecewiseBilinear::value(Real t, const Point & p) const
{
  Real retVal(0);
  if (_yaxisValid && _xaxisValid && _radial)
  {
    Real rx = p(_xaxis) * p(_xaxis);
    Real ry = p(_yaxis) * p(_yaxis);
    Real r = std::sqrt(rx + ry);
    retVal = _bilinear_interp->sample(r, t);
  }
  else if (_axisValid)
    retVal = _bilinear_interp->sample(p(_axis), t);
  else if (_yaxisValid && !_radial)
  {
    if (_xaxisValid)
      retVal = _bilinear_interp->sample(p(_xaxis), p(_yaxis));
    else
      retVal = _bilinear_interp->sample(t, p(_yaxis));
  }
  else
    retVal = _bilinear_interp->sample(p(_xaxis), t);

  return retVal * _scale_factor;
}

void
PiecewiseBilinear::parse(std::vector<Real> & x, std::vector<Real> & y, ColumnMajorMatrix & z)
{
  std::ifstream file(_data_file_name.c_str());
  if (!file.good())
    paramError("data_file", "Error opening file '", _data_file_name, "'.");

  std::size_t num_lines = 0;
  std::size_t num_cols = libMesh::invalid_uint;
  std::vector<Real> data;

  std::string line;
  std::vector<Real> line_data;
  while (std::getline(file, line))
  {
    num_lines++;
    if (!MooseUtils::tokenizeAndConvert<double>(line, line_data, ", "))
      paramError("data_file", "Error parsing file '", _data_file_name, "' on line ", num_lines);

    data.insert(data.end(), line_data.begin(), line_data.end());

    if (num_cols == libMesh::invalid_uint)
      num_cols = line_data.size();
    else if (line_data.size() != num_cols + 1)
      paramError("data_file",
                 "Read ",
                 line_data.size(),
                 " columns of data but expected ",
                 num_cols + 1,
                 " columns in line ",
                 num_lines);
  }

  x.resize(num_cols);
  y.resize(num_lines - 1);
  z.reshape(num_lines - 1, num_cols);
  std::size_t offset = 0;

  // Extract the first line's data (the x axis data)
  for (unsigned int j = 0; j < num_cols; ++j)
    x[j] = data[offset++];

  for (unsigned int i = 0; i < num_lines - 1; ++i)
  {
    // Extract the y axis entry for this line
    y[i] = data[offset++];

    // Extract the function values for this row in the matrix
    for (unsigned int j = 0; j < num_cols; ++j)
      z(i, j) = data[offset++];
  }

  if (data.size() != offset)
    paramError("data_file", "Inconsistency in data read from '", _data_file_name, "'.");
}

{'fraction_non_alphanumeric': 0.15192555042747977, 'fraction_numerical': 0.007187712794128774, 'mean_word_length': 3.0595823095823094, 'pattern_counts': {'":': 8, '<': 85, '<?xml version=': 0, '>': 87, 'https://': 6, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4253277', 'n_tokens_mistral': 4711, 'n_tokens_neox': 4486, 'n_words': 1186}