Restrict screen output calling from Fortran

One hack is to write a quick C++ routine that you can bind to a C name.
This function reassigns the value of lmp->screen to another file (with
a name) that you open in that function. This must be called AFTER you
initialize LAMMPS. Such a routine might look something like,
   #include "lammps.h"
   #include <cstdio>
   void reset_screen (void *ptr)
   {
     FILE *newscreen = fopen("my_output_file.txt", "w");
     LAMMPS *lmp = static_cast<LAMMPS*> (ptr);
     lmp->screen = newscreen;
   }

For a non-hard-coded way, read on....

There actually IS no 100% portable way to pass a string from FORTRAN 77
to C (that's why the ISO_C_BINDING module was written for Fortran 2003).
However, most Fortran 90 compilers will "get it right" if you use
technically non-portable methods that assume the following:
  * C strings are terminated by ASCII code zero (CHAR(0) should work
    in FORTRAN 77, and either that or ACHAR(0) will work on newer
    compilers that support Fortran 90). ISO_C_BINDING stores this as the
    constant C_NULL_CHAR, which is guaranteed portable
  * Fortran strings are "scalar" quantities with an associated length, and
    are distinct from character arrays
  * C strings and Fortran character arrays are generally stored the same
    way, but C strings are not stored the same way as Fortran strings
    (i.e., characters with len /= 1). This is guaranteed true if the
    compiler supports ISO_C_BINDING and you have declared the character's
    KIND to be C_CHAR
  * Spaces at the end of Fortran strings are not significant

I am nearly certain that the following function will convert a Fortran
string to a C string portably,
    pure function string2Cstring (string) result (C_string)
       use, intrinsic :: ISO_C_binding, only : C_char, C_NULL_CHAR
       character (len=*), intent(in) :: string
       character (len=1, kind=C_char) :: C_string (len_trim(string)+1)
       integer :: i, n
       n = len_trim (string)
       forall (i = 1:n)
          C_string(i) = string(i:i)
       end forall
       C_string(n+1) = C_NULL_CHAR
    end function string2Cstring
assuming you can use ISO_C_binding. If not, try omitting that line and
anything that breaks (e.g., kind=C_char). The FORALL loop might also need
to be changed to a plain DO loop.

This gets you half way there. The second half is to convert that
C-style string to a tokenized list of command arguments, which is tricky
because you have to make certain the Fortran compiler doesn't deallocate
things that the C compiler still expected to be allocated. My version of
that routine is too long to post on the list, though. Instead, you could
write the following C routine that initializes LAMMPS based on a single
command line (which you generate in Fortran, perhaps):

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "library.h"
void init_lammps (void **lmp, const char *command_line) {
   char **new_argv = NULL, *tok = NULL, command[strlen(command_line+1];
   int new_argc = 0, nargs = 0;
   strcpy (command, command_line);
   tok = strtok (command, " \t\n\r");
   while (tok != NULL) { nargs += 1; tok = strtok (NULL, " \t\n\r"); }
   new_argv = (char**) malloc (nargs*sizeof(char*));
   strcpy (command, command_line);
   tok = strtok (command, " \t\n\r");
   while (tok != NULL) {
     new_argv[new_argc++] = tok;
     tok = strtok (NULL, " \t\n\r");
   }
   lammps_open_no_mpi (new_argc, new_argv, lmp);
   free (new_argv);
}

That should essentially do what the shell does, converting your string to
an argc/argv pair. It might be confused by quoted strings, though.

Karl D. Hammond
University of Tennessee, Knoxville
[email protected]

"You can never know everything, and part of what you know is always
   wrong. A portion of wisdom lies in knowing that. A portion of courage
   lies in going on anyway."
"Nothing ever goes as you expect. Expect nothing, and you will not be
   surprised."

Message: 8
Date: Sun, 12 Aug 2012 06:42:50 +0200
From: Axel Kohlmeyer <[email protected]>
Subject: Re: [lammps-users] Restrict screen output calling from
      Fortran
To: "Gardner, David James" <[email protected]>
Cc: "[email protected]"
      <[email protected]>
Message-ID:

<[email protected]>

Content-Type: text/plain; charset=ISO-8859-1

david,

Hello all,

I'm trying to couple LAMMPS to a Fortran code and would like to have
LAMMPS write screen information to a file or not print any information
to the screen.

I see when using LAMMPS at the command line this can be done with
-screen file and -screen none but I do not see how to do this when
calling LAMMPS as a library.

it can be easily done, in principle when you call lammps from C or C++,
but from fortran it i not as easy, since there is no *easy and portable*
way to pass an argument array from fortran to C.

you have essentially three choices:
use the fortran 2003/2008 style iso C
bindings and the C library interface,

or make a suggestion what would be
a good and portable way to pass the
list of arguments from fortran to C
(in a fortran 77 compatible way by preference)

or hack the fortran library wrapper to
provide the flags that you want to be
when initializing the lammps object.

let us know, if you have additional
questions on this.

ciao,
   axel.

Thanks,
David

----------------------------------------------------------------------------$

Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond.

Discussions

will include endpoint security, mobile security and the latest in

malware

One hack is to write a quick C++ routine that you can bind to a C name.
This function reassigns the value of lmp->screen to another file (with
a name) that you open in that function. This must be called AFTER you
initialize LAMMPS. Such a routine might look something like,

even easier: using -screen none is equivalent to:

lmp->screen = NULL;

[...]

I am nearly certain that the following function will convert a Fortran
string to a C string portably,
   pure function string2Cstring (string) result (C_string)
      use, intrinsic :: ISO_C_binding, only : C_char, C_NULL_CHAR
      character (len=*), intent(in) :: string
      character (len=1, kind=C_char) :: C_string (len_trim(string)+1)
      integer :: i, n
      n = len_trim (string)
      forall (i = 1:n)
         C_string(i) = string(i:i)
      end forall
      C_string(n+1) = C_NULL_CHAR
   end function string2Cstring
assuming you can use ISO_C_binding. If not, try omitting that line and
anything that breaks (e.g., kind=C_char). The FORALL loop might also need
to be changed to a plain DO loop.

This gets you half way there. The second half is to convert that
C-style string to a tokenized list of command arguments, which is tricky
because you have to make certain the Fortran compiler doesn't deallocate

i think in this special case one can best go with a hybrid
of what you are suggesting and still stick to the simple
fortran 77 compatible and hence more portable wrapper.

one would add to the API for creating a LAMMPS instance
two arguments: 1) a pointer to the string and 2) the length.
even though almost all compilers follow the original sun
string conventions, it is safer to just pass the length.

from then on it gets rather simple:
allocate a buffer of size lenght+1
memcpy the fortran string and then
zero terminate it. this copy can than
safely be tokenized. the leaked
memory will hurt nobody, and could
in principle be recovered, if it really,
really would matter.

[...]

an argc/argv pair. It might be confused by quoted strings, though.

strtok() will get confused, but this is uncommon for lammps arguments.

axel.

even easier: using -screen none is equivalent to:

lmp->screen = NULL;

Yes, of course, and you can define another file handle as the right-hand
side and have it work, too.

i think in this special case one can best go with a hybrid
of what you are suggesting and still stick to the simple
fortran 77 compatible and hence more portable wrapper.

The trick with underscores at the end of names for fortran variables and
procedures in the C name space is compatible with most compilers to the
extent of almost being universal. Incidentally, this naming convention
was NOT adopted for the default name of Fortran/C interoperable
procedures; the default for the NAME= field of the BIND attribute is
the all-lower-case version of the procedure name (no trailing underscore).

That said, I agree: the routines in libfwrapper.c are generally useful,
even without being 100% portable (I'll settle for 90%, or perhaps it's
even greater than that), since there are probably many people out there
using old compilers that don't support ISO_C_BINDING yet. There are
many other things that are hard to do with mixed language programming,
though, which is why I usually only recommend it for those who don't
mind getting their hands dirty.

Steve actually just released a Fortran wrapper I wrote some time ago
(though I'm now having to tweak it with the revisions to library.cpp that
came out yesterday) that solves this problem using ISO_C_BINDING methods.
So have a look at examples/COUPLE/fortran2 for that.

Good luck!

Karl D. Hammond

Thank you Alex, Karl, and Steve for all of your help. It is most appreciated. I've updated to the most recent version of LAMMPS with the new wrapper and it looks to have everything I need. I'll start testing it out this week and let you know how it goes.

Thanks again,
David