Difference between revisions of "Fortran"

From Octave
Jump to navigation Jump to search
(Add description about the data file.)
 
(16 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 +
This page describes an example of how to call liboctave functions from a Fortran program.
 +
In the example we will load a single matrix, stored in ASCII format, from a data file.
 +
It consists of two steps:
  
 +
# write a C++ function with a C compatible interface and C linkage that reads a variable from an Octave ASCII file
 +
# write Fortran code using the "iso_c_binding" intrinsic module to call the C++ function
  
{{Code|C++ function to load a matrix from an ASCII file in Octave native format|<syntaxhighlight lang="C" style="font-size:13px">
+
=== Data file ===
#include <octave_file_io.h>
+
 
 +
{{File|data.txt|<syntaxhighlight lang="text">
 +
1.59797350e-01 5.41307474e-01 1.12127655e-01 2.09249248e-01
 +
3.22564589e-01 7.94307947e-01 8.25924316e-01 5.38352076e-01
 +
3.63990736e-01 1.90371212e-02 2.89370865e-01 1.30131246e-01
 +
6.28360462e-01 1.98831330e-01 6.89539723e-01 6.91062597e-01
 +
</syntaxhighlight>}}
 +
 
 +
The file was generated with
 +
 
 +
<syntaxhighlight lang="octave">
 +
A = rand (4);
 +
save -ascii data.txt A
 +
</syntaxhighlight>
 +
 
 +
=== C++ function ===
  
 +
C++ function to load a single matrix, stored in ASCII format, from a data file.
  
 +
{{File|octave_file_io.cc|<syntaxhighlight lang="C++">
 +
// Octave header
 
#include <octave/oct.h>
 
#include <octave/oct.h>
#include <octave/ov.h>
+
#include <octave/ls-mat-ascii.h>
#include <fstream>
 
#include <ls-oct-ascii.h>
 
#include <octave/octave.h>
 
#include <octave/parse.h>
 
#include <octave/toplev.h>
 
#include <octave/load-save.h>
 
#include <octave/oct-map.h>
 
#include <cstring>
 
  
int octave_load (const char* filename, const char* varname, double** data, int* numel)
+
// Custom header containing the C compatible interface
 +
#include <octave_file_io.h>
 +
 
 +
//! Load a single matrix, stored in ASCII format, from a data file.
 +
//!
 +
//! @param file_name name of the data file.
 +
//! @param data pointer to the read-in matrix stored as fortran vector
 +
//!            (column-major order).
 +
//! @param numel number of elements in @p data.
 +
 
 +
int octave_load (const char* file_name, double** data, int* numel)
 
{
 
{
    
+
   // Define variable to hold the read data.
   string_vector argv (1);
+
   octave_value read_data;
  load_save_format format = LS_ASCII;
 
  
   install_types (); 
+
   // Read a plain ASCII matrix from data file.
  argv(0) = varname;
+
   std::ifstream in_file_stream (file_name, std::ios::binary);
 
+
  read_mat_ascii_data (in_file_stream, file_name, read_data);
   std::ios::openmode mode = std::ios::in | std::ios::binary;
+
   in_file_stream.close ();
   std::string fname (filename);
 
  
   std::ifstream file (fname.c_str (), mode);
+
   // Convert read data to numerical array (matrix).
    
+
   NDArray A = read_data.array_value ();
  octave_scalar_map m = do_load (file, fname, format, oct_mach_info::flt_fmt_unknown,
 
                                false, false, true, argv, 0, 1, 1).scalar_map_value ();
 
  
   file.close ();
+
   // Extract number of elements in matrix A.
 +
  *numel = A.numel ();
  
   Array<double> M = m.contents (varname).array_value ();
+
   // Allocate memory to pointer to returned values.
  if (error_state)
+
  *data  = (double*) malloc (A.numel () * sizeof (double));
    return -1;
 
  
   *data = (double*) malloc (M.numel () * sizeof (double));
+
   // Copy the content of matrix A to data structure Fortran can handle.
  *numel = M.numel ();
+
  memcpy (*data, A.fortran_vec (), A.numel () * sizeof (double));
  
  memcpy (*data, M.fortran_vec (), *numel * sizeof (double));
+
   return 0;
 
 
   return 0;
 
 
}
 
}
 
</syntaxhighlight>}}
 
</syntaxhighlight>}}
  
 +
=== Header file ===
  
{{Code|C interface to a function linking to liboctave|<syntaxhighlight lang="C" style="font-size:13px">
+
Header file with C interface to {{Path|octave_file_io.cc}}.
 +
 
 +
{{File|octave_file_io.h|<syntaxhighlight lang="C">
 
#ifndef OCTAVE_FILE_IO_H
 
#ifndef OCTAVE_FILE_IO_H
 
#define OCTAVE_FILE_IO_H
 
#define OCTAVE_FILE_IO_H
Line 57: Line 79:
 
#endif
 
#endif
 
    
 
    
   int octave_load (const char* filename, const char* varname, double** data, int* numel);
+
   int octave_load (const char* filename, double** data, int* numel);
  
 
#ifdef __cplusplus
 
#ifdef __cplusplus
Line 65: Line 87:
 
#endif
 
#endif
 
</syntaxhighlight>}}
 
</syntaxhighlight>}}
 +
 +
=== Fortran Code ===
 +
 +
Fortran main program to read the plain ASCII matrix with the help of the Octave-C++ function.  The read in matrix is printed to the screen.
 +
 +
{{File|octave_file_io_example.f90|<syntaxhighlight lang="fortran">
 +
program octave_file_io_example
 +
 +
  use iso_c_binding
 +
 +
  implicit none
 +
 +
  interface
 +
    function octave_load (filename, data, numel) bind(c, name="octave_load")
 +
 +
      use iso_c_binding
 +
      implicit none
 +
 +
      integer(c_int) :: octave_load
 +
 +
      character(kind=c_char), intent(in) :: filename(*)
 +
 +
      type(c_ptr), intent(out) :: data
 +
      integer(c_int), intent(out) :: numel
 +
 +
    end function octave_load
 +
  end interface
 +
 +
  integer(c_int) :: res
 +
  type(c_ptr) :: data
 +
  real(c_double), pointer :: fdata(:)
 +
  integer(c_int) :: numel
 +
 +
  res = octave_load (c_char_'data.txt' // c_null_char, data, numel)
 +
 +
  call c_f_pointer (data, fdata, (/numel/))
 +
 +
  write (*,*) fdata
 +
 +
end program octave_file_io_example
 +
</syntaxhighlight>}}
 +
 +
=== Compiling the code ===
 +
 +
Generate {{Path|octave_file_io.o}} from {{Path|octave_file_io.cc}}.
 +
 +
mkoctfile -I. -c octave_file_io.cc
 +
 +
Generate {{Path|octave_file_io_example.exe}} from {{Path|octave_file_io_example.f90}} including {{Path|octave_file_io.o}}.
 +
 +
mkoctfile -I. --link-stand-alone octave_file_io_example.f90 octave_file_io.o -o octave_file_io_example.exe -lgfortran
 +
 +
If you receive errors about missing libraries, make sure your <code>LD_LIBRARY_PATH</code> is set correctly to find all Octave libraries, e.g.
 +
 +
$ ldd ./octave_file_io_example.exe
 +
...
 +
        libgfortran.so.4 => /usr/lib64/libgfortran.so.4 (0x00007fe9eb62b000)
 +
        liboctinterp.so.8 => not found
 +
        liboctave.so.8 => not found
 +
...
 +
 +
Then find {{Path|liboctinterp.so.8}} and {{Path|liboctave.so.8}} on your system and type
 +
 +
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/lib/octave/{{Release}}/
 +
 +
 +
[[Category:Examples]]

Latest revision as of 22:17, 12 July 2020

This page describes an example of how to call liboctave functions from a Fortran program. In the example we will load a single matrix, stored in ASCII format, from a data file. It consists of two steps:

  1. write a C++ function with a C compatible interface and C linkage that reads a variable from an Octave ASCII file
  2. write Fortran code using the "iso_c_binding" intrinsic module to call the C++ function

Data file[edit]

File: data.txt
 1.59797350e-01 5.41307474e-01 1.12127655e-01 2.09249248e-01
 3.22564589e-01 7.94307947e-01 8.25924316e-01 5.38352076e-01
 3.63990736e-01 1.90371212e-02 2.89370865e-01 1.30131246e-01
 6.28360462e-01 1.98831330e-01 6.89539723e-01 6.91062597e-01

The file was generated with

A = rand (4);
save -ascii data.txt A

C++ function[edit]

C++ function to load a single matrix, stored in ASCII format, from a data file.

File: octave_file_io.cc
// Octave header
#include <octave/oct.h>
#include <octave/ls-mat-ascii.h>

// Custom header containing the C compatible interface
#include <octave_file_io.h>

//! Load a single matrix, stored in ASCII format, from a data file.
//!
//! @param file_name name of the data file.
//! @param data pointer to the read-in matrix stored as fortran vector
//!             (column-major order).
//! @param numel number of elements in @p data.

int octave_load (const char* file_name, double** data, int* numel)
{
  // Define variable to hold the read data.
  octave_value read_data;

  // Read a plain ASCII matrix from data file.
  std::ifstream in_file_stream (file_name, std::ios::binary);
  read_mat_ascii_data (in_file_stream, file_name, read_data);
  in_file_stream.close ();

  // Convert read data to numerical array (matrix).
  NDArray A = read_data.array_value ();

  // Extract number of elements in matrix A.
  *numel = A.numel ();

  // Allocate memory to pointer to returned values.
  *data  = (double*) malloc (A.numel () * sizeof (double));

  // Copy the content of matrix A to data structure Fortran can handle.
  memcpy (*data, A.fortran_vec (), A.numel () * sizeof (double));

  return 0;
}

Header file[edit]

Header file with C interface to octave_file_io.cc.

File: octave_file_io.h
#ifndef OCTAVE_FILE_IO_H
#define OCTAVE_FILE_IO_H

#ifdef __cplusplus
extern "C" {
#endif
  
  int octave_load (const char* filename, double** data, int* numel);

#ifdef __cplusplus
}
#endif

#endif

Fortran Code[edit]

Fortran main program to read the plain ASCII matrix with the help of the Octave-C++ function. The read in matrix is printed to the screen.

File: octave_file_io_example.f90
program octave_file_io_example

  use iso_c_binding

  implicit none

  interface
     function octave_load (filename, data, numel) bind(c, name="octave_load")

       use iso_c_binding
       implicit none

       integer(c_int) :: octave_load

       character(kind=c_char), intent(in) :: filename(*)

       type(c_ptr), intent(out) :: data
       integer(c_int), intent(out) :: numel

     end function octave_load
  end interface

  integer(c_int) :: res
  type(c_ptr) :: data
  real(c_double), pointer :: fdata(:)
  integer(c_int) :: numel

  res = octave_load (c_char_'data.txt' // c_null_char, data, numel)

  call c_f_pointer (data, fdata, (/numel/))

  write (*,*) fdata

end program octave_file_io_example

Compiling the code[edit]

Generate octave_file_io.o from octave_file_io.cc.

mkoctfile -I. -c octave_file_io.cc

Generate octave_file_io_example.exe from octave_file_io_example.f90 including octave_file_io.o.

mkoctfile -I. --link-stand-alone octave_file_io_example.f90 octave_file_io.o -o octave_file_io_example.exe -lgfortran

If you receive errors about missing libraries, make sure your LD_LIBRARY_PATH is set correctly to find all Octave libraries, e.g.

$ ldd ./octave_file_io_example.exe
...
       libgfortran.so.4 => /usr/lib64/libgfortran.so.4 (0x00007fe9eb62b000)
       liboctinterp.so.8 => not found
       liboctave.so.8 => not found
...

Then find liboctinterp.so.8 and liboctave.so.8 on your system and type

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/lib/octave/5.2.0/