LCOV - code coverage report
Current view: top level - src/userobjects - LAMMPSFileRunner.C (source / functions) Hit Total Coverage
Test: idaholab/magpie: 5710af Lines: 154 163 94.5 %
Date: 2025-07-21 23:34:39 Functions: 8 8 100.0 %
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 "LAMMPSFileRunner.h"
      10             : #include "MooseUtils.h"
      11             : #include "Function.h"
      12             : #include <sstream>
      13             : #include <cstring>
      14             : #include <cctype>
      15             : 
      16             : registerMooseObject("MagpieApp", LAMMPSFileRunner);
      17             : 
      18             : InputParameters
      19          48 : LAMMPSFileRunner::validParams()
      20             : {
      21          48 :   InputParameters params = MDRunBase::validParams();
      22          48 :   params.addClassDescription("Reads a LAMMPS dump file or sequence of dump files and "
      23             :                              "maps LAMMPS particles to MOOSE FEM mesh.");
      24          96 :   params.addParam<bool>("time_sequence",
      25          96 :                         false,
      26             :                         "If true, a sequence of dump files is read, "
      27             :                         "else a single dump file is read.");
      28          96 :   params.addRequiredParam<FileName>("lammps_file",
      29             :                                     "Name of LAMMPS dump file or file base of LAMMPS file for "
      30             :                                     "a sequence.");
      31          96 :   params.addRequiredParam<std::vector<unsigned int>>(
      32             :       "xyz_columns", "Column ids of the x, y, and z coordinates of particles.");
      33          96 :   params.addParam<std::vector<unsigned int>>(
      34             :       "property_columns", {}, "Column ids of the properties.");
      35          96 :   params.addParam<FunctionName>("time_conversion",
      36             :                                 "A conversion from FEM simulation time to MD time stamps.");
      37          48 :   params.addClassDescription("Allows coupling FEM calculations to LAMMPS dump files.");
      38          48 :   return params;
      39           0 : }
      40             : 
      41          24 : LAMMPSFileRunner::LAMMPSFileRunner(const InputParameters & parameters)
      42             :   : MDRunBase(parameters),
      43          24 :     _time_sequence(getParam<bool>("time_sequence")),
      44          24 :     _lammps_file(getParam<FileName>("lammps_file")),
      45          48 :     _pos_columns(getParam<std::vector<unsigned int>>("xyz_columns")),
      46          48 :     _prop_columns(getParam<std::vector<unsigned int>>("property_columns")),
      47          48 :     _time_conversion(nullptr)
      48             : {
      49             :   ;
      50          24 :   if (_pos_columns.size() != _dim)
      51           0 :     mooseError(
      52           0 :         "Dimensionality is ", _dim, " but xyz_columns has ", _pos_columns.size(), " entries.");
      53             : 
      54          48 :   if (isParamValid("time_conversion"))
      55           8 :     _time_conversion = &getFunction("time_conversion");
      56             : 
      57          24 :   if (_properties.size() != _prop_columns.size())
      58           0 :     mooseError("properties array must have same length as property_columns");
      59          24 : }
      60             : 
      61             : void
      62          24 : LAMMPSFileRunner::initialSetup()
      63             : {
      64             :   // call MDRunBase initialSetup
      65          24 :   MDRunBase::initialSetup();
      66             : 
      67          24 :   if (_time_sequence)
      68             :     return;
      69             : 
      70          12 :   readLAMMPSFile(_lammps_file);
      71          12 :   updateKDTree();
      72             : }
      73             : 
      74             : void
      75         260 : LAMMPSFileRunner::updateParticleList()
      76             : {
      77             :   // read LAMMPS file if a time sequence is read
      78         260 :   if (_time_sequence)
      79             :   {
      80             :     // map to MD time
      81         248 :     Real md_time = _t;
      82         248 :     if (_time_conversion)
      83         240 :       md_time = _time_conversion->value(_t, Point());
      84             : 
      85             :     // find files w/ timestamps "bracketing" md_time
      86             :     std::pair<std::string, std::string> files;
      87         248 :     std::pair<Real, Real> timestamps;
      88         248 :     findBracketingLAMMPSFiles(md_time, files, timestamps);
      89         248 :     readLAMMPSFileHistory(files, timestamps, md_time);
      90         248 :     updateKDTree();
      91             :   }
      92             : 
      93             :   // update the mapping to mesh if mesh has changed or ...
      94         260 :   mapMDParticles();
      95             : 
      96             :   // update the granular candidates as well if necessary
      97         260 :   if (_granular)
      98          16 :     updateElementGranularVolumes();
      99         260 : }
     100             : 
     101             : void
     102         248 : LAMMPSFileRunner::findBracketingLAMMPSFiles(Real md_time,
     103             :                                             std::pair<std::string, std::string> & filenames,
     104             :                                             std::pair<Real, Real> & timestamps)
     105             : {
     106             :   /// the return values
     107             :   std::string before;
     108             :   std::string after;
     109             : 
     110             :   /// extract path and file_base from provided lammps file entry
     111         248 :   std::pair<std::string, std::string> p = MooseUtils::splitFileName(_lammps_file);
     112             : 
     113             :   /// get all files in the directory
     114         744 :   std::list<std::string> files = MooseUtils::getFilesInDirs({p.first});
     115             : 
     116             :   /// loop over all files, retain the ones that begin with base and extract their timestamps
     117             :   std::vector<Real> times;
     118         248 :   times.reserve(files.size());
     119             : 
     120             :   unsigned int lammps_files_found = 0;
     121             :   Real closest_after_time = std::numeric_limits<Real>::max();
     122             :   Real closest_before_time = -std::numeric_limits<Real>::max();
     123        1334 :   for (auto & file : files)
     124             :   {
     125        1086 :     std::pair<std::string, std::string> f = MooseUtils::splitFileName(file);
     126        1086 :     if (f.second.compare(0, p.second.size(), p.second))
     127          64 :       continue;
     128             : 
     129             :     // split the file at dots and check the second item ==> timestamp
     130             :     Real timestamp;
     131             :     std::vector<std::string> elements;
     132        2044 :     MooseUtils::tokenize(f.second, elements, 1, ".");
     133        1022 :     if (elements.size() < 2)
     134           0 :       mooseError("Tokenizing filename ",
     135             :                  f.second,
     136             :                  " failed. LAMMPS filename must be base.<t>.xyz where <t> is the timestamp.");
     137        1022 :     std::stringstream sstr(elements[1]);
     138             : 
     139        2044 :     if (!isTimestamp(elements[1]))
     140             :       continue;
     141             :     sstr >> timestamp;
     142             : 
     143             :     // if (sstr >> timestamp || sstr.eof())
     144             :     //  if (sstr.fail())
     145             :     //    continue;
     146             : 
     147             :     // increase the counter & check if this file is a candidate for before/after
     148         976 :     ++lammps_files_found;
     149         976 :     if (timestamp > md_time && timestamp < closest_after_time)
     150             :     {
     151             :       closest_after_time = timestamp;
     152             :       after = file;
     153             :     }
     154         668 :     else if (timestamp <= md_time && timestamp > closest_before_time)
     155             :     {
     156             :       closest_before_time = timestamp;
     157             :       before = file;
     158             :     }
     159        1022 :   }
     160             : 
     161         248 :   if (lammps_files_found == 0)
     162           0 :     mooseError("No LAMMPS file with name base ", p.second, " in folder ", p.first);
     163             : 
     164             :   // guard against the case that only before or after were found => set the two equal
     165         248 :   if (before.size() == 0)
     166             :     before = after;
     167         248 :   else if (after.size() == 0)
     168             :     after = before;
     169             : 
     170         496 :   filenames = std::pair<std::string, std::string>(before, after);
     171             :   timestamps = std::pair<Real, Real>(closest_before_time, closest_after_time);
     172         496 : }
     173             : 
     174             : void
     175          12 : LAMMPSFileRunner::readLAMMPSFile(FileName filename)
     176             : {
     177             :   // check if this file is readable
     178          12 :   MooseUtils::checkFileReadable(filename);
     179             : 
     180             :   // read LAMMPS file
     181          12 :   std::ifstream file(filename);
     182             : 
     183             :   // skip first three lines
     184          48 :   for (unsigned int j = 0; j < 3; ++j)
     185          36 :     file.ignore(10000, '\n');
     186             : 
     187             :   // get number of particles
     188          12 :   file >> _n_particles;
     189             : 
     190             :   // skip another five lines
     191          84 :   for (unsigned int j = 0; j < 6; ++j)
     192          72 :     file.ignore(10000, '\n');
     193             : 
     194             :   // attach the right _md_particles.pos
     195          12 :   std::vector<Point> & position = _md_particles.pos;
     196             : 
     197             :   // zero out the position & id vector
     198             :   position.clear();
     199          12 :   _md_particles.id.clear();
     200          12 :   _md_particles.properties.clear();
     201             : 
     202             :   // size the md particle vector considering not all processors have all particles
     203          12 :   position.reserve(std::ceil(2.0 * _n_particles / _nproc));
     204          12 :   _md_particles.id.reserve(std::ceil(2.0 * _n_particles / _nproc));
     205          12 :   _md_particles.properties.reserve(std::ceil(2.0 * _n_particles / _nproc));
     206             : 
     207             :   // read particle entries
     208          12 :   _n_local_particles = 0;
     209          56 :   for (unsigned int j = 0; j < _n_particles; ++j)
     210             :   {
     211             :     // check if this particle is in the local bounding box
     212             :     std::string line;
     213          44 :     std::getline(file, line);
     214             :     std::vector<std::string> elements;
     215          88 :     MooseUtils::tokenize<std::string>(line, elements, 1, " ");
     216             : 
     217             : #ifndef NDEBUG
     218             :     // check that enough columns exist in debug
     219             :     // find largest requested column
     220             :     unsigned int largest_column = 0;
     221             :     for (unsigned int i = 0; i < _dim; ++i)
     222             :       if (_pos_columns[i] > largest_column)
     223             :         largest_column = _pos_columns[i];
     224             : 
     225             :     for (unsigned int i = 0; i < _prop_columns.size(); ++i)
     226             :       if (_prop_columns[i] > largest_column)
     227             :         largest_column = _prop_columns[i];
     228             : 
     229             :     if (largest_column >= elements.size())
     230             :       mooseError("Error reading ", filename, " on line ", line);
     231             : #endif
     232             : 
     233             :     // read location
     234             :     Point pos;
     235         176 :     for (unsigned int i = 0; i < _dim; ++i)
     236             :     {
     237         132 :       std::stringstream sstr(elements[_pos_columns[i]]);
     238             :       sstr >> pos(i);
     239         132 :     }
     240             : 
     241             :     // read properties
     242          44 :     std::vector<Real> props(_md_particles._prop_size);
     243          88 :     for (unsigned int i = 0; i < _prop_columns.size(); ++i)
     244             :     {
     245          44 :       unsigned int prop_id = _properties.get(i);
     246          44 :       std::stringstream sstr(elements[_prop_columns[i]]);
     247          44 :       sstr >> props[propIndex(prop_id)];
     248          44 :     }
     249             : 
     250             :     // check if this particle is in this processor BBox
     251          44 :     if (!_bbox.contains_point(pos))
     252             :       continue;
     253             : 
     254          40 :     ++_n_local_particles;
     255          40 :     position.push_back(pos);
     256          40 :     _md_particles.id.push_back(j);
     257          40 :     _md_particles.properties.push_back(props);
     258          44 :   }
     259          12 :   file.close();
     260             : 
     261             :   // size back
     262             :   position.shrink_to_fit();
     263             :   _md_particles.id.shrink_to_fit();
     264          12 : }
     265             : 
     266             : bool
     267        1022 : LAMMPSFileRunner::isTimestamp(std::string ts_candidate) const
     268             : {
     269        2238 :   for (auto & s : ts_candidate)
     270        1262 :     if (!isdigit(s))
     271             :       return false;
     272             :   return true;
     273             : }
     274             : 
     275             : void
     276         248 : LAMMPSFileRunner::readLAMMPSFileHistory(std::pair<FileName, FileName> filenames,
     277             :                                         std::pair<Real, Real> timestamps,
     278             :                                         Real current_time)
     279             : {
     280             :   // check if this file is readable
     281         248 :   MooseUtils::checkFileReadable(filenames.first);
     282         248 :   MooseUtils::checkFileReadable(filenames.second);
     283             : 
     284             :   // open LAMMPS file
     285         248 :   std::ifstream file_before(filenames.first);
     286         248 :   std::ifstream file_after(filenames.second);
     287             : 
     288             :   // compute weights for particle
     289         248 :   Real weight = (current_time - timestamps.first) / (timestamps.second - timestamps.first);
     290             : 
     291             :   // skip first three lines
     292         992 :   for (unsigned int j = 0; j < 3; ++j)
     293             :   {
     294         744 :     file_before.ignore(10000, '\n');
     295         744 :     file_after.ignore(10000, '\n');
     296             :   }
     297             : 
     298             :   // get number of particles
     299             :   unsigned int np;
     300         248 :   file_before >> _n_particles;
     301             :   file_after >> np;
     302         248 :   if (np != _n_particles)
     303           0 :     mooseError("Files ",
     304           0 :                filenames.first,
     305             :                " : ",
     306             :                _n_particles,
     307             :                " and ",
     308           0 :                filenames.second,
     309             :                " : ",
     310             :                np,
     311             :                " have different number of particles.");
     312             : 
     313             :   // skip another five lines
     314        1736 :   for (unsigned int j = 0; j < 6; ++j)
     315             :   {
     316        1488 :     file_before.ignore(10000, '\n');
     317        1488 :     file_after.ignore(10000, '\n');
     318             :   }
     319             : 
     320             :   // attach the right _md_particles.pos
     321         248 :   std::vector<Point> & position = _md_particles.pos;
     322             : 
     323             :   // zero out the position & id vector
     324             :   position.clear();
     325         248 :   _md_particles.id.clear();
     326         248 :   _md_particles.properties.clear();
     327             : 
     328             :   // size the md particle vector considering not all processors have all particles
     329         248 :   position.reserve(std::ceil(2.0 * _n_particles / _nproc));
     330         248 :   _md_particles.id.reserve(std::ceil(2.0 * _n_particles / _nproc));
     331         248 :   _md_particles.properties.reserve(std::ceil(2.0 * _n_particles / _nproc));
     332             : 
     333             :   // read particle entries
     334         248 :   _n_local_particles = 0;
     335         648 :   for (unsigned int j = 0; j < _n_particles; ++j)
     336             :   {
     337             :     // check if this particle is in the local bounding box
     338             :     // either in before or after state
     339             : 
     340             :     // get the MD particle from the before file
     341             :     std::vector<std::string> elements;
     342             :     std::string line_before;
     343         400 :     std::getline(file_before, line_before);
     344         800 :     MooseUtils::tokenize<std::string>(line_before, elements, 1, " ");
     345             : 
     346             : #ifndef NDEBUG
     347             :     // check that enough columns exist in debug/devel
     348             :     // find largest requested column
     349             :     unsigned int largest_column = 0;
     350             :     for (unsigned int i = 0; i < _dim; ++i)
     351             :       if (_pos_columns[i] > largest_column)
     352             :         largest_column = _pos_columns[i];
     353             : 
     354             :     for (unsigned int i = 0; i < _prop_columns.size(); ++i)
     355             :       if (_prop_columns[i] > largest_column)
     356             :         largest_column = _prop_columns[i];
     357             : 
     358             :     if (largest_column >= elements.size())
     359             :       mooseError("Error reading ", filenames.first, " on line ", line_before);
     360             : #endif
     361             : 
     362             :     Point pos_before;
     363        1600 :     for (unsigned int i = 0; i < _dim; ++i)
     364             :     {
     365        1200 :       std::stringstream sstr(elements[_pos_columns[i]]);
     366             :       sstr >> pos_before(i);
     367        1200 :     }
     368             : 
     369             :     // read properties from before file
     370         400 :     std::vector<Real> props_before(_md_particles._prop_size);
     371         480 :     for (unsigned int i = 0; i < _prop_columns.size(); ++i)
     372             :     {
     373          80 :       unsigned int prop_id = _properties.get(i);
     374          80 :       std::stringstream sstr(elements[_prop_columns[i]]);
     375          80 :       sstr >> props_before[propIndex(prop_id)];
     376          80 :     }
     377             : 
     378             :     // get the MD particle from the after file
     379             :     elements.clear();
     380             :     std::string line_after;
     381         400 :     std::getline(file_after, line_after);
     382             : 
     383         800 :     MooseUtils::tokenize<std::string>(line_after, elements, 1, " ");
     384             : 
     385             :     // we have determined largest_column in !NDEBUG so we can use it in mooseAssert
     386             :     mooseAssert(largest_column < elements.size(),
     387             :                 "Error reading " << filenames.second << " on line " << line_after);
     388             : 
     389             :     Point pos_after;
     390        1600 :     for (unsigned int i = 0; i < _dim; ++i)
     391             :     {
     392        1200 :       std::stringstream sstr(elements[_pos_columns[i]]);
     393             :       sstr >> pos_after(i);
     394        1200 :     }
     395             : 
     396             :     // read properties from before file
     397         400 :     std::vector<Real> props_after(_md_particles._prop_size);
     398         480 :     for (unsigned int i = 0; i < _prop_columns.size(); ++i)
     399             :     {
     400          80 :       unsigned int prop_id = _properties.get(i);
     401          80 :       std::stringstream sstr(elements[_prop_columns[i]]);
     402          80 :       sstr >> props_after[propIndex(prop_id)];
     403          80 :     }
     404             : 
     405             :     // check if this particle is in this processor BBox
     406         400 :     if (!_bbox.contains_point(pos_after) && !_bbox.contains_point(pos_before))
     407             :       continue;
     408             : 
     409         400 :     ++_n_local_particles;
     410         400 :     position.push_back((1 - weight) * pos_before + weight * pos_after);
     411         400 :     _md_particles.id.push_back(j);
     412         400 :     _md_particles.properties.push_back(std::vector<Real>(_md_particles._prop_size));
     413         480 :     for (unsigned int i = 0; i < _md_particles._prop_size; ++i)
     414          80 :       _md_particles.properties.back()[i] = (1 - weight) * props_before[i] + weight * props_after[i];
     415         400 :   }
     416         248 :   file_before.close();
     417         248 :   file_after.close();
     418             : 
     419             :   // size back
     420             :   position.shrink_to_fit();
     421             :   _md_particles.id.shrink_to_fit();
     422         248 : }

Generated by: LCOV version 1.14