Author: Stefan Scheiblhofer, Stephan Jäger (LKR Leichtmetallkompetenzzentrum Ranshofen GmbH)
Contact: matthias.hartmann@ait.ac.at
This is the readme to develop a preCICE adapter for LS-DYNA. The readme is based on preCICE V1.3.0 and LS-DYNA R9.3.0 in the double SMP Version.
The typical field of application for LS-DYNA is crash simulation and rather oriented towards the explicit, mechanical solver. However, the finite element method based solver is often exhausted to its limits in the case of the thermal solver. This is especially true with casting process simulations. Thereby, the fluid flow in the melt of the casting component has a significant impact on the thermal field. This also depends on the components dimensions and process parameters. However, this fluid flow cannot be considered with the FEM based LS-DYNA solver. Consequently, the main idea behind the adapter for LS-DYNA is to import and use the temperature field of an external solver. This should help to improve the accuracy of the temperature field in the melt section, while still being able to calculate the resulting strain and stress fields.
preCICE is written in C++. However, preCICE also provides APIs or bindings for non C++ codes. A list and current overview can be seen here. In order to use preCICE within LS-DYNA there are some prerequisites, which have to be met before implementing preCICE in LS-DYNA. After successful installation of preCICE it is necessary to link the preCICE library to LS-DYNA. There are basically two ways to link preCICE to LS-DYNA: the first is linking the static library; the second is linking the shared object library of preCICE to LS-DYNA.
One of the simplest ways is to use the static library. The first step is to copy the static library of preCICE libprecice.a to the LS-DYNA usermat package directory. Second step is the implementation in LS-DYNA to change the Makefile for the LS-DYNA executable. This starts by adding the dependencies of preCICE to the compiler flag LF:
LF= -i-static -openmp -lrt -lstdc++ -lpthread -lboost_system -lboost_log -lboost_log_setup -lboost_thread -lboost_filesystem -lboost_program_options -lpython2.7 -lxml2The next necessary adaption of the Makefile is adding libprecice.a at
the end of the the compiler flag LIBS.
The recommended approach is to build preCICE as a shared object library.
Following the installation instructions on how to build preCICE, the default
install location for the shared object library is /usr/local.
In contrast to linking the static library, the
necessary changes to the Makefile are reduced to the compiler flag LF.
For linking the shared object library of preCICE change LF to the following:
LF= -i-static -openmp -lrt -lstdc++ -lpthread -lboost_system -lboost_log -lboost_log_setup -lboost_thread -lboost_filesystem -lboost_program_options -lpython2.7 -lxml2 -I/usr/local/include -L/usr/local/lib -lpreciceThe detailed description on how to edit the Make-file can be in the precice wiki.
LS-DYNA enables the user to define user defined loads via the keywords
*USER_LOADING and *USER_LOADING_SET. These keywords automatically
invoke the call of the subroutines loadud and loadsetud respectively.
The keyword *USER_LOADING defines parameters, which can then
be used by the keyword *USER_LOADING_SET and its subroutine loadsetud.
The loadings defined with the keyword *USER_LOADING_SET
can be nodal forces, body forces, temperature distribution and pressure
on segments or beams. For the complete description of the keyword
please refer to the LS-DYNA keyword manual Volume 1.
Within the called subroutine loadsetud, which can be found
in the dyn21.f-file of the usermat-version of LS-DYNA,
it is possible to define the user-defined loads. The original
dyn21.f-file returns some examples on how to do that.
The subroutine loadsetud also enables the implementation of analytical equations and/or import of externally solved data. With that in mind and the capabilities of preCICE, it should then be possible to couple an external solver to LS-DYNA.
The key issue, while designing an adapter for a proprietary code as LS-DYNA, is finding the correct placement of the preCICE functions within the available user interfaces. The placement of the preCICE functions has to be tested, to call the functions appropriatly relative to the placement of the subroutines within the source code of LS-DYNA.
The current development status of the preCICE adapter within LS-DYNA uses two subroutines of LS-DYNA, which are uctrl1 and loadsetud. The subroutine loadsetud is called before calculating the time step while uctrl1 is called after each (mechanical) time step. As the function calls are separated between two subroutines the variable definitions for all (preCICE) relevant variables are located in a general module. In this way, both subroutines can access the variables with the same content.
For the goal of coupling temperature fields into LS-DYNA through the
keyword *USER_LOADING_SET and preCICE, loadsetud is called before
uctrl1. Consequently, the initialisation of preCICE
is realised within loadsetud. This is done once at the first call of the subroutine.
The subroutine loadsetud is also called before actually calculating the current timestep. It is mandatory to read the vector or scalar in here. In order to read the data for the current timestep it is necessary to call the precice function advance. This function call transfers the data between the solvers.
While loadsetud intialises preCICE and reads data, uctrl1 writes vector or scalar data after the present timestep has been calculated. Finally, preCICE is finalised in uctrl1 which also includes the termination of LS-DYNA.
The work flow of the preCICE adapter within LS-DYNA can be summmarised with the following two flow chart diagrams. The blue boxes show the functions and routines called in the subroutine loadsetud, while the green boxes represent the subroutine uctrl1. White boxes are functions and processes which are called by LS-DYNA. These cannot be interfered by the user.
The following code lines show the schematic overview of the the subroutines and the general module for the definition of the preCICE relevant variables considering the blocks introduced by the flow chart diagrams.
module common_data
character*50 config
character*50 participantName
character*50 meshName
c parameters for implicit coupling
character*50 writeInitialData
character*50 readItCheckp
character*50 writeItCheckp
integer rank
integer commSize
integer ongoing
integer dimensions
integer meshID
integer tempID
integer dummyID
integer,save :: bool = 0
real dtLimit
integer, dimension(:), allocatable :: vertexIDs
integer, dimension(:), allocatable :: nodeIDs
double precision, dimension(:), allocatable :: coords
double precision, dimension(:), allocatable :: temps
double precision, dimension(:), allocatable :: dummyItCheckp
double precision, dimension(:), allocatable :: dummy
end module common_data
subroutine loadsetud(time,lft,llt,crv,iduls,parm,nod,nnm1)
use common_data
if (time.eq.0) then
c _____________________________________________________________________
c| |
c| Initialise variables |
c|_____________________________________________________________________|
config='precice-config.xml'
participantName='solverMech'
meshName='meshMech'
rank=0
commSize=1
c number of nodes in current set should be given in parameter 1
c which is equal to the first variable in keyword `*USER_LOADING`
numNodesInSet=parm(1)
writeInitialData= &
&' '
reatItCheckp= &
&' '
writeItCheckp= &
&' '
c _____________________________________________________________________
c| |
c| Create preCICE |
c|_____________________________________________________________________|
call precicef_create(participantName,config,rank,commSize)
c _____________________________________________________________________
c| |
c| Initialise constants for implicit coupling |
c|_____________________________________________________________________|
call precicef_action_write_initial_data(writeInitialData)
call precicef_action_read_iter_checkp(readItCheckp)
call precicef_action_write_iter_checkp(writeItCheckp)
c _____________________________________________________________________
c| |
c| Get dimensions of preCICE config file |
c|_____________________________________________________________________|
call precicef_get_dims(dimensions)
c
c After the variable dimensions was set, all arrays can be allocated
c
allocate(vertexIDs(numNodesInSet))
allocate(nodeIDs(numNodesInSet))
allocate(temps(numNodesInSet))
allocate(dummy(numNodesInSet))
allocate(dummyItCheckp(numNodesInSet))
allocate(coords(numNodesInSet*dimensions))
c _____________________________________________________________________
c| |
c| Get mesh ID from preCICE |
c|_____________________________________________________________________|
call precicef_get_mesh_id(meshName, meshID)
c _____________________________________________________________________
c| |
c| Get data ID(s) from preCICE |
c|_____________________________________________________________________|
call precicef_get_data_id('Temperature', meshID, tempID)
call precicef_get_data_id('Dummy', meshID, dummyID)
c _____________________________________________________________________
c| |
c| Set vertices for preCICE |
c|_____________________________________________________________________|
call precicef_set_vertices(meshID, numNodesInSet, coords,vertexIDs)
c _____________________________________________________________________
c| |
c| Initialise preCICE |
c|_____________________________________________________________________|
call precicef_initialize(dtLimit)
c _____________________________________________________________________
c| |
c| Write initial data |
c|_____________________________________________________________________|
call precicef_is_action_required(writeInitialData, bool)
if (bool.eq.1) then
call precicef_write_bsdata(dummyID, numNodesInSet,vertexIDs, dummy)
call precicef_fulfilled_action(writeInitialData)
endif
c _____________________________________________________________________
c| |
c| Get initial data from preCICE |
c|_____________________________________________________________________|
call precicef_initialize_data()
c _____________________________________________________________________
c| |
c| Read initial data |
c|_____________________________________________________________________|
call precicef_read_bsdata(tempID, numNodesInSet, vertexIDs,temps)
c save data for current timestep
call precicef_is_action_required(writeItCheckp, bool)
if (bool.eq.1) then
dummyItCheckp=dummy
call precicef_fulfilled_action(writeItCheckp)
endif
endif
if (time.gt.0.AND.time.le.endtim) then
c _____________________________________________________________________
c| |
c| Advance to the next time step |
c|_____________________________________________________________________|
call precicef_advance(dtLimit)
c _____________________________________________________________________
c| |
c| Read data |
c|_____________________________________________________________________|
call precicef_read_bsdata(tempID,numNodesInSet,vertexIDs,temps)
endif
c set temperatures for current block
do i=lft,llt
udl(i)=temps(i)
enddo
return
end
subroutine uctrl1 (numnp,ndof,time,dt1,dt2,prtc,pltc,frci,prto,
. plto,frco,vt,vr,at,ar,ut,ur,xmst,xmsr,irbody,rbdyn,usrhv,
. messag,totalm,cycle,idrint,mtype,mxrb,nrba,rbcor,x,rbv,nrbn,
. nrb,xrb,yrb,zrb,axrb,ayrb,azrb,dtx,nmmat,rba,fvalnew,fvalold,
. fvalmid,fvalnxt)
use common_data
c _____________________________________________________________________
c| |
c| Write data |
c|_____________________________________________________________________|
call precicef_write_bsdata(dummyID, numNodesInSet,vertexIDs, dummy)
c _____________________________________________________________________
c| |
c| Check if ongoing |
c|_____________________________________________________________________|
call precicef_is_coupling_ongoing(ongoing)
if (ongoing.eq.0) then
c _____________________________________________________________________
c| |
c| Finalise preCICE |
c|_____________________________________________________________________|
call precicef_finalize()
c _____________________________________________________________________
c| |
c| Deallocate memory of arrays |
c|_____________________________________________________________________|
if (allocated (vertexIDs)) deallocate(vertexIDs)
if (allocated (nodeIDs)) deallocate(nodeIDs)
if (allocated (coords)) deallocate(coords)
if (allocated (temps)) deallocate(temps)
if (allocated (dummy)) deallocate(dummy)
if (allocated (dummyItCheckp)) deallocate(dummyItCheckp)
c _____________________________________________________________________
c| |
c| Terminate LS-DYNA |
c|_____________________________________________________________________|
call adios(1)
endif
return
endThe developments of the adapter inside LS-DYNA have been tested and
promoted against the available OpenFOAM adapater. The results of the
developed test case have been published in the ECCOMAS Coupled
Problems 2019 Proceedings.
The presented preCICE adapter for LS-DYNA is based on an
implicit, two-way-coupling. The
two-way-coupling was the more beneficial way to receive correct values for the
data initialisation at time = 0 and at the end of the simulation at
time = endtime. As a result, the present adapter writes dummy values as a second variable.
The participant's name, configuration file name, mesh name, as well as
data names are hard-coded within the subroutines. A more general approach
would be nice, but have not been the goal of these developments
for the LS-DYNA adapter.