├── .gitlab-ci.yml ├── LICENSE ├── README.md ├── abaci.toml ├── jobs └── single-element.inp ├── src ├── Abaqus_Definitions.f90 ├── Elastic_mod.f90 └── umat.f └── test └── test_elastic.f90 /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: bci-abaqus-runner 2 | stages: 3 | - test 4 | 5 | test: 6 | stage: test 7 | tags: 8 | - linux 9 | script: 10 | abaci run -vs 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 University of Bristol 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Sample User Subroutine for Using Modern Fortran with Abaqus 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 4 | 5 | __A simple Abaqus user subroutine demonstrating the use of modern Fortran features:__ 6 | 7 | - [__Free form__](https://bristolcompositesinstitute.github.io/RSE-Guide/abaqus-user-subroutines/free-form-fortran-abaqus.html) formatting using compiler directives for improved readability and ease-of-use. 8 | 9 | - [__Fortran modules__](https://bristolcompositesinstitute.github.io/RSE-Guide/abaqus-user-subroutines/using-fortran-modules.html) for code organisation, modularisation and modern procedure interfaces 10 | 11 | - [__Explicit typing__](https://bristolcompositesinstitute.github.io/RSE-Guide/abaqus-user-subroutines/explicit-typing-abaqus.html) to enforce type-safety and avoid implicit typing errors 12 | 13 | __This sample code follows the [BCI Fortran Coding Guidelines](https://bristolcompositesinstitute.github.io/RSE-Guide/abaqus-user-subroutines/fortran-style-guidelines.html).__ 14 | 15 | 16 | ## Contents 17 | 18 | - `src/umat.f` - *top-level user subroutine to pass to abaqus* 19 | - `src/Elastic_mod.f90` - *Module containing the main functionality of the user subroutine to reproduce linear elastic behaviour* 20 | - `src/Abaqus_Definitions.f90` - *Module for determining the real precision used by Abaqus (Important for Abaqus/Explicit) and storing Abaqus constants* 21 | - `jobs/single-element.inp` *a single-element job file to test the umat* 22 | - `test/test_elastic.f90` *a unit test suite (run with abaci)* 23 | - `abaci.toml` - *configuration file for use with the Abaci test runner tool* 24 | 25 | 26 | __Abaqus/Explicit:__ the example in this repository is a `umat` for use with Abaqus/Standard, however the same techniques can be used with Abaqus/Explicit also. You will need to comment line 21 and uncomment line 22 in [`src/Abaqus_Definitions.f90`](src/Abaqus_Definitions.f90) to use it with a `vumat` in Abaqus/Explicit. 27 | 28 | 29 | ## Running the Example 30 | 31 | You will need Abaqus and the Intel Fortran compiler installed on your system to run this example. 32 | 33 | 1. Open a command window and navigate to the job subfolder within this repository 34 | 35 | 2. Run the following command: 36 | 37 | ```shell 38 | abaqus job=single-element user=../src/umat.f interactive 39 | ``` 40 | 41 | If you have the [Abaci](https://github.com/BristolCompositesInstitute/abaci) test runner installed, 42 | then simply navigate to the root of this repository and run the following command: 43 | 44 | ```shell 45 | abaci run 46 | ``` 47 | 48 | To run the Fortran test suite, run: 49 | 50 | ```shell 51 | abaci test 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /abaci.toml: -------------------------------------------------------------------------------- 1 | # Abaci config file 2 | 3 | # Name of output directory 4 | output = "scratch" 5 | 6 | # Path to user subroutine file 7 | # (relative to this config file) 8 | user-sub-file = "src/umat.f" 9 | 10 | 11 | # ------------ Compile settings ------------ 12 | [compile] 13 | include = "src/*.f90" # extra source files to include 14 | 15 | 16 | # ------------ Example job ------------ 17 | [[job]] 18 | name = "test-job" 19 | job-file = "jobs/single-element.inp" 20 | tags = ['default','test'] 21 | 22 | check.reference = 'jobs/test-job-results.pkl' 23 | check.steps = ['Step-1'] 24 | check.fields = ['AC YIELD', 'E', 'PE', 'PEEQ', 'PEMAG', 'RF', 'S', 'U'] -------------------------------------------------------------------------------- /jobs/single-element.inp: -------------------------------------------------------------------------------- 1 | ** 2 | ** Simple input file demonstrating user material in ABAQUS 3 | ** 4 | ** This input file was created with ABAQUS/CAE, and then edited 5 | ** to insert the additional commands associated with user material. 6 | ** 7 | ** You can run the code with 8 | ** abaqus job=user_element, user=Usermat.for 9 | ** 10 | *Heading 11 | ** Job name: Job-1 Model name: Model-1 12 | *Preprint, echo=NO, model=NO, history=NO, contact=NO 13 | ** 14 | ** PARTS 15 | ** 16 | *Part, name=Part-1 17 | *End Part 18 | ** 19 | ** 20 | ** ASSEMBLY 21 | ** 22 | *Assembly, name=Assembly 23 | ** 24 | *Instance, name=Part-1-1, part=Part-1 25 | *Node 26 | 1, 0., 0. 27 | 2, 2.5, 0. 28 | 3, 0., 2.5 29 | 4, 2.5, 2.5 30 | ** 31 | ** NOTE - if you use reduced integration elements 32 | ** with a UMAT, you have to define the hourglass 33 | ** stiffness for the element 34 | ** 35 | *Element, type=CPE4R 36 | 1, 1, 2, 4, 3 37 | *Nset, nset=_PickedSet2, internal, generate 38 | 1, 4, 1 39 | *Elset, elset=_PickedSet2, internal 40 | 1, 41 | ** Region: (Section-1:Picked) 42 | *Elset, elset=_PickedSet2, internal 43 | 1, 44 | ** Section: Section-1 45 | ** THE LINE BELOW WAS EDITED TO ASSIGN THE USER MATERIAL 46 | *Solid Section, elset=_PickedSet2, material=umat_elastic 47 | 1., 48 | *hourglass stiffness 49 | 0.1 50 | *End Instance 51 | ** 52 | *Nset, nset=_PickedSet4, internal, instance=Part-1-1 53 | 1, 2 54 | *Elset, elset=_PickedSet4, internal, instance=Part-1-1 55 | 1, 56 | *Nset, nset=_PickedSet5, internal, instance=Part-1-1 57 | 1, 3 58 | *Elset, elset=_PickedSet5, internal, instance=Part-1-1 59 | 1, 60 | *Nset, nset=_PickedSet6, internal, instance=Part-1-1 61 | 3, 4 62 | *Elset, elset=_PickedSet6, internal, instance=Part-1-1 63 | 1, 64 | *End Assembly 65 | ** 66 | ** MATERIALS 67 | ** 68 | ** THE LINES BELOW WERE EDITED TO DEFINE THE USER MATERIAL 69 | *Material, name=umat_elastic 70 | *user material, constants=2, type=mechanical 71 | 1000.0, 0.3 72 | ** This defines the number of state variables (none in this case) 73 | *DEPVAR 74 | 1 75 | ** ---------------------------------------------------------------- 76 | ** 77 | ** STEP: Step-1 78 | ** 79 | *Step, name=Step-1 80 | *Static 81 | 0.1, 1., 1e-05, 1. 82 | ** 83 | ** BOUNDARY CONDITIONS 84 | ** 85 | ** Name: BC-1 Type: Displacement/Rotation 86 | *Boundary 87 | _PickedSet4, 2, 2 88 | ** Name: BC-2 Type: Displacement/Rotation 89 | *Boundary 90 | _PickedSet5, 1, 1 91 | ** Name: BC-3 Type: Displacement/Rotation 92 | *Boundary 93 | _PickedSet6, 2, 2, 0.1 94 | ** 95 | ** OUTPUT REQUESTS 96 | ** 97 | *Restart, write, frequency=0 98 | ** 99 | ** FIELD OUTPUT: F-Output-1 100 | ** 101 | *Output, field, variable=PRESELECT 102 | ** 103 | ** HISTORY OUTPUT: H-Output-1 104 | ** 105 | *Output, history, variable=PRESELECT 106 | *End Step 107 | -------------------------------------------------------------------------------- /src/Abaqus_Definitions.f90: -------------------------------------------------------------------------------- 1 | !DIR$ FREEFORM 2 | module Abaqus_Definitions 3 | ! This module exports various Abaqus Fortran definitions including 4 | ! a kind parameter "abaqus_real_kind" that defines the real kind (precision) 5 | ! currently in use by Abaqus. This allows explict typing in Fortran user subroutines. 6 | ! 7 | ! USAGE: 8 | ! 1) Include this file at the beginning of your top-level user subroutine file: 9 | ! include 'Abaqus_Definitions.f' 10 | ! 11 | ! 2) Import the module in your modules/subroutines with the following syntax: 12 | ! use Abaqus_Definitions, only: wp=>abaqus_real_kind 13 | ! 14 | ! 3) Define real parameters from Abaqus with the following syntax: 15 | ! real(wp) :: hsvNew(:,:) 16 | ! 17 | ! Laurence Kedward March 2022 18 | 19 | !DIR$ NOFREEFORM 20 | !DIR$ FIXEDFORMLINESIZE:132 21 | include "aba_param.inc" ! Abaqus/Standard 22 | ! include "vaba_param.inc" ! Abaqus/Explicit 23 | !DIR$ FREEFORM 24 | 25 | private 26 | 27 | ! Detect and export the Abaqus real kind precision 28 | parameter(a = 0) 29 | integer, parameter, public :: abaqus_real_kind = kind(a) 30 | 31 | ! vexternaldb i_Array contents enumerator 32 | integer, parameter, public :: i_int_nTotalNodes = 1, & 33 | i_int_nTotalElements = 2, & 34 | i_int_kStep = 3, & 35 | i_int_kInc = 4, & 36 | i_int_iStatus = 5 37 | 38 | 39 | ! vexternaldb lOp enumerator 40 | integer, parameter, public :: j_int_StartAnalysis = 0, & 41 | j_int_StartStep = 1, & 42 | j_int_SetupIncrement = 2, & 43 | j_int_StartIncrement = 3, & 44 | j_int_EndIncrement = 4, & 45 | j_int_EndStep = 5, & 46 | j_int_EndAnalysis = 6 47 | 48 | ! vexternaldb i_Array(i_int_iStatus) enumerator 49 | integer, parameter, public :: j_int_Continue = 0, & 50 | j_int_TerminateStep = 1, & 51 | j_int_TerminateAnalysis = 2 52 | 53 | ! vexternaldb r_Array contents enumerator 54 | integer, parameter, public :: i_flt_TotalTime = 1, & 55 | i_flt_StepTime = 2, & 56 | i_flt_dTime = 3 57 | 58 | end module Abaqus_Definitions 59 | !DIR$ NOFREEFORM 60 | !DIR$ FIXEDFORMLINESIZE:132 61 | -------------------------------------------------------------------------------- /src/Elastic_mod.f90: -------------------------------------------------------------------------------- 1 | !DIR$ FREEFORM 2 | !> 3 | !> Example Fortran module for reproducing linear elastic behaviour 4 | !> 5 | module Elastic_mod 6 | use iso_fortran_env, only: dp=>real64 7 | implicit none 8 | 9 | 10 | !> Structure for encapsulating material properties by name 11 | type elastic_props_t 12 | real(dp) :: e 13 | real(dp) :: xnu 14 | end type elastic_props_t 15 | 16 | contains 17 | 18 | 19 | !> Top-level wrapper for linear elastic user material 20 | subroutine umat_elastic(ddsdde, stress, props_array, dstran, ndi, nshr) 21 | real(dp), intent(out) :: ddsdde(:,:) 22 | real(dp), intent(inout) :: stress(:) 23 | real(dp), intent(in) :: props_array(:) 24 | real(dp), intent(in) :: dstran(:) 25 | integer, intent(in) :: ndi, nshr 26 | 27 | type(elastic_props_t) :: props 28 | 29 | props = get_properties(props_array) 30 | 31 | if (ndi==3 .and. nshr==1) then 32 | 33 | call plain_strain_jacobian(ddsdde, props) 34 | 35 | else if (ndi==2 .and. nshr==1) then 36 | 37 | call plain_stress_jacobian(ddsdde, props) 38 | 39 | else 40 | 41 | call jacobian_3d(ddsdde,props) 42 | 43 | end if 44 | 45 | call update_stress(stress, dstran, ddsdde) 46 | 47 | end subroutine umat_elastic 48 | 49 | 50 | !> Extract named material properties from props_array 51 | function get_properties(props_array) result(props) 52 | real(dp), intent(in) :: props_array(:) 53 | type(elastic_props_t) :: props 54 | 55 | props%e = props_array(1) 56 | props%xnu = props_array(2) 57 | 58 | end function get_properties 59 | 60 | 61 | !> Calculate jacobian terms for plain strain or axisymmetric case 62 | subroutine plain_strain_jacobian(ddsdde, props) 63 | real(dp), intent(out) :: ddsdde(:,:) 64 | type(elastic_props_t), intent(in) :: props 65 | 66 | ddsdde(:,:) = 0.d0 67 | 68 | ddsdde(1,1) = 1.d0 - props%xnu 69 | ddsdde(1,2) = props%xnu 70 | ddsdde(1,3) = props%xnu 71 | ddsdde(2,1) = props%xnu 72 | ddsdde(2,2) = 1.d0 - props%xnu 73 | ddsdde(2,3) = props%xnu 74 | ddsdde(3,1) = props%xnu 75 | ddsdde(3,2) = props%xnu 76 | ddsdde(3,3) = 1.d0 - props%xnu 77 | ddsdde(4,4) = 0.5d0*(1.d0 - 2.d0*props%xnu) 78 | 79 | ddsdde = ddsdde*props%e/( (1.d0 + props%xnu)*(1.d0-2.d0*props%xnu) ) 80 | 81 | end subroutine plain_strain_jacobian 82 | 83 | 84 | !> Calculate jacobian terms for plains tress case 85 | subroutine plain_stress_jacobian(ddsdde, props) 86 | real(dp), intent(out) :: ddsdde(:,:) 87 | type(elastic_props_t), intent(in) :: props 88 | 89 | ddsdde(:,:) = 0.d0 90 | 91 | ddsdde(1,1) = 1.d0 92 | ddsdde(1,2) = props%xnu 93 | ddsdde(2,1) = props%xnu 94 | ddsdde(2,2) = 1.d0 95 | ddsdde(3,3) = 0.5d0*(1.d0 - props%xnu) 96 | 97 | ddsdde = ddsdde*props%e/( (1.d0+props%xnu**2) ) 98 | 99 | end subroutine plain_stress_jacobian 100 | 101 | 102 | !> Calculate jacobian terms for 3D case 103 | subroutine jacobian_3d(ddsdde,props) 104 | real(dp), intent(out) :: ddsdde(:,:) 105 | type(elastic_props_t), intent(in) :: props 106 | 107 | ddsdde(:,:) = 0.d0 108 | 109 | ddsdde(1,1) = 1.d0-props%xnu 110 | ddsdde(1,2) = props%xnu 111 | ddsdde(1,3) = props%xnu 112 | ddsdde(2,1) = props%xnu 113 | ddsdde(2,2) = 1.d0-props%xnu 114 | ddsdde(2,3) = props%xnu 115 | ddsdde(3,1) = props%xnu 116 | ddsdde(3,2) = props%xnu 117 | ddsdde(3,3) = 1.d0-props%xnu 118 | ddsdde(4,4) = 0.5d0*(1.d0-2.d0*props%xnu) 119 | ddsdde(5,5) = ddsdde(4,4) 120 | ddsdde(6,6) = ddsdde(4,4) 121 | 122 | ddsdde = ddsdde*props%e/( (1.d0+props%xnu)*(1.d0-2.d0*props%xnu) ) 123 | 124 | end subroutine jacobian_3d 125 | 126 | 127 | !> Update stress tensor from strain increments 128 | subroutine update_stress(stress, dstran, ddsdde) 129 | real(dp), intent(inout) :: stress(:) 130 | real(dp), intent(in) :: dstran(:) 131 | real(dp), intent(in) :: ddsdde(:,:) 132 | 133 | integer :: i, j, ntens 134 | ntens = size(stress,1) 135 | 136 | do i = 1,ntens 137 | do j = 1,ntens 138 | stress(i) = stress(i) + ddsdde(i,j)*dstran(j) 139 | end do 140 | end do 141 | 142 | end subroutine update_stress 143 | 144 | end module Elastic_mod 145 | 146 | !DIR$ NOFREEFORM 147 | !DIR$ FIXEDFORMLINESIZE:132 -------------------------------------------------------------------------------- /src/umat.f: -------------------------------------------------------------------------------- 1 | #include 'Abaqus_Definitions.f90' 2 | #include 'Elastic_mod.f90' 3 | !DIR$ FREEFORM 4 | 5 | !> Example UMAT reproducing linear elastic behaviour 6 | subroutine umat(stress,statev,ddsdde,sse,spd,scd, & 7 | rpl,ddsddt,drplde,drpldt, & 8 | stran,dstran,time,dtime,temp,dtemp,predef,dpred,cmname, & 9 | ndi,nshr,ntens,nstatv,props_array,nprops,coords,drot,pnewdt, & 10 | celent,dfgrd0,dfgrd1,noel,npt,layer,kspt,jstep,kinc) 11 | 12 | use Abaqus_Definitions, only: wp=>abaqus_real_kind 13 | use Elastic_mod 14 | implicit none 15 | 16 | real(wp), intent(inout) :: stress(ntens), statev(nstatv), ddsdde(ntens,ntens) 17 | real(wp), intent(inout) :: sse, spd, scd, rpl, ddsddt(ntens), drplde(ntens) 18 | real(wp), intent(inout) :: drpldt 19 | real(wp), intent(in) :: stran(ntens), dstran(ntens), time(2), dtime, temp 20 | real(wp), intent(in) :: dtemp, predef(1), dpred(1) 21 | character(len=80), intent(in) :: cmname 22 | integer, intent(in) :: ndi, nshr, ntens, nstatv, nprops 23 | real(wp), intent(in) :: props_array(nprops), coords(3), drot(3, 3), pnewdt 24 | real(wp), intent(in) :: celent, dfgrd0(3,3), dfgrd1(3,3) 25 | integer, intent(in) :: noel, npt, layer, kspt, jstep(4), kinc 26 | 27 | 28 | if (cmname(1:12) == 'UMAT_ELASTIC') then 29 | 30 | call umat_elastic(ddsdde, stress, props_array, dstran, ndi, nshr) 31 | 32 | ! elseif (cmname == 'UMAT_OTHER') then 33 | 34 | ! call some other material subroutine 35 | 36 | else 37 | 38 | write(*,*) ' (!) UMAT ERROR: Unrecognized user material name "'//trim(cmname)//'"' 39 | call xit() 40 | 41 | end if 42 | 43 | end subroutine umat 44 | -------------------------------------------------------------------------------- /test/test_elastic.f90: -------------------------------------------------------------------------------- 1 | ! Unit test suite 2 | ! 3 | ! Run this test suite with the command: `abaci test` 4 | ! 5 | module test_elastic 6 | use Abaqus_Definitions, only: wp=>abaqus_real_kind 7 | use Elastic_mod ! Module to test 8 | use naturalfruit ! Test framework (for assert methods) 9 | implicit none 10 | 11 | contains 12 | 13 | ! Unit test for the get_props subroutine 14 | ! Check that properties are assigned correctly 15 | subroutine test_get_props 16 | 17 | type(elastic_props_t) :: props 18 | real(wp) :: props_array(2) = [1.0d9, 0.5d0] 19 | 20 | props = get_properties(props_array) 21 | 22 | call assert_equal(props_array(1), props%e, & 23 | message="props%e does not match input array") 24 | 25 | 26 | call assert_equal(props_array(2), props%xnu, & 27 | message="props%xnu does not match input array") 28 | 29 | end subroutine test_get_props 30 | 31 | 32 | ! Unit test for the update_stress subroutine 33 | ! Assert that the resulting stress vector is correct 34 | subroutine test_update_stress 35 | 36 | real(wp) :: stress(3) = 0.0d0 37 | real(wp) :: strain(3) = [1.0d0, 2.0d0, 3.0d0] 38 | real(wp) :: jac_1(3,3) = reshape([1, 0, 0, 0, 1, 0, 0, 0, 1],[3,3]) 39 | real(wp) :: jac_2(3,3) = reshape([0, 0, 1, 0, 1, 0, 1, 0, 0],[3,3]) 40 | 41 | call update_stress(stress, strain, jac_1) 42 | 43 | call assert_equal(stress, [1.0d0, 2.0d0, 3.0d0] , & 44 | message="incorrect stress vector") 45 | 46 | 47 | call update_stress(stress, strain, jac_2) 48 | 49 | call assert_equal(stress, [4.0d0, 4.0d0, 4.0d0] , & 50 | message="incorrect stress vector") 51 | 52 | end subroutine test_update_stress 53 | 54 | end module test_elastic 55 | --------------------------------------------------------------------------------