Call member funtion of a fix(not used in lammps script) class from another Fix(used in lammps script)

I have moderately edited lammps code before for changing behavior of existing fix. This time I need sort of a big overhaul. But I am still a novice and need help in achieving exactly what is mentioned in the title.

I understand that if it is a common funtion that is part of lammps’ execution flow(eg. end_of_step, post_force etc.) I can just copy that content of that function from one fix class (say FixFirst::end_of_step(){copy from here}) and paste it to the target fix I want to modify (say Fix2nd::end_of_step(){append}). Of course I will remove duplicates. Pretty straightforward. But what if,

Case 1:
the FixFirst::end_of_step(){copy from here} contains unique variable to that class FixFirst::. Since I am not calling the fix first style in lammps script, the class should not be available, if I am correct. So using those unique variables will cause error. This is where I am stuck. I could add those variable definition to the target fix class Fix2nd::. But in my case that would require too many variables to be copied. And I am not even sure how copying will mess up the code.

Case 2:
This is the particular case for me. I have some functionality in the source fix that is in a custom(say, function FixFirst::custom(){custom variable and so on}), not the ones lammps calls on its own. So it is definitely required that this class is available, so that I can call this function when needed from within my target fix Fix_2nd.cpp file. Just adding the header files won’t do I presume . Since I will only add fix 2nd style in my lammps simulation model, and not fix first.

So how to make sure that the source ‘fix’ class functions are available to call from target ‘fix’. I know this is more of a c++ problem not a lammps one, but since the community has people who are experienced with handling Lammps code, I am sincerely asking for some suggestions or hints.

Thank you,
Sabik

The cases you describe make no sense at this general level.

To have class members initialized, you need to create an instance of the fix class. Which means that this fix will be executed during the different stages of a timestep as indicated by its Fix::setmask() function and its own setting for execution frequency. Most fixes either manipulate the system or collect/output data at predetermined intervals. If they do compute properties for further use (e.g. fix store/state or fix ave/*) then you can access them via the fix’s compute_*() functions. In that case you must pass the fix id to the target fix and then look up the fix via Modify::get_fix_by_id() and then call the fix’s compute_*() function. To call a function of a fix that is not defined as a public API in fix.h, you need bypass polymorphism and cast the fix from the base class pointer to a pointer of the specific class.
But the fix must be instantiated and thus to access an “inactive” fix is not possible.

On demand computation only make sense for compute class instances. Those can be created within the constructor of a fix (you cannot create a fix within a fix constructor, or a compute within a compute constructor for technical reasons) and then, again, their standard APIs defined in compute.h can be called. This happens a lot, e.g. fix nvt creates a compute temp instance for the group it is operating on. Other fixes create compute pe, compute press instances and so on for more complex and specific cases.

Okay, so I cannot access members of the target fix for convenience without having it called from lammps script. That makes my work way more complicated.

let, me attach a c++ code snippet.

void FixEPH::force_prlcm() {
//creat friction force
  "code for generating friction force_here"

        f_EPH[i][0] = alpha_i * w_i[i][0];
        f_EPH[i][1] = alpha_i * w_i[i][1];
        f_EPH[i][2] = alpha_i * w_i[i][2];

      
  // create random forces
   "code for generate random force here"

        f_RNG[i][0] = var * xi_i[i][0];
        f_RNG[i][1] = var * xi_i[i][1];
        f_RNG[i][2] = var * xi_i[i][2];

}

Then, in post force,

void FixEPH::post_force(int vflag) {
----other stuffs
force_prlcm();

if((eph_flag & Flag::FRICTION) && !(eph_flag & Flag::NOFRICTION)) {
    for(int i = 0; i < nlocal; i++) {
      f[i][0] += f_EPH[i][0];
      f[i][1] += f_EPH[i][1];
      f[i][2] += f_EPH[i][2];
    }
  }

  if((eph_flag & Flag::RANDOM) && !(eph_flag & Flag::NORANDOM)) {
    for(int i = 0; i < nlocal; i++) {
      f[i][0] += f_RNG[i][0];
      f[i][1] += f_RNG[i][1];
      f[i][2] += f_RNG[i][2];
    }
  }
------ other stuffs
}

Fix_eph contains a different way of calculating the frictional component of langevin dynamics that i am trying to bring to FixTTMmod. This is called during post_force. So I wanted to remove the existing langevin equation in FixTTMmod:post force, and just call this function (fixEph::force_prlcm()), incase fix_eph class was available. Besides, force_prlcm() itself, there are other custom function within fix_eph class that also need to be available.
Why not use fix_eph itself, you may ask. That’s because I have implemented a custom laser energy deposition model with fix_ttm, which i am not sure how to integrate with fix_eph. And I have been working with FixTTMmod for quite a long time. So other than editing fixTTMmod class and adding everything necessary from fix_eph, is there any other way that is particular to lammps code?

Possibly, something like removing all flags in fixeph:setmask() and applying fix_eph over the same group of atoms fix ttm/mod is acting on? is this possible?

EDIT: Never mind, I think I am messing up the scope of difference classes. Guess, I will have to incorporate those members from fix eph into fix ttm. Thanks for your response @akohlmey

Sorry, but you are “off the reservation™” with what you are describing. If you do not trust your own C++ programming and want to use rather ugly hacks to circumvent your concerns, then you are in trouble.

I have a hard enough time to maintain a reasonable code quality in the LAMMPS sources with contributions often provided by people without formal training in programming C++ (and being without formal training myself and having learned many lessons the hard way over many years) and therefore I prefer to not spend any time on hacks and workarounds that go against the very principles that I am trying to maintain (and establish in legacy code that is still waiting for a refactoring).

LAMMPS is open source, so there is nothing that keeps you from attempting things like you describe.
I wish you good luck in your endeavors, but that is all. Perhaps someone else here is more adventurous…

Why not just write a completely new fix ttm/eph, copying and pasting the relevant source code from other fixes as needed?

That is what I am currently doing @srtee. It would have been better for me if I could edit on fix eph rather than fix ttm. Becasue fix_eph requires access to neighbor list for calculating langevin damping based on relative postion of atoms. I am not yet confident to deal with codes that need to access neighbor list. Particularly with parallel computation where atom near each processor boundary requires extra care to deal with. So, if I could keep fix eph as it is and edit the electronic equation code, that part would have been left untouched. However the electronic equation is significantly different from Fix TTM/mod. It does not even have provision for a time dependent source term and the area of application cannot be modified like in fix ttm using left and right marker. Making it difficult to model a non-reflective boundary condition with it.

Can you refer to any reading material for understanding parallel computing specifically for molecular dynamics? Particularly handling atom near individual processor boundary (ghost atoms and stuffs)

Have you checked out the most recent LAMMPS paper from 2022 (or the original from 1995)?

There is also a growing selection of material about the implementation of LAMMPS in the manual itself: 4. Information for Developers — LAMMPS documentation Last time I checked that section covered about 140 pages of the about 3000 pages of the manual in PDF format.

Beyond that, there is always the information in the comments in the source code. :wink:

I am having to understand the purpose of each function in the fix ttm mod class. When doing so, I came across a function definition that is not making much sense to me even with the comments.

In fix ttm Mod class, the following is a snippet from post_force()

void FixTTMMod::post_force(int /*vflag*/)
{
"code for determining langevin forces"
.........

  // apply langevin forces 
  for (int i = 0; i < nlocal; i++) {
    if (mask[i] & groupbit) {
      f[i][0] += flangevin[i][0];
      f[i][1] += flangevin[i][1];
      f[i][2] += flangevin[i][2];
    }
  }
}

Okay, good. So the force derived from the potential function has been increased by the amount corresponding to Langevin equation. However,

void FixTTMMod::post_force_setup(int /*vflag*/)
{
  double **f = atom->f;
  int *mask = atom->mask;
  int nlocal = atom->nlocal;

  // apply langevin forces that have been stored from previous run

  for (int i = 0; i < nlocal; i++) {
    if (mask[i] & groupbit) {
      f[i][0] += flangevin[i][0];
      f[i][1] += flangevin[i][1];
      f[i][2] += flangevin[i][2];
    }
  }
}

The basic execution sequence is given in the book (Extending and Modifying LAMMPS ). However, that does not contain post_force_setup(). So, I believe this is a conditional function. I have no clue what triggers it. Does this post_force_setup() function matter at all? It is not mentioned in the setmask() definition. Also, what is meant by previous run? Is it previous iteration?

If some has any idea, please respond.
Thank you

What is a “conditional function”?

Have you tried using “grep” to search through files?

Would you write a function into your code and then not use it?

Fix::setmask() sets the mask for optional and polymorph functions. Functions that are always called are either set to null (and thus must be reimplemented) or a have an overridable dummy.
If these functions are not defined in fix.h they must be an invention of the fix ttm/mod authors and they would have to call them from within the fix.

Obviously a previous run command or data read back from a restart.

Thank you for the response, Professor. I’ve found it

void FixTTMMod::setup(int vflag)
{
  if (utils::strmatch(update->integrate_style,"^verlet")) {
    post_force_setup(vflag);
  } 

By conditional, I meant that the execution is dependent on other setup configuration. Such as here, post_force_setup(vflag) is called when integrate style is verlet. Otherwise, post_force_respa_setup will be called.

So, it should not matter whether restart from a previous simulation or not. As long as verlet integrator is on, this will be called. Now, at which step is it being called? And what change is happening between the values of atom->f in post_force and post_force_setup?

https://docs.lammps.org/Modify_fix.html

Ah, now makes sense. The naming convention made me think post_force_setup() was part of lammps core function calls. I see that this is only called once. Hence, the restart command

Thank you, professor.