feat(fortran): Added fortran bindings
Building of the C API GridFire can now be used from fotran using the gridfire_mod fortran module. This exposes the same, limited, set of funcitonality that the C API does.
This commit is contained in:
216
README.md
216
README.md
@@ -896,6 +896,220 @@ if __name__ == "__main__":
|
||||
```
|
||||
|
||||
|
||||
# External Usage
|
||||
C++ does not have a stable ABI nor does it make any strong guarantees about stl container layouts between compiler versions.
|
||||
Therefore, GridFire includes a set of stable C bindings which can be used to interface with a limited subset of GridFire functionality
|
||||
from other languages.
|
||||
|
||||
> **Note:** These bindings are not intended to allow GridFire to be extended from other languages; rather, they are intended to allow GridFire to be used as a
|
||||
> black-box library from other languages.
|
||||
|
||||
> **Note:** One assumption for external usage is that the ordering of the species list will not change. That is to say that whatever order the array
|
||||
> used to register the species is will be assumed to always be the order used when passing abundance arrays to and from GridFire.
|
||||
|
||||
> **Note:** Because the C API does not pass the general Composition object a `mass_lost`
|
||||
> output parameter has been added to the evolve calls, this tracks the total mass in species which have not been registered with the C API GridFire by the caller
|
||||
## C API Overview
|
||||
In general when using the C API the workflow is to
|
||||
|
||||
1. create a `gf_context` pointer. This object holds the state of GridFire so that it does not need to be re-initialized for each call.
|
||||
2. call initialization routines on the context to set up the engine and solver you wish to use.
|
||||
3. call the `gf_evolve` function to evolve a network over some time.
|
||||
4. At each state check the ret code of the function to ensure that no errors occurred. Valid ret-codes are 0 and 1. All other ret codes indicate an error.
|
||||
5. Finally, call `gf_free` to free the context and all associated memory.
|
||||
|
||||
### C Example
|
||||
|
||||
```c++
|
||||
#include "gridfire/extern/gridfire_extern.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define NUM_SPECIES 8
|
||||
|
||||
// Define a macro to check return codes
|
||||
#define GF_CHECK_RET_CODE(ret, ctx, msg) \
|
||||
if (ret != 0 && ret != 1) { \
|
||||
printf("Error %s: %s\n", msg, gf_get_last_error_message(ctx)); \
|
||||
gf_free(ctx); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
int main() {
|
||||
void* gf_context = gf_init();
|
||||
|
||||
const char* species_names[NUM_SPECIES];
|
||||
species_names[0] = "H-1";
|
||||
species_names[1] = "He-3";
|
||||
species_names[2] = "He-4";
|
||||
species_names[3] = "C-12";
|
||||
species_names[4] = "N-14";
|
||||
species_names[5] = "O-16";
|
||||
species_names[6] = "Ne-20";
|
||||
species_names[7] = "Mg-24";
|
||||
const double abundances[NUM_SPECIES] = {0.702616602672027, 9.74791583949078e-06, 0.06895512307276903, 0.00025, 7.855418029399437e-05, 0.0006014411598306529, 8.103062886768109e-05, 2.151340851063217e-05};
|
||||
|
||||
int ret = gf_register_species(gf_context, NUM_SPECIES, species_names);
|
||||
GF_CHECK_RET_CODE(ret, gf_context, "Species Registration");
|
||||
|
||||
ret = gf_construct_engine_from_policy(gf_context, "MAIN_SEQUENCE_POLICY", abundances, NUM_SPECIES);
|
||||
GF_CHECK_RET_CODE(ret, gf_context, "Policy and Engine Construction");
|
||||
|
||||
ret = gf_construct_solver_from_engine(gf_context, "CVODE");
|
||||
GF_CHECK_RET_CODE(ret, gf_context, "Solver Construction");
|
||||
|
||||
// When using the C API it is assumed that the caller will ensure that the output arrays are large enough to hold the results.
|
||||
double Y_out[NUM_SPECIES];
|
||||
double energy_out;
|
||||
double dEps_dT;
|
||||
double dEps_dRho;
|
||||
double mass_lost;
|
||||
|
||||
ret = gf_evolve(
|
||||
gf_context,
|
||||
abundances,
|
||||
NUM_SPECIES,
|
||||
1.5e7, // Temperature in K
|
||||
1.5e2, // Density in g/cm^3
|
||||
3e17, // Time step in seconds
|
||||
1e-12, // Initial time step in seconds
|
||||
Y_out,
|
||||
&energy_out,
|
||||
&dEps_dT,
|
||||
&dEps_dRho, &mass_lost
|
||||
);
|
||||
|
||||
GF_CHECK_RET_CODE(ret, gf_context, "Evolution");
|
||||
|
||||
|
||||
printf("Evolved abundances:\n");
|
||||
for (size_t i = 0; i < NUM_SPECIES; i++) {
|
||||
printf("Species %s: %e\n", species_names[i], Y_out[i]);
|
||||
}
|
||||
printf("Energy output: %e\n", energy_out);
|
||||
printf("dEps/dT: %e\n", dEps_dT);
|
||||
printf("dEps/dRho: %e\n", dEps_dRho);
|
||||
printf("Mass lost: %e\n", mass_lost);
|
||||
|
||||
gf_free(gf_context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Fortran API Overview
|
||||
|
||||
GridFire makes use of the stable C API and Fortran 2003's `iso_c_bindings` to provide a Fortran interface for legacy
|
||||
code. The fortran interface is designed to be very similar to the C API and exposes the same functionality.
|
||||
|
||||
1. `GridFire%gff_init`: Initializes a GridFire context and returns a handle to it.
|
||||
2. `GridFire%register_species`: Registers species with the GridFire context.
|
||||
3. `GridFire%setup_policy`: Configures the engine using a specified policy and initial abundances.
|
||||
4. `GridFire%setup_solver`: Sets up the solver for the engine.
|
||||
5. `GridFire%evolve`: Evolves the network over a specified time step.
|
||||
6. `GridFire%get_last_error`: Retrieves the last error message from the GridFire context.
|
||||
7. `GridFire%gff_free`: Frees the GridFire context and associated resources.
|
||||
|
||||
> **Note:** You must instantiate a `GridFire` type object to access these methods.
|
||||
|
||||
> **Note:** free and init have had the `gff_` prefix (GridFire Fortran) to avoid name clashes with common Fortran functions.
|
||||
|
||||
When building GridFire a fortran module file `gridfire_mod.mod` is generated which contains all the necessary
|
||||
bindings to use GridFire from Fortran. You must also link your code against the C API library `libgridfire_extern`.
|
||||
|
||||
### Fortran Example
|
||||
|
||||
```fortran
|
||||
program main
|
||||
use iso_c_binding
|
||||
use gridfire_mod
|
||||
implicit none
|
||||
|
||||
type(GridFire) :: net
|
||||
integer(c_int) :: ierr
|
||||
integer :: i
|
||||
|
||||
! --- 1. Define Species and Initial Conditions ---
|
||||
! Note: String lengths must match or exceed the longest name.
|
||||
! We pad with spaces, which 'trim' handles inside the module.
|
||||
character(len=5), dimension(8) :: species_names = [ &
|
||||
"H-1 ", &
|
||||
"He-3 ", &
|
||||
"He-4 ", &
|
||||
"C-12 ", &
|
||||
"N-14 ", &
|
||||
"O-16 ", &
|
||||
"Ne-20", &
|
||||
"Mg-24" &
|
||||
]
|
||||
|
||||
! Initial Mass Fractions (converted to Molar Abundances Y = X/A)
|
||||
! Standard solar-ish composition
|
||||
real(c_double), dimension(8) :: Y_in = [ &
|
||||
0.702616602672027, &
|
||||
9.74791583949078e-06, &
|
||||
0.06895512307276903, &
|
||||
0.00025, &
|
||||
7.855418029399437e-05, &
|
||||
0.0006014411598306529, &
|
||||
8.103062886768109e-05, &
|
||||
2.151340851063217e-05 &
|
||||
]
|
||||
|
||||
! Output buffers
|
||||
real(c_double), dimension(8) :: Y_out
|
||||
real(c_double) :: energy_out, dedt, dedrho, dmass
|
||||
|
||||
! Thermodynamic Conditions (Solar Core-ish)
|
||||
real(c_double) :: T = 1.5e7 ! 15 Million K
|
||||
real(c_double) :: rho = 150.0e0 ! 150 g/cm^3
|
||||
real(c_double) :: dt = 3.1536e17 ! ~10 Gyr timestep
|
||||
|
||||
! --- 2. Initialize GridFire ---
|
||||
print *, "Initializing GridFire..."
|
||||
call net%gff_init()
|
||||
|
||||
! --- 3. Register Species ---
|
||||
print *, "Registering species..."
|
||||
call net%register_species(species_names)
|
||||
|
||||
! --- 4. Configure Engine & Solver ---
|
||||
print *, "Setting up Main Sequence Policy..."
|
||||
call net%setup_policy("MAIN_SEQUENCE_POLICY", Y_in)
|
||||
|
||||
print *, "Setting up CVODE Solver..."
|
||||
call net%setup_solver("CVODE")
|
||||
|
||||
! --- 5. Evolve ---
|
||||
print *, "Evolving system (dt =", dt, "s)..."
|
||||
call net%evolve(Y_in, T, rho, dt, Y_out, energy_out, dedt, dedrho, dmass, ierr)
|
||||
|
||||
if (ierr /= 0) then
|
||||
print *, "Evolution Failed with error code: ", ierr
|
||||
print *, "Error Message: ", net%get_last_error()
|
||||
call net%gff_free() ! Always cleanup
|
||||
stop
|
||||
end if
|
||||
|
||||
! --- 6. Report Results ---
|
||||
print *, ""
|
||||
print *, "--- Results ---"
|
||||
print '(A, ES12.5, A)', "Energy Generation: ", energy_out, " erg/g/s"
|
||||
print '(A, ES12.5)', "dEps/dT: ", dedt
|
||||
print '(A, ES12.5)', "Mass Change: ", dmass
|
||||
|
||||
print *, ""
|
||||
print *, "Abundances:"
|
||||
do i = 1, size(species_names)
|
||||
print '(A, " : ", ES12.5, " -> ", ES12.5)', &
|
||||
trim(species_names(i)), Y_in(i), Y_out(i)
|
||||
end do
|
||||
|
||||
! --- 7. Cleanup ---
|
||||
call net%gff_free()
|
||||
|
||||
end program main
|
||||
```
|
||||
|
||||
# Related Projects
|
||||
|
||||
@@ -910,3 +1124,5 @@ GridFire integrates with and builds upon several key 4D-STAR libraries:
|
||||
utilities.
|
||||
- [liblogging](https://github.com/4D-STAR/liblogging): Flexible logging framework.
|
||||
- [libconstants](https://github.com/4D-STAR/libconstants): Physical constants
|
||||
- [libplugin](https://github.com/4D-STAR/libplugin): Dynamically loadable plugin
|
||||
framework.
|
||||
|
||||
@@ -24,6 +24,10 @@ project('GridFire', ['c', 'cpp', 'fortran'], version: 'v0.7.0_rc1', default_opti
|
||||
add_project_arguments('-fvisibility=default', language: 'cpp')
|
||||
|
||||
message('Found CXX compiler: ' + meson.get_compiler('cpp').get_id())
|
||||
message('Found FORTRAN compiler: ' + meson.get_compiler('fortran').get_id())
|
||||
message('C++ standard set to: ' + get_option('cpp_std'))
|
||||
message('Fortran standard set to: ' + get_option('fortran_std'))
|
||||
|
||||
|
||||
if meson.get_compiler('cpp').get_id() == 'clang'
|
||||
# We disable these because of CppAD
|
||||
|
||||
258
src/extern/fortran/gridfire_mod.f90
vendored
Normal file
258
src/extern/fortran/gridfire_mod.f90
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
module gridfire_mod
|
||||
use iso_c_binding
|
||||
implicit none
|
||||
|
||||
enum, bind (C)
|
||||
enumerator :: FDSSE_NON_4DSTAR_ERROR = -102
|
||||
enumerator :: FDSSE_UNKNOWN_ERROR = -101
|
||||
|
||||
enumerator :: FDSSE_SUCCESS = 1
|
||||
enumerator :: FDSSE_UNKNOWN_SYMBOL_ERROR = 100
|
||||
|
||||
enumerator :: FDSSE_SPECIES_ERROR = 101
|
||||
enumerator :: FDSSE_INVALID_COMPOSITION_ERROR = 102
|
||||
enumerator :: FDSSE_COMPOSITION_ERROR = 103
|
||||
|
||||
enumerator :: GF_NON_GRIDFIRE_ERROR = -2
|
||||
enumerator :: GF_UNKNOWN_ERROR = -1
|
||||
enumerator :: GF_SUCCESS = 0
|
||||
|
||||
enumerator :: GF_INVALID_QSE_SOLUTION_ERROR = 5
|
||||
enumerator :: GF_FAILED_TO_PARTITION_ERROR = 6
|
||||
enumerator :: GF_NETWORK_RESIZED_ERROR = 7
|
||||
enumerator :: GF_UNABLE_TO_SET_NETWORK_REACTIONS_ERROR = 8
|
||||
enumerator :: GF_BAD_COLLECTION_ERROR = 9
|
||||
enumerator :: GF_BAD_RHS_ENIGNE_ERROR = 10
|
||||
enumerator :: GF_STALE_JACOBIAN_ERROR = 11
|
||||
enumerator :: GF_UNINITIALIZED_JACOBIAN_ERROR = 12
|
||||
enumerator :: GF_UNKNONWN_JACOBIAN_ERROR = 13
|
||||
enumerator :: GF_JACOBIAN_ERROR = 14
|
||||
enumerator :: GF_ENGINE_ERROR = 15
|
||||
|
||||
enumerator :: GF_MISSING_BASE_REACTION_ERROR = 16
|
||||
enumerator :: GF_MISSING_SEED_SPECIES_ERROR = 17
|
||||
enumerator :: GF_MISSING_KEY_REACTION_ERROR = 18
|
||||
enumerator :: GF_POLICY_ERROR = 19
|
||||
|
||||
enumerator :: GF_REACTION_PARSING_ERROR = 20
|
||||
enumerator :: GF_REACTOION_ERROR = 21
|
||||
|
||||
enumerator :: GF_SINGULAR_JACOBIAN_ERROR = 22
|
||||
enumerator :: GF_ILL_CONDITIONED_JACOBIAN_ERROR = 23
|
||||
enumerator :: GF_CVODE_SOLVER_FAILURE_ERROR = 24
|
||||
enumerator :: GF_KINSOL_SOLVER_FAILURE_ERROR = 25
|
||||
enumerator :: GF_SUNDIALS_ERROR = 26
|
||||
enumerator :: GF_SOLVER_ERROR = 27
|
||||
|
||||
enumerator :: GF_HASHING_ERROR = 28
|
||||
enumerator :: GF_UTILITY_ERROR = 29
|
||||
|
||||
enumerator :: GF_DEBUG_ERRROR = 30
|
||||
|
||||
enumerator :: GF_GRIDFIRE_ERROR = 31
|
||||
end enum
|
||||
|
||||
interface
|
||||
! void* gf_init()
|
||||
function gf_init() bind(C, name="gf_init")
|
||||
import :: c_ptr
|
||||
type(c_ptr) :: gf_init
|
||||
end function gf_init
|
||||
|
||||
! void gf_free(void* gf)
|
||||
subroutine gf_free(gf) bind(C, name="gf_free")
|
||||
import :: c_ptr
|
||||
type(c_ptr), value :: gf
|
||||
end subroutine gf_free
|
||||
|
||||
! char* gf_get_last_error_message(void* ptr);
|
||||
function gf_get_last_error_message(ptr) result(c_msg) bind(C, name="gf_get_last_error_message")
|
||||
import
|
||||
type(c_ptr), value :: ptr
|
||||
type(c_ptr) :: c_msg
|
||||
end function
|
||||
|
||||
! int gf_register_species(void* ptr, const int num_species, const char** species_names);
|
||||
function gf_register_species(ptr, num_species, species_names) result(ierr) bind(C, name="gf_register_species")
|
||||
import
|
||||
type(c_ptr), value :: ptr
|
||||
integer(c_int), value :: num_species
|
||||
type(c_ptr), dimension(*), intent(in) :: species_names ! Array of C pointers
|
||||
integer(c_int) :: ierr
|
||||
end function
|
||||
|
||||
! int gf_construct_engine_from_policy(void* ptr, const char* policy_name, const double *abundances, size_t num_species);
|
||||
function gf_construct_engine_from_policy(ptr, policy_name, abundances, num_species) result(ierr) &
|
||||
bind(C, name="gf_construct_engine_from_policy")
|
||||
import
|
||||
type(c_ptr), value :: ptr
|
||||
character(kind=c_char), dimension(*), intent(in) :: policy_name
|
||||
real(c_double), dimension(*), intent(in) :: abundances
|
||||
integer(c_size_t), value :: num_species
|
||||
integer(c_int) :: ierr
|
||||
end function
|
||||
|
||||
! int gf_construct_solver_from_engine(void* ptr, const char* solver_name);
|
||||
function gf_construct_solver_from_engine(ptr, solver_name) result(ierr) &
|
||||
bind(C, name="gf_construct_solver_from_engine")
|
||||
import
|
||||
type(c_ptr), value :: ptr
|
||||
character(kind=c_char), dimension(*), intent(in) :: solver_name
|
||||
integer(c_int) :: ierr
|
||||
end function
|
||||
|
||||
! int gf_evolve(...)
|
||||
function gf_evolve(ptr, Y_in, num_species, T, rho, dt, Y_out, energy_out, dEps_dT, dEps_dRho, mass_lost) result(ierr) &
|
||||
bind(C, name="gf_evolve")
|
||||
import
|
||||
type(c_ptr), value :: ptr
|
||||
real(c_double), dimension(*), intent(in) :: Y_in
|
||||
integer(c_size_t), value :: num_species
|
||||
real(c_double), value :: T, rho, dt
|
||||
real(c_double), dimension(*), intent(out) :: Y_out
|
||||
real(c_double), intent(out) :: energy_out, dEps_dT, dEps_dRho, mass_lost
|
||||
integer(c_int) :: ierr
|
||||
end function
|
||||
end interface
|
||||
|
||||
type :: GridFire
|
||||
type(c_ptr) :: ctx = c_null_ptr
|
||||
integer(c_size_t) :: num_species = 0
|
||||
contains
|
||||
procedure :: gff_init
|
||||
procedure :: gff_free
|
||||
procedure :: register_species
|
||||
procedure :: setup_policy
|
||||
procedure :: setup_solver
|
||||
procedure :: evolve
|
||||
procedure :: get_last_error
|
||||
end type GridFire
|
||||
|
||||
contains
|
||||
subroutine gff_init(self)
|
||||
class(GridFire), intent(out) :: self
|
||||
|
||||
self%ctx = gf_init()
|
||||
end subroutine gff_init
|
||||
|
||||
subroutine gff_free(self)
|
||||
class(GridFire), intent(inout) :: self
|
||||
|
||||
if (c_associated(self%ctx)) then
|
||||
call gf_free(self%ctx)
|
||||
self%ctx = c_null_ptr
|
||||
end if
|
||||
end subroutine gff_free
|
||||
|
||||
function get_last_error(self) result(msg)
|
||||
class(GridFire), intent(in) :: self
|
||||
character(len=:), allocatable :: msg
|
||||
type(c_ptr) :: c_msg_ptr
|
||||
character(kind=c_char), pointer :: char_ptr(:)
|
||||
integer :: i, len_str
|
||||
|
||||
c_msg_ptr = gf_get_last_error_message(self%ctx)
|
||||
if (.not. c_associated(c_msg_ptr)) then
|
||||
msg = "GridFire: Unknown Error (Null Pointer returned)"
|
||||
return
|
||||
end if
|
||||
|
||||
call c_f_pointer(c_msg_ptr, char_ptr, [1024])
|
||||
len_str = 0
|
||||
do i = 1, 1024
|
||||
if (char_ptr(i) == c_null_char) exit
|
||||
len_str = len_str + 1
|
||||
end do
|
||||
|
||||
msg = repeat(' ', len_str+10)
|
||||
msg(1:10) = "GridFire: "
|
||||
do i = 1, len_str
|
||||
msg(i+10:i+10) = char_ptr(i)
|
||||
end do
|
||||
end function get_last_error
|
||||
|
||||
subroutine register_species(self, species_list)
|
||||
class(GridFire), intent(inout) :: self
|
||||
character(len=*), dimension(:), intent(in) :: species_list
|
||||
|
||||
type(c_ptr), allocatable, dimension(:) :: c_ptrs
|
||||
character(kind=c_char, len=:), allocatable, target :: temp_strs(:)
|
||||
integer :: i, n, ierr
|
||||
|
||||
print *, "Registering ", size(species_list), " species."
|
||||
n = size(species_list)
|
||||
self%num_species = int(n, c_size_t)
|
||||
|
||||
allocate(c_ptrs(n))
|
||||
allocate(character(len=len(species_list(1))+1) :: temp_strs(n)) ! +1 for null terminator
|
||||
|
||||
do i = 1, n
|
||||
temp_strs(i) = trim(species_list(i)) // c_null_char
|
||||
c_ptrs(i) = c_loc(temp_strs(i))
|
||||
end do
|
||||
|
||||
print *, "Calling gf_register_species..."
|
||||
ierr = gf_register_species(self%ctx, int(n, c_int), c_ptrs)
|
||||
print *, "gf_register_species returned with code: ", ierr
|
||||
|
||||
if (ierr /= GF_SUCCESS .AND. ierr /= FDSSE_SUCCESS) then
|
||||
print *, "GridFire: ", self%get_last_error()
|
||||
error stop
|
||||
end if
|
||||
end subroutine register_species
|
||||
|
||||
subroutine setup_policy(self, policy_name, abundances)
|
||||
class(GridFire), intent(in) :: self
|
||||
character(len=*), intent(in) :: policy_name
|
||||
real(c_double), dimension(:), intent(in) :: abundances
|
||||
integer(c_int) :: ierr
|
||||
|
||||
if (size(abundances) /= self%num_species) then
|
||||
print *, "GridFire Error: Abundance array size mismatch."
|
||||
error stop
|
||||
end if
|
||||
|
||||
ierr = gf_construct_engine_from_policy(self%ctx, &
|
||||
trim(policy_name) // c_null_char, &
|
||||
abundances, &
|
||||
self%num_species)
|
||||
|
||||
if (ierr /= GF_SUCCESS .AND. ierr /= FDSSE_SUCCESS) then
|
||||
print *, "GridFire Policy Error: ", self%get_last_error()
|
||||
error stop
|
||||
end if
|
||||
end subroutine setup_policy
|
||||
|
||||
subroutine setup_solver(self, solver_name)
|
||||
class(GridFire), intent(in) :: self
|
||||
character(len=*), intent(in) :: solver_name
|
||||
integer(c_int) :: ierr
|
||||
|
||||
ierr = gf_construct_solver_from_engine(self%ctx, trim(solver_name) // c_null_char)
|
||||
if (ierr /= GF_SUCCESS .AND. ierr /= FDSSE_SUCCESS) then
|
||||
print *, "GridFire Solver Error: ", self%get_last_error()
|
||||
error stop
|
||||
end if
|
||||
end subroutine setup_solver
|
||||
|
||||
subroutine evolve(self, Y_in, T, rho, dt, Y_out, energy, dedt, dedrho, mass_lost, ierr)
|
||||
class(GridFire), intent(in) :: self
|
||||
real(c_double), dimension(:), intent(in) :: Y_in
|
||||
real(c_double), value :: T, rho, dt
|
||||
real(c_double), dimension(:), intent(out) :: Y_out
|
||||
real(c_double), intent(out) :: energy, dedt, dedrho, mass_lost
|
||||
integer, intent(out) :: ierr
|
||||
integer(c_int) :: c_ierr
|
||||
|
||||
c_ierr = gf_evolve(self%ctx, &
|
||||
Y_in, self%num_species, &
|
||||
T, rho, dt, &
|
||||
Y_out, &
|
||||
energy, dedt, dedrho, mass_lost)
|
||||
|
||||
ierr = int(c_ierr)
|
||||
if (ierr /= GF_SUCCESS .AND. ierr /= FDSSE_SUCCESS) then
|
||||
print *, "GridFire Evolve Error: ", self%get_last_error()
|
||||
end if
|
||||
end subroutine evolve
|
||||
end module gridfire_mod
|
||||
11
src/extern/fortran/meson.build
vendored
Normal file
11
src/extern/fortran/meson.build
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
gridfire_fortran_lib = library('gridfire_fortran',
|
||||
'gridfire_mod.f90',
|
||||
link_with: libgridfire_extern,
|
||||
install: true,
|
||||
install_dir: get_option('libdir')
|
||||
)
|
||||
|
||||
gridfire_fortran_dep = declare_dependency(
|
||||
link_with: [gridfire_fortran_lib, libgridfire_extern],
|
||||
include_directories: include_directories('.')
|
||||
)
|
||||
@@ -62,7 +62,7 @@ extern "C" {
|
||||
|
||||
void* gf_init();
|
||||
|
||||
int gf_free(void* ctx);
|
||||
void gf_free(void* ctx);
|
||||
|
||||
int gf_register_species(void* ptr, const int num_species, const char** species_names);
|
||||
|
||||
|
||||
3
src/extern/lib/gridfire_extern.cpp
vendored
3
src/extern/lib/gridfire_extern.cpp
vendored
@@ -8,9 +8,8 @@ extern "C" {
|
||||
return new GridFireContext();
|
||||
}
|
||||
|
||||
int gf_free(void* ctx) {
|
||||
void gf_free(void* ctx) {
|
||||
delete static_cast<GridFireContext*>(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gf_register_species(void* ptr, const int num_species, const char** species_names) {
|
||||
|
||||
2
src/extern/meson.build
vendored
2
src/extern/meson.build
vendored
@@ -22,3 +22,5 @@ gridfire_extern_dep = declare_dependency(
|
||||
)
|
||||
|
||||
install_subdir('include/gridfire', install_dir: get_option('includedir'))
|
||||
|
||||
subdir('fortran')
|
||||
|
||||
11
tests/extern/C/gridfire_evolve.c
vendored
11
tests/extern/C/gridfire_evolve.c
vendored
@@ -22,7 +22,16 @@ int main() {
|
||||
species_names[5] = "O-16";
|
||||
species_names[6] = "Ne-20";
|
||||
species_names[7] = "Mg-24";
|
||||
const double abundances[NUM_SPECIES] = {0.702616602672027, 9.74791583949078e-06, 0.06895512307276903, 0.00025, 7.855418029399437e-05, 0.0006014411598306529, 8.103062886768109e-05, 2.151340851063217e-05};
|
||||
const double abundances[NUM_SPECIES] = {
|
||||
0.702616602672027,
|
||||
9.74791583949078e-06,
|
||||
0.06895512307276903,
|
||||
0.00025,
|
||||
7.855418029399437e-05,
|
||||
0.0006014411598306529,
|
||||
8.103062886768109e-05,
|
||||
2.151340851063217e-05
|
||||
};
|
||||
|
||||
int ret = gf_register_species(ctx, NUM_SPECIES, species_names);
|
||||
CHECK_RET_CODE(ret, ctx, "SPECIES");
|
||||
|
||||
89
tests/extern/fortran/gridfire_evolve.f90
vendored
Normal file
89
tests/extern/fortran/gridfire_evolve.f90
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
program main
|
||||
use iso_c_binding
|
||||
use gridfire_mod
|
||||
implicit none
|
||||
|
||||
type(GridFire) :: net
|
||||
integer(c_int) :: ierr
|
||||
integer :: i
|
||||
|
||||
! --- 1. Define Species and Initial Conditions ---
|
||||
! Note: String lengths must match or exceed the longest name.
|
||||
! We pad with spaces, which 'trim' handles inside the module.
|
||||
character(len=5), dimension(8) :: species_names = [ &
|
||||
"H-1 ", &
|
||||
"He-3 ", &
|
||||
"He-4 ", &
|
||||
"C-12 ", &
|
||||
"N-14 ", &
|
||||
"O-16 ", &
|
||||
"Ne-20", &
|
||||
"Mg-24" &
|
||||
]
|
||||
|
||||
! Initial Mass Fractions (converted to Molar Abundances Y = X/A)
|
||||
! Standard solar-ish composition
|
||||
real(c_double), dimension(8) :: Y_in = [ &
|
||||
0.702616602672027, &
|
||||
9.74791583949078e-06, &
|
||||
0.06895512307276903, &
|
||||
0.00025, &
|
||||
7.855418029399437e-05, &
|
||||
0.0006014411598306529, &
|
||||
8.103062886768109e-05, &
|
||||
2.151340851063217e-05 &
|
||||
]
|
||||
|
||||
! Output buffers
|
||||
real(c_double), dimension(8) :: Y_out
|
||||
real(c_double) :: energy_out, dedt, dedrho, dmass
|
||||
|
||||
! Thermodynamic Conditions (Solar Core-ish)
|
||||
real(c_double) :: T = 1.5e7 ! 15 Million K
|
||||
real(c_double) :: rho = 150.0e0 ! 150 g/cm^3
|
||||
real(c_double) :: dt = 3.0e17 ! 1 second timestep
|
||||
|
||||
! --- 2. Initialize GridFire ---
|
||||
print *, "Initializing GridFire..."
|
||||
call net%gff_init()
|
||||
|
||||
! --- 3. Register Species ---
|
||||
print *, "Registering species..."
|
||||
call net%register_species(species_names)
|
||||
|
||||
! --- 4. Configure Engine & Solver ---
|
||||
print *, "Setting up Main Sequence Policy..."
|
||||
call net%setup_policy("MAIN_SEQUENCE_POLICY", Y_in)
|
||||
|
||||
print *, "Setting up CVODE Solver..."
|
||||
call net%setup_solver("CVODE")
|
||||
|
||||
! --- 5. Evolve ---
|
||||
print *, "Evolving system (dt =", dt, "s)..."
|
||||
call net%evolve(Y_in, T, rho, dt, Y_out, energy_out, dedt, dedrho, dmass, ierr)
|
||||
|
||||
if (ierr /= 0) then
|
||||
print *, "Evolution Failed with error code: ", ierr
|
||||
print *, "Error Message: ", net%get_last_error()
|
||||
call net%gff_free() ! Always cleanup
|
||||
stop
|
||||
end if
|
||||
|
||||
! --- 6. Report Results ---
|
||||
print *, ""
|
||||
print *, "--- Results ---"
|
||||
print '(A, ES12.5, A)', "Energy Generation: ", energy_out, " erg/g/s"
|
||||
print '(A, ES12.5)', "dEps/dT: ", dedt
|
||||
print '(A, ES12.5)', "Mass Change: ", dmass
|
||||
|
||||
print *, ""
|
||||
print *, "Abundances:"
|
||||
do i = 1, size(species_names)
|
||||
print '(A, " : ", ES12.5, " -> ", ES12.5)', &
|
||||
trim(species_names(i)), Y_in(i), Y_out(i)
|
||||
end do
|
||||
|
||||
! --- 7. Cleanup ---
|
||||
call net%gff_free()
|
||||
|
||||
end program main
|
||||
5
tests/extern/fortran/meson.build
vendored
Normal file
5
tests/extern/fortran/meson.build
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
executable('test_fortran_extern', 'gridfire_evolve.f90',
|
||||
install: false,
|
||||
fortran_args: ['-Wall', '-Wextra'],
|
||||
dependencies: [gridfire_fortran_dep]
|
||||
)
|
||||
1
tests/extern/meson.build
vendored
1
tests/extern/meson.build
vendored
@@ -1 +1,2 @@
|
||||
subdir('C')
|
||||
subdir('fortran')
|
||||
Reference in New Issue
Block a user