From 548145af19dd295646b459dc4f3174bb3b102b69 Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 7 Nov 2017 10:58:12 +0100 Subject: [PATCH 01/26] [skip ci] updated version information after successful test of v2.0.1-980-ge2c66ca --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 63005f482..d63a99433 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-976-g035215e +v2.0.1-980-ge2c66ca From e4700cda25e163a2c778b0a7b11159049e87ffc4 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 7 Nov 2017 14:56:28 -0500 Subject: [PATCH 02/26] changed fixed_seed to random_seed for clarity --- examples/ConfigFiles/numerics.config | 2 +- src/math.f90 | 6 +++--- src/numerics.f90 | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/ConfigFiles/numerics.config b/examples/ConfigFiles/numerics.config index 580b58e57..ab8903927 100644 --- a/examples/ConfigFiles/numerics.config +++ b/examples/ConfigFiles/numerics.config @@ -49,7 +49,7 @@ maxVolDiscrepancy_RGC 1.0e-5 # maximum allowable relative volume discr volDiscrepancyMod_RGC 1.0e+12 discrepancyPower_RGC 5.0 -fixed_seed 0 # put any number larger than zero, integer, if you want to have a pseudo random distribution +random_seed 0 # any integer larger than zero seeds the random generator, otherwise random seeding ## spectral parameters ## err_div_tolAbs 1.0e-3 # absolute tolerance for fulfillment of stress equilibrium diff --git a/src/math.f90 b/src/math.f90 index 80bf239a3..29d401162 100644 --- a/src/math.f90 +++ b/src/math.f90 @@ -177,7 +177,7 @@ subroutine math_init compiler_version, & compiler_options #endif - use numerics, only: fixedSeed + use numerics, only: randomSeed use IO, only: IO_timeStamp implicit none @@ -194,8 +194,8 @@ subroutine math_init call random_seed(size=randSize) if (allocated(randInit)) deallocate(randInit) allocate(randInit(randSize)) - if (fixedSeed > 0_pInt) then - randInit(1:randSize) = int(fixedSeed) ! fixedSeed is of type pInt, randInit not + if (randomSeed > 0_pInt) then + randInit(1:randSize) = int(randomSeed) ! randomSeed is of type pInt, randInit not call random_seed(put=randInit) else call random_seed() diff --git a/src/numerics.f90 b/src/numerics.f90 index 2085e221e..70c7f3c30 100644 --- a/src/numerics.f90 +++ b/src/numerics.f90 @@ -25,7 +25,7 @@ module numerics nState = 10_pInt, & !< state loop limit nStress = 40_pInt, & !< stress loop limit pert_method = 1_pInt, & !< method used in perturbation technique for tangent - fixedSeed = 0_pInt, & !< fixed seeding for pseudo-random number generator, Default 0: use random seed + randomSeed = 0_pInt, & !< fixed seeding for pseudo-random number generator, Default 0: use random seed worldrank = 0_pInt, & !< MPI worldrank (/=0 for MPI simulations only) worldsize = 0_pInt !< MPI worldsize (/=0 for MPI simulations only) integer(4), protected, public :: & @@ -359,8 +359,8 @@ subroutine numerics_init !-------------------------------------------------------------------------------------------------- ! random seeding parameter - case ('fixed_seed') - fixedSeed = IO_intValue(line,chunkPos,2_pInt) + case ('random_seed','fixed_seed') + randomSeed = IO_intValue(line,chunkPos,2_pInt) !-------------------------------------------------------------------------------------------------- ! gradient parameter @@ -560,9 +560,9 @@ subroutine numerics_init !-------------------------------------------------------------------------------------------------- ! Random seeding parameter - write(6,'(a24,1x,i16,/)') ' fixed_seed: ',fixedSeed - if (fixedSeed <= 0_pInt) & - write(6,'(a,/)') ' No fixed Seed: Random is random!' + write(6,'(a24,1x,i16,/)') ' random_seed: ',randomSeed + if (randomSeed <= 0_pInt) & + write(6,'(a,/)') ' random seed will be generated!' !-------------------------------------------------------------------------------------------------- ! gradient parameter From 74396bf828ba22ab2e3360e9bf43bb2b3c61a5c1 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 8 Nov 2017 04:48:44 +0100 Subject: [PATCH 03/26] [skip ci] updated version information after successful test of v2.0.1-982-ge4700cd --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d63a99433..ed59e5b34 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-980-ge2c66ca +v2.0.1-982-ge4700cd From c81a438546d1dda06a6c322001c280f2330c943a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 18 Nov 2017 22:39:13 +0100 Subject: [PATCH 04/26] SCHMID-BOAS notation for reference --- src/lattice.f90 | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lattice.f90 b/src/lattice.f90 index 328d65380..9635643e8 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -96,19 +96,19 @@ module lattice real(pReal), dimension(3+3,LATTICE_fcc_Nslip), parameter, private :: & LATTICE_fcc_systemSlip = reshape(real([& - ! Slip direction Plane normal - 0, 1,-1, 1, 1, 1, & - -1, 0, 1, 1, 1, 1, & - 1,-1, 0, 1, 1, 1, & - 0,-1,-1, -1,-1, 1, & - 1, 0, 1, -1,-1, 1, & - -1, 1, 0, -1,-1, 1, & - 0,-1, 1, 1,-1,-1, & - -1, 0,-1, 1,-1,-1, & - 1, 1, 0, 1,-1,-1, & - 0, 1, 1, -1, 1,-1, & - 1, 0,-1, -1, 1,-1, & - -1,-1, 0, -1, 1,-1 & + ! Slip direction Plane normal ! SCHMID-BOAS notation + 0, 1,-1, 1, 1, 1, & ! B2 + -1, 0, 1, 1, 1, 1, & ! B4 + 1,-1, 0, 1, 1, 1, & ! B5 + 0,-1,-1, -1,-1, 1, & ! C1 + 1, 0, 1, -1,-1, 1, & ! C3 + -1, 1, 0, -1,-1, 1, & ! C5 + 0,-1, 1, 1,-1,-1, & ! A2 + -1, 0,-1, 1,-1,-1, & ! A3 + 1, 1, 0, 1,-1,-1, & ! A6 + 0, 1, 1, -1, 1,-1, & ! D1 + 1, 0,-1, -1, 1,-1, & ! D4 + -1,-1, 0, -1, 1,-1 & ! D6 ],pReal),[ 3_pInt + 3_pInt,LATTICE_fcc_Nslip]) !< Slip system <110>{111} directions. Sorted according to Eisenlohr & Hantcherli real(pReal), dimension(3+3,LATTICE_fcc_Ntwin), parameter, private :: & From 90c617e05552f5617601c0d9ab093928fa41c86b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 20 Nov 2017 12:57:30 +0100 Subject: [PATCH 05/26] incorporating changes in PETSc and DAMASK --- installation/patch/PETSc3.8 | 447 ++++++++++++----------------------- installation/patch/README.md | 7 + 2 files changed, 152 insertions(+), 302 deletions(-) diff --git a/installation/patch/PETSc3.8 b/installation/patch/PETSc3.8 index c6b95f775..c7008fffa 100644 --- a/installation/patch/PETSc3.8 +++ b/installation/patch/PETSc3.8 @@ -1,38 +1,36 @@ -From 2355d41203f829e5a24154184ab1a1a05e40b5e2 Mon Sep 17 00:00:00 2001 +From 1cc8e60d02323c3c9448df73bfc8f36f697e939a Mon Sep 17 00:00:00 2001 From: Martin Diehl -Date: Sun, 5 Nov 2017 12:48:31 +0100 -Subject: [PATCH 1/3] adjusted calling of PETSc routines. Compiles but crashes - conditional prints for worldrank not needed (redirected to /dev/null) failing - during compilation is faster than during runtime +Date: Mon, 20 Nov 2017 12:53:47 +0100 +Subject: [PATCH] existing patch + further fixes --- - src/DAMASK_spectral.f90 | 27 ++++++------------ - src/constitutive.f90 | 6 ++-- - src/damage_local.f90 | 8 ++---- - src/damage_none.f90 | 8 ++---- - src/damage_nonlocal.f90 | 8 ++---- - src/homogenization_RGC.f90 | 8 ++---- - src/homogenization_isostrain.f90 | 8 ++---- - src/homogenization_none.f90 | 10 ++----- - src/hydrogenflux_cahnhilliard.f90 | 8 ++---- - src/hydrogenflux_isoconc.f90 | 10 ++----- - src/kinematics_cleavage_opening.f90 | 8 ++---- - src/kinematics_slipplane_opening.f90 | 8 ++---- - src/kinematics_thermal_expansion.f90 | 8 ++---- - src/kinematics_vacancy_strain.f90 | 8 ++---- - src/mesh.f90 | 10 +++---- - src/numerics.f90 | 13 ++++----- - src/spectral_damage.f90 | 37 +++++++----------------- - src/spectral_interface.f90 | 31 ++++++++++---------- - src/spectral_mech_AL.f90 | 43 +++++++++------------------- - src/spectral_mech_Basic.f90 | 48 +++++++++++-------------------- - src/spectral_mech_Polarisation.f90 | 49 +++++++++++--------------------- - src/spectral_thermal.f90 | 55 ++++++++++++++++-------------------- - src/spectral_utilities.f90 | 34 ++++++++-------------- - 23 files changed, 156 insertions(+), 297 deletions(-) + src/DAMASK_spectral.f90 | 27 +++++-------- + src/constitutive.f90 | 6 +-- + src/damage_local.f90 | 8 +--- + src/damage_none.f90 | 8 +--- + src/damage_nonlocal.f90 | 8 +--- + src/homogenization_RGC.f90 | 8 +--- + src/homogenization_isostrain.f90 | 8 +--- + src/homogenization_none.f90 | 10 ++--- + src/hydrogenflux_cahnhilliard.f90 | 8 +--- + src/hydrogenflux_isoconc.f90 | 10 ++--- + src/kinematics_cleavage_opening.f90 | 8 +--- + src/kinematics_slipplane_opening.f90 | 8 +--- + src/kinematics_thermal_expansion.f90 | 8 +--- + src/kinematics_vacancy_strain.f90 | 8 +--- + src/mesh.f90 | 12 +++--- + src/numerics.f90 | 13 +++---- + src/spectral_damage.f90 | 39 ++++++------------- + src/spectral_interface.f90 | 31 ++++++++------- + src/spectral_mech_AL.f90 | 46 ++++++++-------------- + src/spectral_mech_Basic.f90 | 52 +++++++++---------------- + src/spectral_mech_Polarisation.f90 | 52 +++++++++---------------- + src/spectral_thermal.f90 | 75 +++++++++++++++++------------------- + src/spectral_utilities.f90 | 34 ++++++---------- + 23 files changed, 175 insertions(+), 312 deletions(-) diff --git a/src/DAMASK_spectral.f90 b/src/DAMASK_spectral.f90 -index dc529b2e..ee6b20fc 100644 +index f32bfb7b..c315b1b8 100644 --- a/src/DAMASK_spectral.f90 +++ b/src/DAMASK_spectral.f90 @@ -12,6 +12,8 @@ program DAMASK_spectral @@ -82,9 +80,9 @@ index dc529b2e..ee6b20fc 100644 @@ -448,7 +440,7 @@ program DAMASK_spectral call MPI_file_write(resUnit, & reshape(materialpoint_results(:,:,outputIndex(1):outputIndex(2)), & - [(outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults]), & -- (outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults, & -+ int((outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults), & + [(outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)]), & +- (outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt), & ++ int(outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults, & MPI_DOUBLE, MPI_STATUS_IGNORE, ierr) if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_file_write') enddo @@ -101,9 +99,9 @@ index dc529b2e..ee6b20fc 100644 @@ -646,7 +637,7 @@ program DAMASK_spectral min(i*((maxRealOut)/materialpoint_sizeResults),size(materialpoint_results,3))],pLongInt) call MPI_file_write(resUnit,reshape(materialpoint_results(:,:,outputIndex(1):outputIndex(2)),& - [(outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults]), & -- (outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults,& -+ int((outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults), & + [(outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)]), & +- (outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt),& ++ int(outputIndex(2)-outputIndex(1)+1)*materialpoint_sizeResults,& MPI_DOUBLE, MPI_STATUS_IGNORE, ierr) if(ierr /=0_pInt) call IO_error(894_pInt, ext_msg='MPI_file_write') enddo @@ -454,7 +452,7 @@ index 791c0e3c..9558f506 100644 maxNinstance = int(count(phase_kinematics == KINEMATICS_vacancy_strain_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/mesh.f90 b/src/mesh.f90 -index 87160f2c..6e3b4823 100644 +index 666fe1e3..a314c22c 100644 --- a/src/mesh.f90 +++ b/src/mesh.f90 @@ -115,11 +115,6 @@ module mesh @@ -488,8 +486,17 @@ index 87160f2c..6e3b4823 100644 integer(C_INTPTR_T) :: devNull, local_K, local_K_offset integer :: ierr, worldsize #endif +@@ -518,8 +518,6 @@ subroutine mesh_init(ip,el) + integer(pInt), intent(in) :: el, ip + integer(pInt) :: j + logical :: myDebug +- +- external :: MPI_comm_size + + write(6,'(/,a)') ' <<<+- mesh init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() diff --git a/src/numerics.f90 b/src/numerics.f90 -index 2085e221..d2d00f3e 100644 +index 70c7f3c3..e7d54893 100644 --- a/src/numerics.f90 +++ b/src/numerics.f90 @@ -10,9 +10,6 @@ module numerics @@ -527,7 +534,7 @@ index 2085e221..d2d00f3e 100644 call MPI_Comm_rank(PETSC_COMM_WORLD,worldrank,ierr);CHKERRQ(ierr) call MPI_Comm_size(PETSC_COMM_WORLD,worldsize,ierr);CHKERRQ(ierr) diff --git a/src/spectral_damage.f90 b/src/spectral_damage.f90 -index 72765987..cea6f69c 100644 +index 72765987..11da3b96 100644 --- a/src/spectral_damage.f90 +++ b/src/spectral_damage.f90 @@ -4,8 +4,10 @@ @@ -597,8 +604,12 @@ index 72765987..cea6f69c 100644 DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, & !< cut off stencil at boundary DMDA_STENCIL_BOX, & !< Moore (26) neighborhood around central point grid(1),grid(2),grid(3), & !< global grid -@@ -126,7 +116,7 @@ subroutine spectral_damage_init() +@@ -124,9 +114,11 @@ subroutine spectral_damage_init() + damage_grid,ierr) !< handle, error + CHKERRQ(ierr) call SNESSetDM(damage_snes,damage_grid,ierr); CHKERRQ(ierr) !< connect snes to da ++ call DMsetFromOptions(damage_grid,ierr); CHKERRQ(ierr) ++ call DMsetUp(damage_grid,ierr); CHKERRQ(ierr) call DMCreateGlobalVector(damage_grid,solution,ierr); CHKERRQ(ierr) !< global solution vector (grid x 1, i.e. every def grad tensor) call DMDASNESSetFunctionLocal(damage_grid,INSERT_VALUES,spectral_damage_formResidual,& - PETSC_NULL_OBJECT,ierr) !< residual vector of same shape as solution vector @@ -606,16 +617,16 @@ index 72765987..cea6f69c 100644 CHKERRQ(ierr) call SNESSetFromOptions(damage_snes,ierr); CHKERRQ(ierr) !< pull it all together with additional cli arguments call SNESGetType(damage_snes,snes_type,ierr); CHKERRQ(ierr) -@@ -214,7 +204,7 @@ type(tSolutionState) function spectral_damage_solution(timeinc,timeinc_old,loadC +@@ -214,7 +206,7 @@ type(tSolutionState) function spectral_damage_solution(timeinc,timeinc_old,loadC params%timeinc = timeinc params%timeincOld = timeinc_old - call SNESSolve(damage_snes,PETSC_NULL_OBJECT,solution,ierr); CHKERRQ(ierr) -+ call SNESSolve(damage_snes,PETSC_NULL_SNES,solution,ierr); CHKERRQ(ierr) ++ call SNESSolve(damage_snes,PETSC_NULL_VEC,solution,ierr); CHKERRQ(ierr) call SNESGetConvergedReason(damage_snes,reason,ierr); CHKERRQ(ierr) if (reason < 1) then -@@ -360,9 +350,6 @@ subroutine spectral_damage_forward() +@@ -360,9 +352,6 @@ subroutine spectral_damage_forward() PetscScalar, dimension(:,:,:), pointer :: x_scal PetscErrorCode :: ierr @@ -625,7 +636,7 @@ index 72765987..cea6f69c 100644 if (cutBack) then damage_current = damage_lastInc damage_stagInc = damage_lastInc -@@ -405,10 +392,6 @@ subroutine spectral_damage_destroy() +@@ -405,10 +394,6 @@ subroutine spectral_damage_destroy() implicit none PetscErrorCode :: ierr @@ -716,7 +727,7 @@ index 3c8489d0..51360ac1 100644 getGeometryFile = geometryParameter posExt = scan(getGeometryFile,'.',back=.true.) diff --git a/src/spectral_mech_AL.f90 b/src/spectral_mech_AL.f90 -index 6d0fff28..e7ff0fbe 100644 +index 6d0fff28..dc221f6c 100644 --- a/src/spectral_mech_AL.f90 +++ b/src/spectral_mech_AL.f90 @@ -5,6 +5,8 @@ @@ -778,9 +789,12 @@ index 6d0fff28..e7ff0fbe 100644 write(6,'(/,a)') ' <<<+- DAMASK_spectral_solverAL init -+>>>' write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" -@@ -166,9 +161,9 @@ subroutine AL_init +@@ -165,10 +160,12 @@ subroutine AL_init + da,ierr) ! handle, error CHKERRQ(ierr) call SNESSetDM(snes,da,ierr); CHKERRQ(ierr) ++ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) ++ call DMsetUp(da,ierr); CHKERRQ(ierr) call DMCreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) - call DMDASNESSetFunctionLocal(da,INSERT_VALUES,AL_formResidual,PETSC_NULL_OBJECT,ierr) + call DMDASNESSetFunctionLocal(da,INSERT_VALUES,AL_formResidual,PETSC_NULL_SNES,ierr) @@ -790,7 +804,7 @@ index 6d0fff28..e7ff0fbe 100644 CHKERRQ(ierr) call SNESSetFromOptions(snes,ierr); CHKERRQ(ierr) -@@ -280,8 +275,7 @@ type(tSolutionState) function & +@@ -280,8 +277,7 @@ type(tSolutionState) function & SNESConvergedReason :: reason external :: & @@ -800,16 +814,17 @@ index 6d0fff28..e7ff0fbe 100644 incInfo = incInfoIn -@@ -304,7 +298,7 @@ type(tSolutionState) function & +@@ -304,8 +300,7 @@ type(tSolutionState) function & !-------------------------------------------------------------------------------------------------- ! solve BVP - call SNESSolve(snes,PETSC_NULL_OBJECT,solution_vec,ierr) -+ call SNESSolve(snes,PETSC_NULL_SNES,solution_vec,ierr) - CHKERRQ(ierr) +- CHKERRQ(ierr) ++ call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) !-------------------------------------------------------------------------------------------------- -@@ -383,10 +377,6 @@ subroutine AL_formResidual(in,x_scal,f_scal,dummy,ierr) + ! check convergence +@@ -383,10 +378,6 @@ subroutine AL_formResidual(in,x_scal,f_scal,dummy,ierr) integer(pInt) :: & i, j, k, e @@ -820,7 +835,7 @@ index 6d0fff28..e7ff0fbe 100644 F => x_scal(1:3,1:3,1,& XG_RANGE,YG_RANGE,ZG_RANGE) F_lambda => x_scal(1:3,1:3,2,& -@@ -697,11 +687,6 @@ subroutine AL_destroy() +@@ -697,11 +688,6 @@ subroutine AL_destroy() implicit none PetscErrorCode :: ierr @@ -833,7 +848,7 @@ index 6d0fff28..e7ff0fbe 100644 call SNESDestroy(snes,ierr); CHKERRQ(ierr) call DMDestroy(da,ierr); CHKERRQ(ierr) diff --git a/src/spectral_mech_Basic.f90 b/src/spectral_mech_Basic.f90 -index cfb72712..b02cfd8c 100644 +index 55403ee7..fe9eb493 100644 --- a/src/spectral_mech_Basic.f90 +++ b/src/spectral_mech_Basic.f90 @@ -5,6 +5,8 @@ @@ -888,7 +903,7 @@ index cfb72712..b02cfd8c 100644 write(6,'(/,a)') ' <<<+- DAMASK_spectral_solverBasicPETSc init -+>>>' write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" -@@ -152,14 +146,14 @@ subroutine basicPETSc_init +@@ -152,19 +146,20 @@ subroutine basicPETSc_init grid(1),grid(2),localK, & ! local grid da,ierr) ! handle, error CHKERRQ(ierr) @@ -896,6 +911,8 @@ index cfb72712..b02cfd8c 100644 - call DMCreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) ! global solution vector (grid x 9, i.e. every def grad tensor) - call DMDASNESSetFunctionLocal(da,INSERT_VALUES,BasicPETSC_formResidual,PETSC_NULL_OBJECT,ierr) ! residual vector of same shape as solution vector + call SNESsetDM(snes,da,ierr); CHKERRQ(ierr) ++ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) ++ call DMsetUp(da,ierr); CHKERRQ(ierr) + call DMcreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) ! global solution vector (grid x 9, i.e. every def grad tensor) + call DMDASNESsetFunctionLocal(da,INSERT_VALUES,BasicPETSC_formResidual,PETSC_NULL_SNES,ierr) ! residual vector of same shape as solution vector CHKERRQ(ierr) @@ -909,7 +926,12 @@ index cfb72712..b02cfd8c 100644 !-------------------------------------------------------------------------------------------------- ! init fields -@@ -253,8 +247,7 @@ type(tSolutionState) function & + call DMDAVecGetArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) ! get the data out of PETSc to work with +- + restart: if (restartInc > 1_pInt) then + if (iand(debug_level(debug_spectral),debug_spectralRestart)/= 0) & + write(6,'(/,a,'//IO_intOut(restartInc-1_pInt)//',a)') & +@@ -253,8 +248,7 @@ type(tSolutionState) function & SNESConvergedReason :: reason external :: & @@ -919,16 +941,17 @@ index cfb72712..b02cfd8c 100644 incInfo = incInfoIn -@@ -274,7 +267,7 @@ type(tSolutionState) function & +@@ -274,8 +268,7 @@ type(tSolutionState) function & !-------------------------------------------------------------------------------------------------- ! solve BVP - call SNESSolve(snes,PETSC_NULL_OBJECT,solution_vec,ierr) -+ call SNESSolve(snes,PETSC_NULL_SNES,solution_vec,ierr) - CHKERRQ(ierr) +- CHKERRQ(ierr) ++ call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) !-------------------------------------------------------------------------------------------------- -@@ -337,10 +330,6 @@ subroutine BasicPETSC_formResidual(in,x_scal,f_scal,dummy,ierr) + ! check convergence +@@ -336,10 +329,6 @@ subroutine BasicPETSC_formResidual(in,x_scal,f_scal,dummy,ierr) PetscObject :: dummy PetscErrorCode :: ierr @@ -939,7 +962,7 @@ index cfb72712..b02cfd8c 100644 call SNESGetNumberFunctionEvals(snes,nfuncs,ierr); CHKERRQ(ierr) call SNESGetIterationNumber(snes,PETScIter,ierr); CHKERRQ(ierr) -@@ -556,11 +545,6 @@ subroutine BasicPETSc_destroy() +@@ -555,11 +544,6 @@ subroutine BasicPETSc_destroy() implicit none PetscErrorCode :: ierr @@ -952,7 +975,7 @@ index cfb72712..b02cfd8c 100644 call SNESDestroy(snes,ierr); CHKERRQ(ierr) call DMDestroy(da,ierr); CHKERRQ(ierr) diff --git a/src/spectral_mech_Polarisation.f90 b/src/spectral_mech_Polarisation.f90 -index ecf707d4..2b9dddc0 100644 +index ecf707d4..3b024f56 100644 --- a/src/spectral_mech_Polarisation.f90 +++ b/src/spectral_mech_Polarisation.f90 @@ -5,6 +5,8 @@ @@ -1014,7 +1037,7 @@ index ecf707d4..2b9dddc0 100644 write(6,'(/,a)') ' <<<+- DAMASK_spectral_solverPolarisation init -+>>>' write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" -@@ -164,13 +159,13 @@ subroutine Polarisation_init +@@ -164,13 +159,15 @@ subroutine Polarisation_init grid(1),grid(2),localK, & ! local grid da,ierr) ! handle, error CHKERRQ(ierr) @@ -1022,6 +1045,8 @@ index ecf707d4..2b9dddc0 100644 - call DMCreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) - call DMDASNESSetFunctionLocal(da,INSERT_VALUES,Polarisation_formResidual,PETSC_NULL_OBJECT,ierr) + call SNESsetDM(snes,da,ierr); CHKERRQ(ierr) ++ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) ++ call DMsetUp(da,ierr); CHKERRQ(ierr) + call DMcreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) + call DMDASNESsetFunctionLocal(da,INSERT_VALUES,Polarisation_formResidual,PETSC_NULL_SNES,ierr) CHKERRQ(ierr) @@ -1033,7 +1058,7 @@ index ecf707d4..2b9dddc0 100644 !-------------------------------------------------------------------------------------------------- ! init fields -@@ -280,8 +275,7 @@ type(tSolutionState) function & +@@ -280,8 +277,7 @@ type(tSolutionState) function & SNESConvergedReason :: reason external :: & @@ -1043,16 +1068,17 @@ index ecf707d4..2b9dddc0 100644 incInfo = incInfoIn -@@ -304,7 +298,7 @@ type(tSolutionState) function & +@@ -304,8 +300,7 @@ type(tSolutionState) function & !-------------------------------------------------------------------------------------------------- ! solve BVP - call SNESSolve(snes,PETSC_NULL_OBJECT,solution_vec,ierr) -+ call SNESSolve(snes,PETSC_NULL_SNES,solution_vec,ierr) - CHKERRQ(ierr) +- CHKERRQ(ierr) ++ call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) !-------------------------------------------------------------------------------------------------- -@@ -383,10 +377,6 @@ subroutine Polarisation_formResidual(in,x_scal,f_scal,dummy,ierr) + ! check convergence +@@ -383,10 +378,6 @@ subroutine Polarisation_formResidual(in,x_scal,f_scal,dummy,ierr) integer(pInt) :: & i, j, k, e @@ -1063,7 +1089,7 @@ index ecf707d4..2b9dddc0 100644 F => x_scal(1:3,1:3,1,& XG_RANGE,YG_RANGE,ZG_RANGE) F_tau => x_scal(1:3,1:3,2,& -@@ -698,11 +688,6 @@ subroutine Polarisation_destroy() +@@ -698,11 +689,6 @@ subroutine Polarisation_destroy() implicit none PetscErrorCode :: ierr @@ -1076,7 +1102,7 @@ index ecf707d4..2b9dddc0 100644 call SNESDestroy(snes,ierr); CHKERRQ(ierr) call DMDestroy(da,ierr); CHKERRQ(ierr) diff --git a/src/spectral_thermal.f90 b/src/spectral_thermal.f90 -index 322f1203..cc0f7678 100644 +index 322f1203..2374d83b 100644 --- a/src/spectral_thermal.f90 +++ b/src/spectral_thermal.f90 @@ -4,6 +4,8 @@ @@ -1151,25 +1177,64 @@ index 322f1203..cc0f7678 100644 !-------------------------------------------------------------------------------------------------- ! initialize solver specific parts of PETSc -@@ -127,7 +122,7 @@ subroutine spectral_thermal_init - call SNESSetDM(thermal_snes,thermal_grid,ierr); CHKERRQ(ierr) ! connect snes to da - call DMCreateGlobalVector(thermal_grid,solution ,ierr); CHKERRQ(ierr) ! global solution vector (grid x 1, i.e. every def grad tensor) - call DMDASNESSetFunctionLocal(thermal_grid,INSERT_VALUES,spectral_thermal_formResidual,& +@@ -124,16 +119,18 @@ subroutine spectral_thermal_init + grid (1),grid(2),localK, & ! local grid + thermal_grid,ierr) ! handle, error + CHKERRQ(ierr) +- call SNESSetDM(thermal_snes,thermal_grid,ierr); CHKERRQ(ierr) ! connect snes to da +- call DMCreateGlobalVector(thermal_grid,solution ,ierr); CHKERRQ(ierr) ! global solution vector (grid x 1, i.e. every def grad tensor) +- call DMDASNESSetFunctionLocal(thermal_grid,INSERT_VALUES,spectral_thermal_formResidual,& - PETSC_NULL_OBJECT,ierr) ! residual vector of same shape as solution vector ++ call SNESsetDM(thermal_snes,thermal_grid,ierr); CHKERRQ(ierr) ! connect snes to da ++ call DMsetFromOptions(thermal_grid,ierr); CHKERRQ(ierr) ++ call DMsetUp(thermal_grid,ierr); CHKERRQ(ierr) ++ call DMcreateGlobalVector(thermal_grid,solution,ierr); CHKERRQ(ierr) ! global solution vector (grid x 1, i.e. every def grad tensor) ++ call DMDASNESsetFunctionLocal(thermal_grid,INSERT_VALUES,spectral_thermal_formResidual,& + PETSC_NULL_SNES,ierr) ! residual vector of same shape as solution vector CHKERRQ(ierr) - call SNESSetFromOptions(thermal_snes,ierr); CHKERRQ(ierr) ! pull it all together with additional cli arguments +- call SNESSetFromOptions(thermal_snes,ierr); CHKERRQ(ierr) ! pull it all together with additional cli arguments ++ call SNESsetFromOptions(thermal_snes,ierr); CHKERRQ(ierr) ! pull it all together with additional cli arguments -@@ -215,7 +210,7 @@ type(tSolutionState) function spectral_thermal_solution(timeinc,timeinc_old,load + !-------------------------------------------------------------------------------------------------- + ! init fields +- call DMDAGetCorners(thermal_grid,xstart,ystart,zstart,xend,yend,zend,ierr) ++ call DMDAgetCorners(thermal_grid,xstart,ystart,zstart,xend,yend,zend,ierr) + CHKERRQ(ierr) + xend = xstart + xend - 1 + yend = ystart + yend - 1 +@@ -149,9 +146,9 @@ subroutine spectral_thermal_init + temperature_lastInc(i,j,k) = temperature_current(i,j,k) + temperature_stagInc(i,j,k) = temperature_current(i,j,k) + enddo; enddo; enddo +- call DMDAVecGetArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) !< get the data out of PETSc to work with ++ call DMDAvecGetArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) !< get the data out of PETSc to work with + x_scal(xstart:xend,ystart:yend,zstart:zend) = temperature_current +- call DMDAVecRestoreArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) ++ call DMDAvecRestoreArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) + + !-------------------------------------------------------------------------------------------------- + ! thermal reference diffusion update +@@ -205,8 +202,8 @@ type(tSolutionState) function spectral_thermal_solution(timeinc,timeinc_old,load + external :: & + VecMin, & + VecMax, & +- SNESSolve, & +- SNESGetConvergedReason ++ SNESsolve, & ++ SNESgetConvergedReason + + spectral_thermal_solution%converged =.false. + +@@ -215,7 +212,7 @@ type(tSolutionState) function spectral_thermal_solution(timeinc,timeinc_old,load params%timeinc = timeinc params%timeincOld = timeinc_old - call SNESSolve(thermal_snes,PETSC_NULL_OBJECT,solution,ierr); CHKERRQ(ierr) -+ call SNESSolve(thermal_snes,PETSC_NULL_SNES,solution,ierr); CHKERRQ(ierr) ++ call SNESsolve(thermal_snes,PETSC_NULL_VEC,solution,ierr); CHKERRQ(ierr) call SNESGetConvergedReason(thermal_snes,reason,ierr); CHKERRQ(ierr) if (reason < 1) then -@@ -245,14 +240,12 @@ type(tSolutionState) function spectral_thermal_solution(timeinc,timeinc_old,load +@@ -245,14 +242,12 @@ type(tSolutionState) function spectral_thermal_solution(timeinc,timeinc_old,load call VecMin(solution,position,minTemperature,ierr); CHKERRQ(ierr) call VecMax(solution,position,maxTemperature,ierr); CHKERRQ(ierr) @@ -1191,7 +1256,7 @@ index 322f1203..cc0f7678 100644 end function spectral_thermal_solution diff --git a/src/spectral_utilities.f90 b/src/spectral_utilities.f90 -index 1ad25174..bbef337f 100644 +index 1bbf2e60..52bb07fd 100644 --- a/src/spectral_utilities.f90 +++ b/src/spectral_utilities.f90 @@ -5,15 +5,16 @@ @@ -1282,7 +1347,7 @@ index 1ad25174..bbef337f 100644 write(6,'(/,a)') ' ... calculating curl ......................................................' flush(6) -@@ -1096,9 +1089,6 @@ function utilities_forwardField(timeinc,field_lastInc,rate,aim) +@@ -1099,9 +1092,6 @@ function utilities_forwardField(timeinc,field_lastInc,rate,aim) real(pReal), dimension(3,3) :: fieldDiff !< - aim PetscErrorCode :: ierr @@ -1292,7 +1357,7 @@ index 1ad25174..bbef337f 100644 utilities_forwardField = field_lastInc + rate*timeinc if (present(aim)) then !< correct to match average fieldDiff = sum(sum(sum(utilities_forwardField,dim=5),dim=4),dim=3)*wgt -@@ -1190,8 +1180,6 @@ subroutine utilities_updateIPcoords(F) +@@ -1193,8 +1183,6 @@ subroutine utilities_updateIPcoords(F) integer(pInt) :: i, j, k, m, ierr real(pReal), dimension(3) :: step, offset_coords real(pReal), dimension(3,3) :: Favg @@ -1304,225 +1369,3 @@ index 1ad25174..bbef337f 100644 -- 2.15.0 - -From 237f199bbf574bb2509123ce8b037ac751abd15d Mon Sep 17 00:00:00 2001 -From: Martin Diehl -Date: Sun, 5 Nov 2017 13:45:52 +0100 -Subject: [PATCH 2/3] extra function calls for da needed - (https://lists.mcs.anl.gov/pipermail/petsc-users/2017-February/031538.html) - SNESsolve requires PETSC_NULL_VEC not PETSC_NULL_SNES (indicating b=0) - ---- - src/spectral_damage.f90 | 4 +++- - src/spectral_mech_AL.f90 | 5 +++-- - src/spectral_mech_Basic.f90 | 6 +++--- - src/spectral_mech_Polarisation.f90 | 5 +++-- - src/spectral_thermal.f90 | 22 ++++++++++++---------- - 5 files changed, 24 insertions(+), 18 deletions(-) - -diff --git a/src/spectral_damage.f90 b/src/spectral_damage.f90 -index cea6f69c..2c195c56 100644 ---- a/src/spectral_damage.f90 -+++ b/src/spectral_damage.f90 -@@ -114,6 +114,8 @@ subroutine spectral_damage_init() - damage_grid,ierr) !< handle, error - CHKERRQ(ierr) - call SNESSetDM(damage_snes,damage_grid,ierr); CHKERRQ(ierr) !< connect snes to da -+ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) -+ call DMsetUp(da,ierr); CHKERRQ(ierr) - call DMCreateGlobalVector(damage_grid,solution,ierr); CHKERRQ(ierr) !< global solution vector (grid x 1, i.e. every def grad tensor) - call DMDASNESSetFunctionLocal(damage_grid,INSERT_VALUES,spectral_damage_formResidual,& - PETSC_NULL_SNES,ierr) !< residual vector of same shape as solution vector -@@ -204,7 +206,7 @@ type(tSolutionState) function spectral_damage_solution(timeinc,timeinc_old,loadC - params%timeinc = timeinc - params%timeincOld = timeinc_old - -- call SNESSolve(damage_snes,PETSC_NULL_SNES,solution,ierr); CHKERRQ(ierr) -+ call SNESSolve(damage_snes,PETSC_NULL_VEC,solution,ierr); CHKERRQ(ierr) - call SNESGetConvergedReason(damage_snes,reason,ierr); CHKERRQ(ierr) - - if (reason < 1) then -diff --git a/src/spectral_mech_AL.f90 b/src/spectral_mech_AL.f90 -index e7ff0fbe..dc221f6c 100644 ---- a/src/spectral_mech_AL.f90 -+++ b/src/spectral_mech_AL.f90 -@@ -160,6 +160,8 @@ subroutine AL_init - da,ierr) ! handle, error - CHKERRQ(ierr) - call SNESSetDM(snes,da,ierr); CHKERRQ(ierr) -+ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) -+ call DMsetUp(da,ierr); CHKERRQ(ierr) - call DMCreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) - call DMDASNESSetFunctionLocal(da,INSERT_VALUES,AL_formResidual,PETSC_NULL_SNES,ierr) - CHKERRQ(ierr) -@@ -298,8 +300,7 @@ type(tSolutionState) function & - - !-------------------------------------------------------------------------------------------------- - ! solve BVP -- call SNESSolve(snes,PETSC_NULL_SNES,solution_vec,ierr) -- CHKERRQ(ierr) -+ call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) - - !-------------------------------------------------------------------------------------------------- - ! check convergence -diff --git a/src/spectral_mech_Basic.f90 b/src/spectral_mech_Basic.f90 -index b02cfd8c..c335f2d7 100644 ---- a/src/spectral_mech_Basic.f90 -+++ b/src/spectral_mech_Basic.f90 -@@ -147,6 +147,8 @@ subroutine basicPETSc_init - da,ierr) ! handle, error - CHKERRQ(ierr) - call SNESsetDM(snes,da,ierr); CHKERRQ(ierr) -+ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) -+ call DMsetUp(da,ierr); CHKERRQ(ierr) - call DMcreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) ! global solution vector (grid x 9, i.e. every def grad tensor) - call DMDASNESsetFunctionLocal(da,INSERT_VALUES,BasicPETSC_formResidual,PETSC_NULL_SNES,ierr) ! residual vector of same shape as solution vector - CHKERRQ(ierr) -@@ -158,7 +160,6 @@ subroutine basicPETSc_init - !-------------------------------------------------------------------------------------------------- - ! init fields - call DMDAVecGetArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) ! get the data out of PETSc to work with -- - restart: if (restartInc > 1_pInt) then - if (iand(debug_level(debug_spectral),debug_spectralRestart)/= 0) & - write(6,'(/,a,'//IO_intOut(restartInc-1_pInt)//',a)') & -@@ -267,8 +268,7 @@ type(tSolutionState) function & - - !-------------------------------------------------------------------------------------------------- - ! solve BVP -- call SNESSolve(snes,PETSC_NULL_SNES,solution_vec,ierr) -- CHKERRQ(ierr) -+ call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) - - !-------------------------------------------------------------------------------------------------- - ! check convergence -diff --git a/src/spectral_mech_Polarisation.f90 b/src/spectral_mech_Polarisation.f90 -index 2b9dddc0..3b024f56 100644 ---- a/src/spectral_mech_Polarisation.f90 -+++ b/src/spectral_mech_Polarisation.f90 -@@ -160,6 +160,8 @@ subroutine Polarisation_init - da,ierr) ! handle, error - CHKERRQ(ierr) - call SNESsetDM(snes,da,ierr); CHKERRQ(ierr) -+ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) -+ call DMsetUp(da,ierr); CHKERRQ(ierr) - call DMcreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) - call DMDASNESsetFunctionLocal(da,INSERT_VALUES,Polarisation_formResidual,PETSC_NULL_SNES,ierr) - CHKERRQ(ierr) -@@ -298,8 +300,7 @@ type(tSolutionState) function & - - !-------------------------------------------------------------------------------------------------- - ! solve BVP -- call SNESSolve(snes,PETSC_NULL_SNES,solution_vec,ierr) -- CHKERRQ(ierr) -+ call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) - - !-------------------------------------------------------------------------------------------------- - ! check convergence -diff --git a/src/spectral_thermal.f90 b/src/spectral_thermal.f90 -index cc0f7678..7115538c 100644 ---- a/src/spectral_thermal.f90 -+++ b/src/spectral_thermal.f90 -@@ -119,16 +119,18 @@ subroutine spectral_thermal_init - grid (1),grid(2),localK, & ! local grid - thermal_grid,ierr) ! handle, error - CHKERRQ(ierr) -- call SNESSetDM(thermal_snes,thermal_grid,ierr); CHKERRQ(ierr) ! connect snes to da -- call DMCreateGlobalVector(thermal_grid,solution ,ierr); CHKERRQ(ierr) ! global solution vector (grid x 1, i.e. every def grad tensor) -- call DMDASNESSetFunctionLocal(thermal_grid,INSERT_VALUES,spectral_thermal_formResidual,& -+ call SNESsetDM(thermal_snes,thermal_grid,ierr); CHKERRQ(ierr) ! connect snes to da -+ call DMsetFromOptions(da,ierr); CHKERRQ(ierr) -+ call DMsetUp(da,ierr); CHKERRQ(ierr) -+ call DMcreateGlobalVector(thermal_grid,solution,ierr); CHKERRQ(ierr) ! global solution vector (grid x 1, i.e. every def grad tensor) -+ call DMDASNESsetFunctionLocal(thermal_grid,INSERT_VALUES,spectral_thermal_formResidual,& - PETSC_NULL_SNES,ierr) ! residual vector of same shape as solution vector - CHKERRQ(ierr) -- call SNESSetFromOptions(thermal_snes,ierr); CHKERRQ(ierr) ! pull it all together with additional cli arguments -+ call SNESsetFromOptions(thermal_snes,ierr); CHKERRQ(ierr) ! pull it all together with additional cli arguments - - !-------------------------------------------------------------------------------------------------- - ! init fields -- call DMDAGetCorners(thermal_grid,xstart,ystart,zstart,xend,yend,zend,ierr) -+ call DMDAgetCorners(thermal_grid,xstart,ystart,zstart,xend,yend,zend,ierr) - CHKERRQ(ierr) - xend = xstart + xend - 1 - yend = ystart + yend - 1 -@@ -144,9 +146,9 @@ subroutine spectral_thermal_init - temperature_lastInc(i,j,k) = temperature_current(i,j,k) - temperature_stagInc(i,j,k) = temperature_current(i,j,k) - enddo; enddo; enddo -- call DMDAVecGetArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) !< get the data out of PETSc to work with -+ call DMDAvecGetArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) !< get the data out of PETSc to work with - x_scal(xstart:xend,ystart:yend,zstart:zend) = temperature_current -- call DMDAVecRestoreArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) -+ call DMDAvecRestoreArrayF90(thermal_grid,solution,x_scal,ierr); CHKERRQ(ierr) - - !-------------------------------------------------------------------------------------------------- - ! thermal reference diffusion update -@@ -200,8 +202,8 @@ type(tSolutionState) function spectral_thermal_solution(timeinc,timeinc_old,load - external :: & - VecMin, & - VecMax, & -- SNESSolve, & -- SNESGetConvergedReason -+ SNESsolve, & -+ SNESgetConvergedReason - - spectral_thermal_solution%converged =.false. - -@@ -210,7 +212,7 @@ type(tSolutionState) function spectral_thermal_solution(timeinc,timeinc_old,load - params%timeinc = timeinc - params%timeincOld = timeinc_old - -- call SNESSolve(thermal_snes,PETSC_NULL_SNES,solution,ierr); CHKERRQ(ierr) -+ call SNESsolve(thermal_snes,PETSC_NULL_VEC,solution,ierr); CHKERRQ(ierr) - call SNESGetConvergedReason(thermal_snes,reason,ierr); CHKERRQ(ierr) - - if (reason < 1) then --- -2.15.0 - - -From 1af2e332a1b86f388aa9e481255f4405874d7960 Mon Sep 17 00:00:00 2001 -From: Martin Diehl -Date: Sun, 5 Nov 2017 14:18:45 +0100 -Subject: [PATCH 3/3] named better in thermal and damage - ---- - src/spectral_damage.f90 | 4 ++-- - src/spectral_thermal.f90 | 4 ++-- - 2 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/src/spectral_damage.f90 b/src/spectral_damage.f90 -index 2c195c56..11da3b96 100644 ---- a/src/spectral_damage.f90 -+++ b/src/spectral_damage.f90 -@@ -114,8 +114,8 @@ subroutine spectral_damage_init() - damage_grid,ierr) !< handle, error - CHKERRQ(ierr) - call SNESSetDM(damage_snes,damage_grid,ierr); CHKERRQ(ierr) !< connect snes to da -- call DMsetFromOptions(da,ierr); CHKERRQ(ierr) -- call DMsetUp(da,ierr); CHKERRQ(ierr) -+ call DMsetFromOptions(damage_grid,ierr); CHKERRQ(ierr) -+ call DMsetUp(damage_grid,ierr); CHKERRQ(ierr) - call DMCreateGlobalVector(damage_grid,solution,ierr); CHKERRQ(ierr) !< global solution vector (grid x 1, i.e. every def grad tensor) - call DMDASNESSetFunctionLocal(damage_grid,INSERT_VALUES,spectral_damage_formResidual,& - PETSC_NULL_SNES,ierr) !< residual vector of same shape as solution vector -diff --git a/src/spectral_thermal.f90 b/src/spectral_thermal.f90 -index 7115538c..2374d83b 100644 ---- a/src/spectral_thermal.f90 -+++ b/src/spectral_thermal.f90 -@@ -120,8 +120,8 @@ subroutine spectral_thermal_init - thermal_grid,ierr) ! handle, error - CHKERRQ(ierr) - call SNESsetDM(thermal_snes,thermal_grid,ierr); CHKERRQ(ierr) ! connect snes to da -- call DMsetFromOptions(da,ierr); CHKERRQ(ierr) -- call DMsetUp(da,ierr); CHKERRQ(ierr) -+ call DMsetFromOptions(thermal_grid,ierr); CHKERRQ(ierr) -+ call DMsetUp(thermal_grid,ierr); CHKERRQ(ierr) - call DMcreateGlobalVector(thermal_grid,solution,ierr); CHKERRQ(ierr) ! global solution vector (grid x 1, i.e. every def grad tensor) - call DMDASNESsetFunctionLocal(thermal_grid,INSERT_VALUES,spectral_thermal_formResidual,& - PETSC_NULL_SNES,ierr) ! residual vector of same shape as solution vector --- -2.15.0 - diff --git a/installation/patch/README.md b/installation/patch/README.md index cd4549b2b..69b9176ee 100644 --- a/installation/patch/README.md +++ b/installation/patch/README.md @@ -16,3 +16,10 @@ patch -p1 < installation/patch/nameOfPatch * **PETSc-3.8** adjusts all includes nad calls to PETSc to the 3.8.x API This allows to use the current version of PETSc + +## Create patch +commit your changes + +```bash +git format-patch PATH_TO_COMPARE --stdout > +``` From b53a2a65077b19b43773d7db8e9a4aa85b863f07 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 20 Nov 2017 16:59:52 +0100 Subject: [PATCH 06/26] [skip ci] updated version information after successful test of v2.0.1-985-g90c617e --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ed59e5b34..7a1f21f80 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-982-ge4700cd +v2.0.1-985-g90c617e From 37e154de651e714033648968acbe10127a667be6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 20 Nov 2017 23:43:06 +0100 Subject: [PATCH 07/26] preventing division by zero --- src/plastic_disloUCLA.f90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plastic_disloUCLA.f90 b/src/plastic_disloUCLA.f90 index 75e087770..c02a7c4d4 100644 --- a/src/plastic_disloUCLA.f90 +++ b/src/plastic_disloUCLA.f90 @@ -1178,7 +1178,7 @@ end subroutine plastic_disloUCLA_dotState function plastic_disloUCLA_postResults(Tstar_v,Temperature,ipc,ip,el) use prec, only: & tol_math_check, & - dEq + dEq, dNeq0 use math, only: & pi use material, only: & @@ -1445,9 +1445,13 @@ function plastic_disloUCLA_postResults(Tstar_v,Temperature,ipc,ip,el) index_myFamily = sum(lattice_NslipSystem(1:f-1_pInt,ph)) ! at which index starts my family slipSystems2: do i = 1_pInt,plastic_disloUCLA_Nslip(f,instance) j = j + 1_pInt + if (dNeq0(abs(dot_product(Tstar_v,lattice_Sslip_v(:,1,index_myFamily+i,ph))))) then plastic_disloUCLA_postResults(c+j) = & (3.0_pReal*lattice_mu(ph)*plastic_disloUCLA_burgersPerSlipSystem(j,instance))/& (16.0_pReal*pi*abs(dot_product(Tstar_v,lattice_Sslip_v(:,1,index_myFamily+i,ph)))) + else + plastic_disloUCLA_postResults(c+j) = huge(1.0_pReal) + endif plastic_disloUCLA_postResults(c+j)=min(plastic_disloUCLA_postResults(c+j),& state(instance)%mfp_slip(j,of)) enddo slipSystems2; enddo slipFamilies2 From 09a66d918d684c3ab6fe5748becc5cbe9662e9cb Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 21 Nov 2017 09:18:03 +0100 Subject: [PATCH 08/26] (in)equality comparison for double was far too tolerant --- src/prec.f90 | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/prec.f90 b/src/prec.f90 index 0e3b276db..912a02533 100644 --- a/src/prec.f90 +++ b/src/prec.f90 @@ -137,6 +137,7 @@ end subroutine prec_init !> @brief equality comparison for float with double precision ! replaces "==" but for certain (relative) tolerance. Counterpart to dNeq ! https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ +! AlmostEqualRelative !-------------------------------------------------------------------------------------------------- logical elemental pure function dEq(a,b,tol) @@ -153,6 +154,7 @@ end function dEq !> @brief inequality comparison for float with double precision ! replaces "!=" but for certain (relative) tolerance. Counterpart to dEq ! https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ +! AlmostEqualRelative NOT !-------------------------------------------------------------------------------------------------- logical elemental pure function dNeq(a,b,tol) @@ -167,33 +169,35 @@ end function dNeq !-------------------------------------------------------------------------------------------------- !> @brief equality to 0 comparison for float with double precision -! replaces "==0" but for certain (absolute) tolerance. Counterpart to dNeq0 -! https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ +! replaces "==0" but everything not representable as a normal number is treated as 0. Counterpart to dNeq0 +! https://de.mathworks.com/help/matlab/ref/realmin.html +! https://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html !-------------------------------------------------------------------------------------------------- logical elemental pure function dEq0(a,tol) implicit none real(pReal), intent(in) :: a real(pReal), intent(in), optional :: tol - real(pReal), parameter :: eps = 2.220446049250313E-16 ! DBL_EPSILON in C + real(pReal), parameter :: eps = 2.2250738585072014E-308 ! smallest non-denormalized number - dEq0 = merge(.True., .False.,abs(a) <= merge(tol,eps,present(tol))*10.0_pReal) + dEq0 = merge(.True., .False.,abs(a) <= merge(tol,eps,present(tol))) end function dEq0 !-------------------------------------------------------------------------------------------------- !> @brief inequality to 0 comparison for float with double precision -! replaces "!=0" but for certain (absolute) tolerance. Counterpart to dEq0 -! https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ +! replaces "!=0" but everything not representable as a normal number is treated as 0. Counterpart to dEq0 +! https://de.mathworks.com/help/matlab/ref/realmin.html +! https://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html !-------------------------------------------------------------------------------------------------- logical elemental pure function dNeq0(a,tol) implicit none real(pReal), intent(in) :: a real(pReal), intent(in), optional :: tol - real(pReal), parameter :: eps = 2.220446049250313E-16 ! DBL_EPSILON in C + real(pReal), parameter :: eps = 2.2250738585072014E-308 ! smallest non-denormalized number - dNeq0 = merge(.False., .True.,abs(a) <= merge(tol,eps,present(tol))*10.0_pReal) + dNeq0 = merge(.False., .True.,abs(a) <= merge(tol,eps,present(tol))) end function dNeq0 From 4dfb52c79295a24516a7d18be65c925558798bf7 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 20 Nov 2017 13:35:25 +0100 Subject: [PATCH 09/26] unfinished HDF5 should not be part of the next release --- lib/damask/DS_HDF5.xml | 198 ------------------ lib/damask/__init__.py | 18 -- lib/damask/h5table.py | 146 ------------- processing/post/h5_addCalculation.py | 72 ------- processing/post/h5_addCauchy.py | 61 ------ processing/post/h5_addIPFcolor.py | 145 ------------- processing/post/h5_addMises.py | 85 -------- processing/post/h5_addStrainTensors.py | 156 -------------- processing/post/h5_addXdmfWapper.py | 130 ------------ .../post/h5_vtkAddRectilinearGridData.py | 191 ----------------- processing/post/h5_vtkRectilinearGrid.py | 135 ------------ processing/pre/3DRVEfrom2Dang.py | 0 12 files changed, 1337 deletions(-) delete mode 100644 lib/damask/DS_HDF5.xml delete mode 100644 lib/damask/h5table.py delete mode 100755 processing/post/h5_addCalculation.py delete mode 100755 processing/post/h5_addCauchy.py delete mode 100755 processing/post/h5_addIPFcolor.py delete mode 100755 processing/post/h5_addMises.py delete mode 100755 processing/post/h5_addStrainTensors.py delete mode 100755 processing/post/h5_addXdmfWapper.py delete mode 100755 processing/post/h5_vtkAddRectilinearGridData.py delete mode 100755 processing/post/h5_vtkRectilinearGrid.py mode change 100644 => 100755 processing/pre/3DRVEfrom2Dang.py diff --git a/lib/damask/DS_HDF5.xml b/lib/damask/DS_HDF5.xml deleted file mode 100644 index 1277ce8d2..000000000 --- a/lib/damask/DS_HDF5.xml +++ /dev/null @@ -1,198 +0,0 @@ - - - - - attr - / - - store cmd history - - - - attr - / - - - - - - - Scalar - /Geometry/Vx - Geometry - Vector along x define the spectral mesh - - - - Scalar - /Geometry/Vy - Geometry - Vector along y defines the spectral mesh - - - - Scalar - /Geometry/Vz - Geometry - Vector along z defines the spectral mesh - - - - Scalar - /Geometry/ip - Geometry - - - - - Scalar - /Geometry/node - Geometry - - - - - Scalar - /Geometry/grain - Geometry - - - - - Vector - /Geometry/pos - Geometry - - - - - Scalar - /Geometry/elem - Geometry - - - - - - Scalar - /Crystallite/phase - Crystallite - - - - - Scalar - /Crystallite/texture - Crystallite - - - - - Scalar - /Crystallite/volume - Crystallite - - - - - Vector - /Crystallite/orientation - Crystallite - - - - - Vector - /Crystallite/eulerangles - Crystallite - Bunnge Euler angles in degrees - - - - Vector - /Crystallite/grainrotation - Crystallite - - - - - Tensor - /Crystallite/f - Crystallite - deformation gradient (F) - - -

- Tensor - /Crystallite/p - Crystallite - Pikola Kirkhoff stress -

- - - Tensor - /Crystallite/Cauchy - Crystallite - Cauchy stress tensor - - - - Tensor - /Crystallite/lnV - Crystallite - - - - - Scalar - /Crystallite/MisesCauchy - Crystallite - von Mises equivalent of Cauchy stress - - - - Scalar - /Crystallite/MiseslnV - Crystallite - left von Mises strain - - - - - Vector - /Constitutive/resistance_slip - Constitutive - - - - - Vector - /Constitutive/shearrate_slip - Constitutive - - - - - Vector - /Constitutive/resolvedstress_slip - Constitutive - - - - - Scalar - /Constitutive/totalshear - Constitutive - - - - - Matrix - /Constitutive/accumulatedshear_slip - Constitutive - vector contains accumulated shear per slip system - - - - -
\ No newline at end of file diff --git a/lib/damask/__init__.py b/lib/damask/__init__.py index 1875ffdae..5b748ee19 100644 --- a/lib/damask/__init__.py +++ b/lib/damask/__init__.py @@ -3,29 +3,11 @@ """Main aggregator""" import os,sys,time -h5py_flag = os.path.join(os.path.dirname(__file__),'../../.noH5py') -h5py_grace = 7200 # only complain once every 7200 sec (2 hours) -h5py_msg = "h5py module not found." - -now = time.time() - with open(os.path.join(os.path.dirname(__file__),'../../VERSION')) as f: version = f.readline()[:-1] from .environment import Environment # noqa from .asciitable import ASCIItable # noqa -try: - from .h5table import H5Table # noqa - if os.path.exists(h5py_flag): os.remove(h5py_flag) # delete flagging file on success -except ImportError: - if os.path.exists(h5py_flag): - if now - os.path.getmtime(h5py_flag) > h5py_grace: # complain (again) every so-and-so often - sys.stderr.write(h5py_msg+'\n') - with open(h5py_flag, 'a'): - os.utime(h5py_flag,(now,now)) # update flag modification time to "now" - else: - open(h5py_flag, 'a').close() # create flagging file - sys.stderr.write(h5py_msg+'\n') # complain for the first time from .config import Material # noqa from .colormaps import Colormap, Color # noqa diff --git a/lib/damask/h5table.py b/lib/damask/h5table.py deleted file mode 100644 index 67d5853b6..000000000 --- a/lib/damask/h5table.py +++ /dev/null @@ -1,146 +0,0 @@ -# -*- coding: UTF-8 no BOM -*- - -# ----------------------------------------------------------- # -# Ideally the h5py should be enough to serve as the data # -# interface for future DAMASK, but since we are still not # -# sure when this major shift will happen, it seems to be a # -# good idea to provide a interface class that help user ease # -# into using HDF5 as the new daily storage driver. # -# ----------------------------------------------------------- # - -import os -import h5py -import numpy as np -import xml.etree.cElementTree as ET - -# ---------------------------------------------------------------- # -# python 3 has no unicode object, this ensures that the code works # -# on Python 2&3 # -# ---------------------------------------------------------------- # -try: - test = isinstance('test', unicode) -except(NameError): - unicode = str - - -def lables_to_path(label, dsXMLPath=None): - """Read the XML definition file and return the path.""" - if dsXMLPath is None: - # use the default storage layout in DS_HDF5.xml - if "h5table.pyc" in __file__: - dsXMLPath = os.path.abspath(__file__).replace("h5table.pyc", - "DS_HDF5.xml") - else: - dsXMLPath = os.path.abspath(__file__).replace("h5table.py", - "DS_HDF5.xml") - # This current implementation requires that all variables - # stay under the root node, the nesting is defined through the - # h5path. - # Allow new derived data to be put under the root - tree = ET.parse(dsXMLPath) - try: - dataType = tree.find('{}/type'.format(label)).text - h5path = tree.find('{}/h5path'.format(label)).text - except: - dataType = "Scalar" - h5path = "/{}".format(label) # just put it under root - return (dataType, h5path) - - -class H5Table(object): - """ - Lightweight interface class for h5py - - DESCRIPTION - ----------- - Interface/wrapper class for manipulating data in HDF5 with DAMASK - specialized data structure. - --> try to maintain a minimal API design. - PARAMETERS - ---------- - h5f_path: str - Absolute path of the HDF5 file - METHOD - ------ - del_entry() -- Force delete attributes/group/datasets (dangerous) - get_attr() -- Return attributes if possible - add_attr() -- Add NEW attributes to dataset/group (no force overwrite) - get_data() -- Retrieve data in numpy.ndarray - add_data() -- Add dataset to H5 file - get_cmdlog() -- Return the command used to generate the data if possible - NOTE - ---- - 1. As an interface class, it uses the lazy evaluation design - that reads the data only when it is absolutely necessary. - 2. The command line used to generate each new feature is stored with - each dataset as dataset attribute. - - """ - - def __init__(self, h5f_path, new_file=False, dsXMLFile=None): - self.h5f_path = h5f_path - self.dsXMLFile = dsXMLFile - msg = 'Created by H5Talbe from DAMASK' - mode = 'w' if new_file else 'a' - with h5py.File(self.h5f_path, mode) as h5f: - h5f['/'].attrs['description'] = msg - - def del_entry(self, feature_name): - """Delete entry in HDF5 table""" - dataType, h5f_path = lables_to_path(feature_name, - dsXMLPath=self.dsXMLFile) - with h5py.File(self.h5f_path, 'a') as h5f: - del h5f[h5f_path] - - def get_attr(self, attr_name): - dataType, h5f_path = lables_to_path(attr_name, - dsXMLPath=self.dsXMLFile) - with h5py.File(self.h5f_path, 'a') as h5f: - rst_attr = h5f[h5f_path].attrs[attr_name] - return rst_attr - - def add_attr(self, attr_name, attr_data): - dataType, h5f_path = lables_to_path(attr_name, - dsXMLPath=self.dsXMLFile) - with h5py.File(self.h5f_path, 'a') as h5f: - h5f[h5f_path].attrs[attr_name] = attr_data - h5f.flush() - - def get_data(self, feature_name=None): - """Extract dataset from HDF5 table and return it in a numpy array""" - dataType, h5f_path = lables_to_path(feature_name, - dsXMLPath=self.dsXMLFile) - with h5py.File(self.h5f_path, 'a') as h5f: - h5f_dst = h5f[h5f_path] # get the handle for target dataset(table) - rst_data = np.zeros(h5f_dst.shape) - h5f_dst.read_direct(rst_data) - return rst_data - - def add_data(self, feature_name, dataset, cmd_log=None): - """Adding new feature into existing HDF5 file""" - dataType, h5f_path = lables_to_path(feature_name, - dsXMLPath=self.dsXMLFile) - with h5py.File(self.h5f_path, 'a') as h5f: - # NOTE: - # --> If dataset exists, delete the old one so as to write - # a new one. For brand new dataset. For brand new one, - # record its state as fresh in the cmd log. - try: - del h5f[h5f_path] - print("***deleting old {} from {}".format(feature_name,self.h5f_path)) - except: - # if no cmd log, None will used - cmd_log = str(cmd_log) + " [FRESH]" - h5f.create_dataset(h5f_path, data=dataset) - # store the cmd in log is possible - if cmd_log is not None: - h5f[h5f_path].attrs['log'] = str(cmd_log) - h5f.flush() - - def get_cmdlog(self, feature_name): - """Get cmd history used to generate the feature""" - dataType, h5f_path = lables_to_path(feature_name, - dsXMLPath=self.dsXMLFile) - with h5py.File(self.h5f_path, 'a') as h5f: - cmd_logs = h5f[h5f_path].attrs['log'] - return cmd_logs diff --git a/processing/post/h5_addCalculation.py b/processing/post/h5_addCalculation.py deleted file mode 100755 index 0ce1981a1..000000000 --- a/processing/post/h5_addCalculation.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import os -# import re -# import sys -import collections -# import math -import damask -# import numpy as np -from optparse import OptionParser - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName, damask.version]) - - -# ----- Helper functions ----- # -def listify(x): - return x if isinstance(x, collections.Iterable) else [x] - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -usageEx = """ -usage_in_details: - Column labels are tagged by '#label#' in formulas. - Use ';' for ',' in functions. Numpy is available as 'np'. - Special variables: #_row_# -- row index - - Examples: - (1) magnitude of vector -- "np.linalg.norm(#vec#)" - (2) rounded root of row number -- "round(math.sqrt(#_row_#);3)" -""" -desp = "Add or alter column(s) with derived values according to " -desp += "user-defined arithmetic operation between column(s)." - -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]' + usageEx, - description=desp, - version=scriptID) -parser.add_option('-l', '--label', - dest='labels', - action='extend', metavar='', - help='(list of) new column labels') -parser.add_option('-f', '--formula', - dest='formulas', - action='extend', metavar='', - help='(list of) formulas corresponding to labels') -parser.add_option('-c', '--condition', - dest='condition', metavar='string', - help='condition to filter rows') - -parser.set_defaults(condition=None) - -(options, filenames) = parser.parse_args() - -# ----- parse formulas ----- # -for i in range(len(options.formulas)): - options.formulas[i] = options.formulas[i].replace(';', ',') - -# ----- loop over input files ----- # -for name in filenames: - try: - h5f = damask.H5Table(name, new_file=False) - except: - print("!!!Cannot process {}".format(name)) - continue - damask.util.report(scriptName, name) - -# Note: -# --> not immediately needed, come back later diff --git a/processing/post/h5_addCauchy.py b/processing/post/h5_addCauchy.py deleted file mode 100755 index 84145d99d..000000000 --- a/processing/post/h5_addCauchy.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import os -import damask -import numpy as np -from optparse import OptionParser - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName, damask.version]) - - -def getCauchy(f, p): - """Return Cauchy stress for given f and p""" - # [Cauchy] = (1/det(F)) * [P].[F_transpose] - f = f.reshape((3, 3)) - p = p.reshape((3, 3)) - return 1.0/np.linalg.det(f)*np.dot(p, f.T).reshape(9) - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -desp = "Add column(s) containing Cauchy stress based on given column(s)" -desp += "of deformation gradient and first Piola--Kirchhoff stress." -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description=desp, - version=scriptID) -parser.add_option('-f', '--defgrad', - dest='defgrad', - type='string', metavar='string', - help='heading for deformation gradient [%default]') -parser.add_option('-p', '--stress', - dest='stress', - type='string', metavar='string', - help='heading for first Piola--Kirchhoff stress [%default]') - -parser.set_defaults(defgrad='f', - stress='p') - -(options, filenames) = parser.parse_args() - -# ----- loop over input H5 files ----- # -for name in filenames: - try: - h5f = damask.H5Table(name, new_file=False) - except: - continue - damask.util.report(scriptName, name) - - # ----- read in data ----- # - f = h5f.get_data("f") - p = h5f.get_data("p") - - # ----- calculate Cauchy stress ----- # - cauchy = [getCauchy(f_i, p_i) for f_i, p_i in zip(f, p)] - - # ----- write to HDF5 file ----- # - cmd_log = " ".join([scriptID, name]) - h5f.add_data('Cauchy', np.array(cauchy), cmd_log=cmd_log) diff --git a/processing/post/h5_addIPFcolor.py b/processing/post/h5_addIPFcolor.py deleted file mode 100755 index c92483fa5..000000000 --- a/processing/post/h5_addIPFcolor.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import os -import sys -import math -import damask -import numpy as np -from optparse import OptionParser - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName, damask.version]) - -# TODO -# This implementation will have to iterate through the array one -# element at a time, maybe there are some other ways to make this -# faster. - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -desp = "Add RGB color value corresponding to TSL-OIM scheme for IPF." -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description=desp, - version=scriptID) -parser.add_option('-p', '--pole', - dest='pole', - type='float', nargs=3, metavar='float float float', - help='lab frame direction for IPF [%default]') -msg = ', '.join(damask.Symmetry.lattices[1:]) -parser.add_option('-s', '--symmetry', - dest='symmetry', - type='choice', choices=damask.Symmetry.lattices[1:], - metavar='string', - help='crystal symmetry [%default] {{{}}} '.format(msg)) -parser.add_option('-e', '--eulers', - dest='eulers', - type='string', metavar='string', - help='Euler angles label') -parser.add_option('-d', '--degrees', - dest='degrees', - action='store_true', - help='Euler angles are given in degrees [%default]') -parser.add_option('-m', '--matrix', - dest='matrix', - type='string', metavar='string', - help='orientation matrix label') -parser.add_option('-a', - dest='a', - type='string', metavar='string', - help='crystal frame a vector label') -parser.add_option('-b', - dest='b', - type='string', metavar='string', - help='crystal frame b vector label') -parser.add_option('-c', - dest='c', - type='string', metavar='string', - help='crystal frame c vector label') -parser.add_option('-q', '--quaternion', - dest='quaternion', - type='string', metavar='string', - help='quaternion label') - -parser.set_defaults(pole=(0.0, 0.0, 1.0), - symmetry=damask.Symmetry.lattices[-1], - degrees=False) - -(options, filenames) = parser.parse_args() - -# safe guarding to have only one orientation representation -# use dynamic typing to group a,b,c into frame -options.frame = [options.a, options.b, options.c] -input = [options.eulers is not None, - all(options.frame), - options.matrix is not None, - options.quaternion is not None] - -if np.sum(input) != 1: - parser.error('needs exactly one input format.') - -# select input label that was requested (active) -label_active = np.where(input)[0][0] -(label, dim, inputtype) = [(options.eulers, 3, 'eulers'), - (options.frame, [3, 3, 3], 'frame'), - (options.matrix, 9, 'matrix'), - (options.quaternion, 4, 'quaternion')][label_active] - -# rescale degrees to radians -toRadians = math.pi/180.0 if options.degrees else 1.0 - -# only use normalized pole -pole = np.array(options.pole) -pole /= np.linalg.norm(pole) - -# ----- Loop over input files ----- # -for name in filenames: - try: - h5f = damask.H5Table(name, new_file=False) - except: - continue - damask.util.report(scriptName, name) - - # extract data from HDF5 file - if inputtype == 'eulers': - orieData = h5f.get_data(label) - elif inputtype == 'matrix': - orieData = h5f.get_data(label) - orieData = orieData.reshape(orieData.shape[0], 3, 3) - elif inputtype == 'frame': - vctr_a = h5f.get_data(label[0]) - vctr_b = h5f.get_data(label[1]) - vctr_c = h5f.get_data(label[2]) - frame = np.column_stack((vctr_a, vctr_b, vctr_c)) - orieData = frame.reshape(frame.shape[0], 3, 3) - elif inputtype == 'quaternion': - orieData = h5f.get_data(label) - - # calculate the IPF color - rgbArrays = np.zeros((orieData.shape[0], 3)) - for ci in range(rgbArrays.shape[0]): - if inputtype == 'eulers': - o = damask.Orientation(Eulers=np.array(orieData[ci, :])*toRadians, - symmetry=options.symmetry).reduced() - elif inputtype == 'matrix': - o = damask.Orientation(matrix=orieData[ci, :, :].transpose(), - symmetry=options.symmetry).reduced() - elif inputtype == 'frame': - o = damask.Orientation(matrix=orieData[ci, :, :], - symmetry=options.symmetry).reduced() - elif inputtype == 'quaternion': - o = damask.Orientation(quaternion=orieData[ci, :], - symmetry=options.symmetry).reduced() - rgbArrays[ci, :] = o.IPFcolor(pole) - - # compose labels/headers for IPF color (RGB) - labelIPF = 'IPF_{:g}{:g}{:g}_{sym}'.format(*options.pole, - sym=options.symmetry.lower()) - - # compose cmd history (go with dataset) - cmd_log = scriptID + '\t' + ' '.join(sys.argv[1:]) - - # write data to HDF5 file - h5f.add_data(labelIPF, rgbArrays, cmd_log=cmd_log) diff --git a/processing/post/h5_addMises.py b/processing/post/h5_addMises.py deleted file mode 100755 index 99367cd80..000000000 --- a/processing/post/h5_addMises.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import os -import sys -import math -import damask -import numpy as np -from optparse import OptionParser - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName, damask.version]) - - -# ----- Helper functions ----- # -def calcMises(what, tensor): - """Calculate von Mises equivalent""" - dev = tensor - np.trace(tensor)/3.0*np.eye(3) - symdev = 0.5*(dev+dev.T) - return math.sqrt(np.sum(symdev*symdev.T) * - { - 'stress': 3.0/2.0, - 'strain': 2.0/3.0, - }[what.lower()]) - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -desp = "Add von Mises equivalent values for symmetric part of requested" -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description=desp, - version=scriptID) -parser.add_option('-e', '--strain', - dest='strain', - metavar='string', - help='name of dataset containing strain tensors') -parser.add_option('-s', '--stress', - dest='stress', - metavar='string', - help='name of dataset containing stress tensors') - -parser.set_defaults(strain=None, stress=None) - -(options, filenames) = parser.parse_args() - -# ----- Loop over input files ----- # -for name in filenames: - try: - h5f = damask.H5Table(name, new_file=False) - except: - continue - damask.util.report(scriptName, name) - - # TODO: - # Could use some refactoring here - if options.stress is not None: - # extract stress tensor from HDF5 - tnsr = h5f.get_data(options.stress) - - # calculate von Mises equivalent row by row - vmStress = np.zeros(tnsr.shape[0]) - for ri in range(tnsr.shape[0]): - stressTnsr = tnsr[ri, :].reshape(3, 3) - vmStress[ri] = calcMises('stress', stressTnsr) - - # compose label - label = "Mises{}".format(options.stress) - - # prepare log info - cmd_log = scriptID + '\t' + ' '.join(sys.argv[1:]) - - # write data to HDF5 file - h5f.add_data(label, vmStress, cmd_log=cmd_log) - - if options.strain is not None: - tnsr = h5f.get_data(options.strain) - vmStrain = np.zeros(tnsr.shape[0]) - for ri in range(tnsr.shape[0]): - strainTnsr = tnsr[ri, :].reshape(3, 3) - vmStrain[ri] = calcMises('strain', strainTnsr) - label = "Mises{}".format(options.strain) - cmd_log = scriptID + '\t' + ' '.join(sys.argv[1:]) - h5f.add_data(label, vmStrain, cmd_log=cmd_log) diff --git a/processing/post/h5_addStrainTensors.py b/processing/post/h5_addStrainTensors.py deleted file mode 100755 index 9e3f49233..000000000 --- a/processing/post/h5_addStrainTensors.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import os -import sys -import damask -import numpy as np -from optparse import OptionParser - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName, damask.version]) - - -# ----- Helper functions ----- # -def operator(stretch, strain, eigenvalues): - # Albrecht Bertram: Elasticity and Plasticity of Large Deformations - # An Introduction (3rd Edition, 2012), p. 102 - return {'V#ln': np.log(eigenvalues), - 'U#ln': np.log(eigenvalues), - 'V#Biot': (np.ones(3, 'd') - 1.0/eigenvalues), - 'U#Biot': (eigenvalues - np.ones(3, 'd')), - 'V#Green': (np.ones(3, 'd') - 1.0/eigenvalues/eigenvalues)*0.5, - 'U#Green': (eigenvalues*eigenvalues - np.ones(3, 'd'))*0.5, - }[stretch+'#'+strain] - - -def calcEPS(defgrads, stretchType, strainType): - """Calculate specific type of strain tensor""" - eps = np.zeros(defgrads.shape) # initialize container - - # TODO: - # this loop can use some performance boost - # (multi-threading?) - for ri in range(defgrads.shape[0]): - f = defgrads[ri, :].reshape(3, 3) - U, S, Vh = np.linalg.svd(f) - R = np.dot(U, Vh) # rotation of polar decomposition - if stretchType == 'U': - stretch = np.dot(np.linalg.inv(R), f) # F = RU - elif stretchType == 'V': - stretch = np.dot(f, np.linalg.inv(R)) # F = VR - - # kill nasty noisy data - stretch = np.where(abs(stretch) < 1e-12, 0, stretch) - - (D, V) = np.linalg.eig(stretch) - # flip principal component with negative Eigen values - neg = np.where(D < 0.0) - D[neg] *= -1. - V[:, neg] *= -1. - - # check each vector for orthogonality - # --> brutal force enforcing orthogonal base - # and re-normalize - for i, eigval in enumerate(D): - if np.dot(V[:, i], V[:, (i+1) % 3]) != 0.0: - V[:, (i+1) % 3] = np.cross(V[:, (i+2) % 3], V[:, i]) - V[:, (i+1) % 3] /= np.sqrt(np.dot(V[:, (i+1) % 3], - V[:, (i+1) % 3].conj())) - - # calculate requested version of strain tensor - d = operator(stretchType, strainType, D) - eps[ri] = (np.dot(V, np.dot(np.diag(d), V.T)).real).reshape(9) - - return eps - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -desp = "Add column(s) containing given strains based on given stretches" -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description=desp, - version=scriptID) -msg = 'material strains based on right Cauchy-Green deformation, i.e., C and U' -parser.add_option('-u', '--right', - dest='right', - action='store_true', - help=msg) -msg = 'spatial strains based on left Cauchy--Green deformation, i.e., B and V' -parser.add_option('-v', '--left', - dest='left', - action='store_true', - help=msg) -parser.add_option('-0', '--logarithmic', - dest='logarithmic', - action='store_true', - help='calculate logarithmic strain tensor') -parser.add_option('-1', '--biot', - dest='biot', - action='store_true', - help='calculate biot strain tensor') -parser.add_option('-2', '--green', - dest='green', - action='store_true', - help='calculate green strain tensor') -# NOTE: -# It might be easier to just calculate one type of deformation gradient -# at a time. -msg = 'heading(s) of columns containing deformation tensor values' -parser.add_option('-f', '--defgrad', - dest='defgrad', - action='extend', - metavar='', - help=msg) - -parser.set_defaults(right=False, left=False, - logarithmic=False, biot=False, green=False, - defgrad='f') - -(options, filenames) = parser.parse_args() - -stretches = [] -strains = [] - -if options.right: - stretches.append('U') -if options.left: - stretches.append('V') - -if options.logarithmic: - strains.append('ln') -if options.biot: - strains.append('Biot') -if options.green: - strains.append('Green') - -if options.defgrad is None: - parser.error('no data column specified.') - -# ----- Loop over input files ----- # -for name in filenames: - try: - h5f = damask.H5Table(name, new_file=False) - except: - continue - damask.util.report(scriptName, name) - - # extract defgrads from HDF5 storage - F = h5f.get_data(options.defgrad) - - # allow calculate multiple types of strain within the - # same cmd call - for stretchType in stretches: - for strainType in strains: - # calculate strain tensor for this type - eps = calcEPS(F, stretchType, strainType) - - # compose labels/headers for this strain tensor - labelsStrain = strainType + stretchType - - # prepare log info - cmd_log = scriptID + '\t' + ' '.join(sys.argv[1:]) - - # write data to HDF5 file - h5f.add_data(labelsStrain, eps, cmd_log=cmd_log) diff --git a/processing/post/h5_addXdmfWapper.py b/processing/post/h5_addXdmfWapper.py deleted file mode 100755 index e5588a069..000000000 --- a/processing/post/h5_addXdmfWapper.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -# ------------------------------------------------------------------- # -# NOTE: # -# 1. Current Xdmf rendering in Paraview has some memory issue where # -# large number of polyvertices will cause segmentation fault. By # -# default, paraview output a cell based xdmf description, which # -# is working for small and medium mesh (<10,000) points. Hence a # -# rectangular mesh is used as the de facto Geometry description # -# here. # -# 2. Due to the unstable state Xdmf, it is safer to use port data # -# to VTR rather than using xdmf as interpretive layer for data # -# visualization. # -# ------------------------------------------------------------------- # - - -import os -import damask -import h5py -import xml.etree.cElementTree as ET -from optparse import OptionParser -from xml.dom import minidom -from damask.h5table import lables_to_path - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - - -# ----- HELPER FUNCTIONS -----# -def addTopLvlCmt(xmlstr, topLevelCmt): - """Add top level comment to string from ET""" - # a quick hack to add the top level comment to XML file - # --> somehow Elementtree does not provide this functionality - # --> by default - strList = xmlstr.split("\n") - strList[0] += "\n"+topLevelCmt - return "\n".join(strList) - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- - -msg = 'Generate Xdmf wrapper for HDF5 file.' -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description = msg, - version = scriptID) - -(options, filenames) = parser.parse_args() - -h5f = filenames[0] -h5f_base = h5f.split("/")[-1] - -# ----- parse HDF5 file ----- # -h5f_dataDim = {} -h5f_dataPath = {} -h5f_dataType = {} -with h5py.File(h5f, 'a') as f: - labels = f.keys() - labels += f['/Constitutive'].keys() - labels += f['/Crystallite'].keys() - labels += ['Vx', 'Vy', "Vz"] - # remove group names as they do not contain real data - # TODO: use h5py/H5table API to detect dataset name to - # avoid necessary name space pruning. - labels.remove('Constitutive') - labels.remove('Crystallite') - labels.remove('Geometry') - # loop through remaining labels - for label in labels: - dataType, h5Path = lables_to_path(label) - h5f_dataType[label] = dataType - h5f_dataDim[label] = " ".join(map(str,f[h5Path].shape)) - h5f_dataPath[label] = h5Path - -# ----- constructing xdmf elements ----- # -root = ET.Element("Xdmf", version='3.3') -root.set('xmlns:xi', "http://www.w3.org/2001/XInclude") -root.append(ET.Comment('Generated Xdmf wapper for DAMASH H5 output')) - -# usually there should only be ONE domain -domain = ET.SubElement(root, 'Domain', - Name=h5f_base.split(".")[0]) - -# use global topology through reference -grid = ET.SubElement(domain, 'Grid', GridType="Uniform") -# geometry section -geometry = ET.SubElement(grid, 'Geometry', GeometryType="VXVYVZ") -for vector in ["Vz", "Vy", "Vx"]: - dataitem = ET.SubElement(geometry, "DataItem", - DataType="Float", - Dimensions=h5f_dataDim[vector], - Name=vector, - Format="HDF") - dataitem.text = h5f_base.split("/")[-1] + ":{}".format(h5f_dataPath[vector]) -# topology section -# TODO: support for other format based on given option -meshDim = [h5f_dataDim["Vz"], h5f_dataDim["Vy"], h5f_dataDim["Vx"]] -topology = ET.SubElement(grid, 'Topology', - TopologyType="3DRectMesh", - Dimensions=" ".join(map(str, meshDim))) - -# attributes section -# Question: how to properly handle data mapping for multiphase situations -labelsProcessed = ['Vx', 'Vy', 'Vz'] -# walk through each attributes -for label in labels: - if label in labelsProcessed: continue - print("adding {}...".format(label)) - attr = ET.SubElement(grid, 'Attribute', - Name=label, - Type="None", - Center="Cell") - dataitem = ET.SubElement(attr, 'DataItem', - Name=label, - Format='HDF', - Dimensions=h5f_dataDim[label]) - dataitem.text = h5f_base + ":" + h5f_dataPath[label] - # update progress list - labelsProcessed.append(label) - - -# pretty print the xdmf(xml) file content -xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="\t") -xmlstr = addTopLvlCmt(xmlstr, '') -# write str to file through native python API -with open(h5f.replace(".h5", ".xmf"), 'w') as f: - f.write(xmlstr) diff --git a/processing/post/h5_vtkAddRectilinearGridData.py b/processing/post/h5_vtkAddRectilinearGridData.py deleted file mode 100755 index 1c0492f53..000000000 --- a/processing/post/h5_vtkAddRectilinearGridData.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import os -import vtk -import damask -from vtk.util import numpy_support -from optparse import OptionParser - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName, damask.version]) - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -msg = "Add scalars, vectors, and/or an RGB tuple from" -msg += "an HDF5 to existing VTK rectilinear grid (.vtr/.vtk)." -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description=msg, - version=scriptID) -parser.add_option('--vtk', - dest='vtk', - type='string', metavar='string', - help='VTK file name') -parser.add_option('--inplace', - dest='inplace', - action='store_true', - help='modify VTK file in-place') -parser.add_option('-r', '--render', - dest='render', - action='store_true', - help='open output in VTK render window') -parser.add_option('-d', '--data', - dest='data', - action='extend', metavar='', - help='scalar/vector value(s) label(s)') -parser.add_option('-t', '--tensor', - dest='tensor', - action='extend', metavar='', - help='tensor (3x3) value label(s)') -parser.add_option('-c', '--color', - dest='color', - action='extend', metavar='', - help='RGB color tuple label') -parser.add_option('-m', - '--mode', - dest='mode', - metavar='string', - type='choice', choices=['cell', 'point'], - help='cell-centered or point-centered coordinates') - -parser.set_defaults(data=[], - tensor=[], - color=[], - mode='cell', - inplace=False, - render=False) - -(options, filenames) = parser.parse_args() - -# ----- Legacy VTK format support ----- # -if os.path.splitext(options.vtk)[1] == '.vtr': - reader = vtk.vtkXMLRectilinearGridReader() - reader.SetFileName(options.vtk) - reader.Update() - rGrid = reader.GetOutput() -elif os.path.splitext(options.vtk)[1] == '.vtk': - reader = vtk.vtkGenericDataObjectReader() - reader.SetFileName(options.vtk) - reader.Update() - rGrid = reader.GetRectilinearGridOutput() -else: - parser.error('Unsupported VTK file type extension.') - -Npoints = rGrid.GetNumberOfPoints() -Ncells = rGrid.GetNumberOfCells() - -# ----- Summary output (Sanity Check) ----- # -msg = '{}: {} points and {} cells...'.format(options.vtk, - Npoints, - Ncells) -damask.util.croak(msg) - -# ----- Read HDF5 file ----- # -# NOTE: -# --> It is possible in the future we are trying to add data -# from different increment into the same VTK file, but -# this feature is not supported for the moment. -# --> Let it fail, if the HDF5 is invalid, python interpretor -# --> should be able to catch this error. -h5f = damask.H5Table(filenames[0], new_file=False) - -# ----- Process data ----- # -featureToAdd = {'data': options.data, - 'tensor': options.tensor, - 'color': options.color} -VTKarray = {} # store all vtkData in dict, then ship them to file -for dataType in featureToAdd.keys(): - featureNames = featureToAdd[dataType] - for featureName in featureNames: - VTKtype = vtk.VTK_DOUBLE - VTKdata = h5f.get_data(featureName) - if dataType == 'color': - VTKtype = vtk.VTK_UNSIGNED_CHAR - VTKdata = (VTKdata*255).astype(int) - elif dataType == 'tensor': - # Force symmetries tensor type data - VTKdata[:, 1] = VTKdata[:, 3] = 0.5*(VTKdata[:, 1]+VTKdata[:, 3]) - VTKdata[:, 2] = VTKdata[:, 6] = 0.5*(VTKdata[:, 2]+VTKdata[:, 6]) - VTKdata[:, 5] = VTKdata[:, 7] = 0.5*(VTKdata[:, 5]+VTKdata[:, 7]) - # use vtk build-in numpy support to add data (much faster) - # NOTE: - # --> deep copy is necessary here, otherwise memory leak could occur - VTKarray[featureName] = numpy_support.numpy_to_vtk(num_array=VTKdata, - deep=True, - array_type=VTKtype) - VTKarray[featureName].SetName(featureName) - -# ----- ship data to vtkGrid ----- # -mode = options.mode -damask.util.croak('{} mode...'.format(mode)) - -# NOTE: -# --> For unknown reason, Paraview only recognize one -# tensor attributes per cell, thus it would be safe -# to only add one attributes as tensor. -for dataType in featureToAdd.keys(): - featureNames = featureToAdd[dataType] - for featureName in featureNames: - if dataType == 'color': - if mode == 'cell': - rGrid.GetCellData().SetScalars(VTKarray[featureName]) - elif mode == 'point': - rGrid.GetPointData().SetScalars(VTKarray[featureName]) - elif dataType == 'tensor': - if mode == 'cell': - rGrid.GetCellData().SetTensors(VTKarray[featureName]) - elif mode == 'point': - rGrid.GetPointData().SetTensors(VTKarray[featureName]) - else: - if mode == 'cell': - rGrid.GetCellData().AddArray(VTKarray[featureName]) - elif mode == 'point': - rGrid.GetPointData().AddArray(VTKarray[featureName]) - -rGrid.Modified() -if vtk.VTK_MAJOR_VERSION <= 5: - rGrid.Update() - -# ----- write Grid to VTK file ----- # -writer = vtk.vtkXMLRectilinearGridWriter() -writer.SetDataModeToBinary() -writer.SetCompressorTypeToZLib() -vtkFileN = os.path.splitext(options.vtk)[0] -vtkExtsn = '.vtr' if options.inplace else '_added.vtr' -writer.SetFileName(vtkFileN+vtkExtsn) -if vtk.VTK_MAJOR_VERSION <= 5: - writer.SetInput(rGrid) -else: - writer.SetInputData(rGrid) -writer.Write() - -# ----- render results from script ----- # -if options.render: - mapper = vtk.vtkDataSetMapper() - mapper.SetInputData(rGrid) - actor = vtk.vtkActor() - actor.SetMapper(mapper) - - # Create the graphics structure. The renderer renders into the - # render window. The render window interactor captures mouse events - # and will perform appropriate camera or actor manipulation - # depending on the nature of the events. - - ren = vtk.vtkRenderer() - - renWin = vtk.vtkRenderWindow() - renWin.AddRenderer(ren) - - ren.AddActor(actor) - ren.SetBackground(1, 1, 1) - renWin.SetSize(200, 200) - - iren = vtk.vtkRenderWindowInteractor() - iren.SetRenderWindow(renWin) - - iren.Initialize() - renWin.Render() - iren.Start() diff --git a/processing/post/h5_vtkRectilinearGrid.py b/processing/post/h5_vtkRectilinearGrid.py deleted file mode 100755 index b08070b84..000000000 --- a/processing/post/h5_vtkRectilinearGrid.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -# ------------------------------------------------------------------ # -# NOTE: # -# 1. It might be a good idea to separate IO and calculation. # -# 2. Some of the calculation could be useful in other situations, # -# why not build a math_util, or math_sup module that contains # -# all the useful functions. # -# ------------------------------------------------------------------ # - -import os -import vtk -import numpy as np -import damask -from optparse import OptionParser - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName, damask.version]) - - -# ----- HELPER FUNCTION ----- # -def getMeshFromXYZ(xyzArray, mode): - """Calc Vx,Vy,Vz vectors for vtk rectangular mesh""" - # NOTE: - # --> np.unique will automatically sort the list - # --> although not exactly n(1), but since mesh dimension should - # small anyway, so this is still light weight. - dim = xyzArray.shape[1] # 2D:2, 3D:3 - coords = [np.unique(xyzArray[:, i]) for i in range(dim)] - - if mode == 'cell': - # since x, y, z might now have the same number of elements, - # we have to deal with them individually - for ri in range(dim): - vctr_pt = coords[ri] - vctr_cell = np.empty(len(vctr_pt)+1) - # calculate first and last end point - vctr_cell[0] = vctr_pt[0] - 0.5*abs(vctr_pt[1] - vctr_pt[0]) - vctr_cell[-1] = vctr_pt[-1] + 0.5*abs(vctr_pt[-2] - vctr_pt[-1]) - for cj in range(1, len(vctr_cell)-1): - vctr_cell[cj] = 0.5*(vctr_pt[cj-1] + vctr_pt[cj]) - # update the coords - coords[ri] = vctr_cell - - if dim < 3: - coords.append([0]) # expand to a 3D with 0 for z - - # auxiliary description - grid = np.array(map(len, coords), 'i') - N = grid.prod() if mode == 'point' else (grid-1).prod() - return coords, grid, N - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- - -msg = "Create regular voxel grid from points in an ASCIItable." -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description=msg, - version=scriptID) - -parser.add_option('-m', - '--mode', - dest='mode', - metavar='string', - type='choice', choices=['cell', 'point'], - help='cell-centered or point-centered coordinates') -parser.add_option('-p', - '--pos', '--position', - dest='pos', - type='string', metavar='string', - help='label of coordinates [%default]') - -parser.set_defaults(mode='cell', - pos='pos') - -(options, filenames) = parser.parse_args() - -# ----- loop over input files ----- # -for name in filenames: - try: - h5f = damask.H5Table(name, new_file=False) - except: - continue - damask.util.report(scriptName, name) - - # ----- read xyzArray from HDF5 file ----- # - xyzArray = h5f.get_data(options.pos) - - # ----- figure out size and grid ----- # - coords, grid, N = getMeshFromXYZ(xyzArray, options.mode) - - # ----- process data ----- # - rGrid = vtk.vtkRectilinearGrid() - # WARNING: list expansion does not work here as these are - # just pointers for a vtk instance. Simply put, - # DON't USE - # [] * - coordArray = [vtk.vtkDoubleArray(), - vtk.vtkDoubleArray(), - vtk.vtkDoubleArray()] - - rGrid.SetDimensions(*grid) - for i, points in enumerate(coords): - for point in points: - coordArray[i].InsertNextValue(point) - - rGrid.SetXCoordinates(coordArray[0]) - rGrid.SetYCoordinates(coordArray[1]) - rGrid.SetZCoordinates(coordArray[2]) - - # ----- output result ----- # - dirPath = os.path.split(name)[0] - if name: - writer = vtk.vtkXMLRectilinearGridWriter() - writer.SetCompressorTypeToZLib() - writer.SetDataModeToBinary() - # getting the name is a little bit tricky - vtkFileName = os.path.splitext(os.path.split(name)[1])[0] - vtkFileName += '_{}({})'.format(options.pos, options.mode) - vtkFileName += '.' + writer.GetDefaultFileExtension() - writer.SetFileName(os.path.join(dirPath, vtkFileName)) - else: - writer = vtk.vtkDataSetWriter() - writer.SetHeader('# powered by '+scriptID) - writer.WriteToOutputStringOn() - - if vtk.VTK_MAJOR_VERSION <= 5: - writer.SetInput(rGrid) - else: - writer.SetInputData(rGrid) - - writer.Write() diff --git a/processing/pre/3DRVEfrom2Dang.py b/processing/pre/3DRVEfrom2Dang.py old mode 100644 new mode 100755 From f52721e0a74719b15ed47680b6859c8ac2581376 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 21 Nov 2017 11:41:02 +0100 Subject: [PATCH 10/26] unneded modules --- lib/damask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/damask/__init__.py b/lib/damask/__init__.py index 5b748ee19..379b23547 100644 --- a/lib/damask/__init__.py +++ b/lib/damask/__init__.py @@ -1,7 +1,7 @@ # -*- coding: UTF-8 no BOM -*- """Main aggregator""" -import os,sys,time +import os with open(os.path.join(os.path.dirname(__file__),'../../VERSION')) as f: version = f.readline()[:-1] From 8d705522ea7e0ff76d279637c6d21295fff36b4f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 21 Nov 2017 15:08:45 +0100 Subject: [PATCH 11/26] output already disabled for worldrank !=0 --- src/constitutive.f90 | 6 +++--- src/damage_local.f90 | 8 ++------ src/damage_none.f90 | 8 ++------ src/damage_nonlocal.f90 | 8 ++------ src/homogenization_RGC.f90 | 8 ++------ src/homogenization_isostrain.f90 | 8 ++------ src/homogenization_none.f90 | 10 +++------- src/hydrogenflux_cahnhilliard.f90 | 8 ++------ src/hydrogenflux_isoconc.f90 | 10 +++------- src/kinematics_cleavage_opening.f90 | 8 ++------ src/kinematics_slipplane_opening.f90 | 8 ++------ src/kinematics_thermal_expansion.f90 | 8 ++------ src/kinematics_vacancy_strain.f90 | 8 ++------ 13 files changed, 29 insertions(+), 77 deletions(-) diff --git a/src/constitutive.f90 b/src/constitutive.f90 index 202242aec..f124e545b 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -186,11 +186,11 @@ subroutine constitutive_init() if (any(phase_kinematics == KINEMATICS_hydrogen_strain_ID)) call kinematics_hydrogen_strain_init(FILEUNIT) close(FILEUNIT) - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- constitutive init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- constitutive init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" + mainProcess: if (worldrank == 0) then !-------------------------------------------------------------------------------------------------- ! write description file for constitutive output call IO_write_jobFile(FILEUNIT,'outputConstitutive') diff --git a/src/damage_local.f90 b/src/damage_local.f90 index a24f0b1a5..2f3014937 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -72,8 +72,6 @@ subroutine damage_local_init(fileUnit) damage, & damage_initialPhi, & material_partHomogenization - use numerics,only: & - worldrank implicit none integer(pInt), intent(in) :: fileUnit @@ -86,11 +84,9 @@ subroutine damage_local_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(damage_type == DAMAGE_local_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/damage_none.f90 b/src/damage_none.f90 index 746de340c..4750f5949 100644 --- a/src/damage_none.f90 +++ b/src/damage_none.f90 @@ -26,19 +26,15 @@ subroutine damage_none_init() use IO, only: & IO_timeStamp use material - use numerics, only: & - worldrank implicit none integer(pInt) :: & homog, & NofMyHomog - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_none_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_none_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess initializeInstances: do homog = 1_pInt, material_Nhomogenization diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index fb960ed7f..cd6ba8a5b 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -77,8 +77,6 @@ subroutine damage_nonlocal_init(fileUnit) damage, & damage_initialPhi, & material_partHomogenization - use numerics,only: & - worldrank implicit none integer(pInt), intent(in) :: fileUnit @@ -91,11 +89,9 @@ subroutine damage_nonlocal_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(damage_type == DAMAGE_nonlocal_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/homogenization_RGC.f90 b/src/homogenization_RGC.f90 index 43c16a39d..84cb594db 100644 --- a/src/homogenization_RGC.f90 +++ b/src/homogenization_RGC.f90 @@ -100,8 +100,6 @@ subroutine homogenization_RGC_init(fileUnit) FE_geomtype use IO use material - use numerics, only: & - worldrank implicit none integer(pInt), intent(in) :: fileUnit !< file pointer to material configuration @@ -117,11 +115,9 @@ subroutine homogenization_RGC_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_RGC_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_RGC_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(homogenization_type == HOMOGENIZATION_RGC_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/homogenization_isostrain.f90 b/src/homogenization_isostrain.f90 index aeb77c275..055bfbb46 100644 --- a/src/homogenization_isostrain.f90 +++ b/src/homogenization_isostrain.f90 @@ -62,8 +62,6 @@ subroutine homogenization_isostrain_init(fileUnit) debug_levelBasic use IO use material - use numerics, only: & - worldrank implicit none integer(pInt), intent(in) :: fileUnit @@ -80,11 +78,9 @@ subroutine homogenization_isostrain_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_ISOSTRAIN_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_ISOSTRAIN_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = count(homogenization_type == HOMOGENIZATION_ISOSTRAIN_ID) if (maxNinstance == 0) return diff --git a/src/homogenization_none.f90 b/src/homogenization_none.f90 index 11bed7813..75d8bcd3a 100644 --- a/src/homogenization_none.f90 +++ b/src/homogenization_none.f90 @@ -29,21 +29,17 @@ subroutine homogenization_none_init() use IO, only: & IO_timeStamp use material - use numerics, only: & - worldrank implicit none integer(pInt) :: & homog, & NofMyHomog - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_NONE_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_NONE_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess - initializeInstances: do homog = 1_pInt, material_Nhomogenization + initializeInstances: do homog = 1_pInt, material_Nhomogenization myhomog: if (homogenization_type(homog) == HOMOGENIZATION_none_ID) then NofMyHomog = count(material_homog == homog) diff --git a/src/hydrogenflux_cahnhilliard.f90 b/src/hydrogenflux_cahnhilliard.f90 index db08bf5d8..89479a9c9 100644 --- a/src/hydrogenflux_cahnhilliard.f90 +++ b/src/hydrogenflux_cahnhilliard.f90 @@ -84,8 +84,6 @@ subroutine hydrogenflux_cahnhilliard_init(fileUnit) hydrogenflux_initialCh, & material_partHomogenization, & material_partPhase - use numerics,only: & - worldrank implicit none integer(pInt), intent(in) :: fileUnit @@ -98,11 +96,9 @@ subroutine hydrogenflux_cahnhilliard_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_cahnhilliard_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_cahnhilliard_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(hydrogenflux_type == HYDROGENFLUX_cahnhilliard_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/hydrogenflux_isoconc.f90 b/src/hydrogenflux_isoconc.f90 index df5c01e68..bef2a8437 100644 --- a/src/hydrogenflux_isoconc.f90 +++ b/src/hydrogenflux_isoconc.f90 @@ -27,21 +27,17 @@ subroutine hydrogenflux_isoconc_init() use IO, only: & IO_timeStamp use material - use numerics, only: & - worldrank implicit none integer(pInt) :: & homog, & NofMyHomog - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_isoconc_label//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_isoconc_label//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess - initializeInstances: do homog = 1_pInt, material_Nhomogenization + initializeInstances: do homog = 1_pInt, material_Nhomogenization myhomog: if (hydrogenflux_type(homog) == HYDROGENFLUX_isoconc_ID) then NofMyHomog = count(material_homog == homog) diff --git a/src/kinematics_cleavage_opening.f90 b/src/kinematics_cleavage_opening.f90 index 146918f5c..fffa26165 100644 --- a/src/kinematics_cleavage_opening.f90 +++ b/src/kinematics_cleavage_opening.f90 @@ -81,8 +81,6 @@ subroutine kinematics_cleavage_opening_init(fileUnit) KINEMATICS_cleavage_opening_ID, & material_Nphase, & MATERIAL_partPhase - use numerics,only: & - worldrank use lattice, only: & lattice_maxNcleavageFamily, & lattice_NcleavageSystem @@ -97,11 +95,9 @@ subroutine kinematics_cleavage_opening_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_cleavage_opening_LABEL//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_cleavage_opening_LABEL//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(phase_kinematics == KINEMATICS_cleavage_opening_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/kinematics_slipplane_opening.f90 b/src/kinematics_slipplane_opening.f90 index f32efa929..07b98aa23 100644 --- a/src/kinematics_slipplane_opening.f90 +++ b/src/kinematics_slipplane_opening.f90 @@ -81,8 +81,6 @@ subroutine kinematics_slipplane_opening_init(fileUnit) KINEMATICS_slipplane_opening_ID, & material_Nphase, & MATERIAL_partPhase - use numerics,only: & - worldrank use lattice, only: & lattice_maxNslipFamily, & lattice_NslipSystem @@ -97,11 +95,9 @@ subroutine kinematics_slipplane_opening_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_slipplane_opening_LABEL//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_slipplane_opening_LABEL//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(phase_kinematics == KINEMATICS_slipplane_opening_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/kinematics_thermal_expansion.f90 b/src/kinematics_thermal_expansion.f90 index 30c267d34..e7cbca673 100644 --- a/src/kinematics_thermal_expansion.f90 +++ b/src/kinematics_thermal_expansion.f90 @@ -71,8 +71,6 @@ subroutine kinematics_thermal_expansion_init(fileUnit) KINEMATICS_thermal_expansion_ID, & material_Nphase, & MATERIAL_partPhase - use numerics,only: & - worldrank implicit none integer(pInt), intent(in) :: fileUnit @@ -83,11 +81,9 @@ subroutine kinematics_thermal_expansion_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_thermal_expansion_LABEL//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_thermal_expansion_LABEL//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(phase_kinematics == KINEMATICS_thermal_expansion_ID),pInt) if (maxNinstance == 0_pInt) return diff --git a/src/kinematics_vacancy_strain.f90 b/src/kinematics_vacancy_strain.f90 index 791c0e3c1..9558f506d 100644 --- a/src/kinematics_vacancy_strain.f90 +++ b/src/kinematics_vacancy_strain.f90 @@ -71,8 +71,6 @@ subroutine kinematics_vacancy_strain_init(fileUnit) KINEMATICS_vacancy_strain_ID, & material_Nphase, & MATERIAL_partPhase - use numerics,only: & - worldrank implicit none integer(pInt), intent(in) :: fileUnit @@ -83,11 +81,9 @@ subroutine kinematics_vacancy_strain_init(fileUnit) tag = '', & line = '' - mainProcess: if (worldrank == 0) then - write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_vacancy_strain_LABEL//' init -+>>>' - write(6,'(a15,a)') ' Current time: ',IO_timeStamp() + write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_vacancy_strain_LABEL//' init -+>>>' + write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" - endif mainProcess maxNinstance = int(count(phase_kinematics == KINEMATICS_vacancy_strain_ID),pInt) if (maxNinstance == 0_pInt) return From 20d8133fa9d16b855c22f340d880a7fd5d9219e3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 21 Nov 2017 15:15:55 +0100 Subject: [PATCH 12/26] diff for changes only directly related to PETSc 3.8 --- installation/patch/PETSc3.8 | 387 ++---------------------------------- 1 file changed, 14 insertions(+), 373 deletions(-) diff --git a/installation/patch/PETSc3.8 b/installation/patch/PETSc3.8 index c7008fffa..f66ee6d09 100644 --- a/installation/patch/PETSc3.8 +++ b/installation/patch/PETSc3.8 @@ -1,33 +1,20 @@ -From 1cc8e60d02323c3c9448df73bfc8f36f697e939a Mon Sep 17 00:00:00 2001 +From 87e307a9c511f3f40598edbd5996297d7804ce62 Mon Sep 17 00:00:00 2001 From: Martin Diehl -Date: Mon, 20 Nov 2017 12:53:47 +0100 -Subject: [PATCH] existing patch + further fixes +Date: Tue, 21 Nov 2017 15:12:04 +0100 +Subject: [PATCH] due to changes in interface of PETSc --- - src/DAMASK_spectral.f90 | 27 +++++-------- - src/constitutive.f90 | 6 +-- - src/damage_local.f90 | 8 +--- - src/damage_none.f90 | 8 +--- - src/damage_nonlocal.f90 | 8 +--- - src/homogenization_RGC.f90 | 8 +--- - src/homogenization_isostrain.f90 | 8 +--- - src/homogenization_none.f90 | 10 ++--- - src/hydrogenflux_cahnhilliard.f90 | 8 +--- - src/hydrogenflux_isoconc.f90 | 10 ++--- - src/kinematics_cleavage_opening.f90 | 8 +--- - src/kinematics_slipplane_opening.f90 | 8 +--- - src/kinematics_thermal_expansion.f90 | 8 +--- - src/kinematics_vacancy_strain.f90 | 8 +--- - src/mesh.f90 | 12 +++--- - src/numerics.f90 | 13 +++---- - src/spectral_damage.f90 | 39 ++++++------------- - src/spectral_interface.f90 | 31 ++++++++------- - src/spectral_mech_AL.f90 | 46 ++++++++-------------- - src/spectral_mech_Basic.f90 | 52 +++++++++---------------- - src/spectral_mech_Polarisation.f90 | 52 +++++++++---------------- - src/spectral_thermal.f90 | 75 +++++++++++++++++------------------- - src/spectral_utilities.f90 | 34 ++++++---------- - 23 files changed, 175 insertions(+), 312 deletions(-) + src/DAMASK_spectral.f90 | 27 +++++--------- + src/mesh.f90 | 12 +++--- + src/numerics.f90 | 13 +++---- + src/spectral_damage.f90 | 39 ++++++-------------- + src/spectral_interface.f90 | 31 ++++++++-------- + src/spectral_mech_AL.f90 | 46 ++++++++--------------- + src/spectral_mech_Basic.f90 | 52 +++++++++----------------- + src/spectral_mech_Polarisation.f90 | 52 ++++++++++---------------- + src/spectral_thermal.f90 | 75 ++++++++++++++++++-------------------- + src/spectral_utilities.f90 | 34 ++++++----------- + 10 files changed, 146 insertions(+), 235 deletions(-) diff --git a/src/DAMASK_spectral.f90 b/src/DAMASK_spectral.f90 index f32bfb7b..c315b1b8 100644 @@ -105,352 +92,6 @@ index f32bfb7b..c315b1b8 100644 MPI_DOUBLE, MPI_STATUS_IGNORE, ierr) if(ierr /=0_pInt) call IO_error(894_pInt, ext_msg='MPI_file_write') enddo -diff --git a/src/constitutive.f90 b/src/constitutive.f90 -index 202242ae..f124e545 100644 ---- a/src/constitutive.f90 -+++ b/src/constitutive.f90 -@@ -186,11 +186,11 @@ subroutine constitutive_init() - if (any(phase_kinematics == KINEMATICS_hydrogen_strain_ID)) call kinematics_hydrogen_strain_init(FILEUNIT) - close(FILEUNIT) - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- constitutive init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- constitutive init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" - -+ mainProcess: if (worldrank == 0) then - !-------------------------------------------------------------------------------------------------- - ! write description file for constitutive output - call IO_write_jobFile(FILEUNIT,'outputConstitutive') -diff --git a/src/damage_local.f90 b/src/damage_local.f90 -index a24f0b1a..2f301493 100644 ---- a/src/damage_local.f90 -+++ b/src/damage_local.f90 -@@ -72,8 +72,6 @@ subroutine damage_local_init(fileUnit) - damage, & - damage_initialPhi, & - material_partHomogenization -- use numerics,only: & -- worldrank - - implicit none - integer(pInt), intent(in) :: fileUnit -@@ -86,11 +84,9 @@ subroutine damage_local_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(damage_type == DAMAGE_local_ID),pInt) - if (maxNinstance == 0_pInt) return -diff --git a/src/damage_none.f90 b/src/damage_none.f90 -index 746de340..4750f594 100644 ---- a/src/damage_none.f90 -+++ b/src/damage_none.f90 -@@ -26,19 +26,15 @@ subroutine damage_none_init() - use IO, only: & - IO_timeStamp - use material -- use numerics, only: & -- worldrank - - implicit none - integer(pInt) :: & - homog, & - NofMyHomog - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_none_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_none_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - initializeInstances: do homog = 1_pInt, material_Nhomogenization - -diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 -index fb960ed7..cd6ba8a5 100644 ---- a/src/damage_nonlocal.f90 -+++ b/src/damage_nonlocal.f90 -@@ -77,8 +77,6 @@ subroutine damage_nonlocal_init(fileUnit) - damage, & - damage_initialPhi, & - material_partHomogenization -- use numerics,only: & -- worldrank - - implicit none - integer(pInt), intent(in) :: fileUnit -@@ -91,11 +89,9 @@ subroutine damage_nonlocal_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(damage_type == DAMAGE_nonlocal_ID),pInt) - if (maxNinstance == 0_pInt) return -diff --git a/src/homogenization_RGC.f90 b/src/homogenization_RGC.f90 -index 43c16a39..84cb594d 100644 ---- a/src/homogenization_RGC.f90 -+++ b/src/homogenization_RGC.f90 -@@ -100,8 +100,6 @@ subroutine homogenization_RGC_init(fileUnit) - FE_geomtype - use IO - use material -- use numerics, only: & -- worldrank - - implicit none - integer(pInt), intent(in) :: fileUnit !< file pointer to material configuration -@@ -117,11 +115,9 @@ subroutine homogenization_RGC_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_RGC_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_RGC_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(homogenization_type == HOMOGENIZATION_RGC_ID),pInt) - if (maxNinstance == 0_pInt) return -diff --git a/src/homogenization_isostrain.f90 b/src/homogenization_isostrain.f90 -index aeb77c27..055bfbb4 100644 ---- a/src/homogenization_isostrain.f90 -+++ b/src/homogenization_isostrain.f90 -@@ -62,8 +62,6 @@ subroutine homogenization_isostrain_init(fileUnit) - debug_levelBasic - use IO - use material -- use numerics, only: & -- worldrank - - implicit none - integer(pInt), intent(in) :: fileUnit -@@ -80,11 +78,9 @@ subroutine homogenization_isostrain_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_ISOSTRAIN_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_ISOSTRAIN_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = count(homogenization_type == HOMOGENIZATION_ISOSTRAIN_ID) - if (maxNinstance == 0) return -diff --git a/src/homogenization_none.f90 b/src/homogenization_none.f90 -index 11bed781..75d8bcd3 100644 ---- a/src/homogenization_none.f90 -+++ b/src/homogenization_none.f90 -@@ -29,21 +29,17 @@ subroutine homogenization_none_init() - use IO, only: & - IO_timeStamp - use material -- use numerics, only: & -- worldrank - - implicit none - integer(pInt) :: & - homog, & - NofMyHomog - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_NONE_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_NONE_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - -- initializeInstances: do homog = 1_pInt, material_Nhomogenization -+ initializeInstances: do homog = 1_pInt, material_Nhomogenization - - myhomog: if (homogenization_type(homog) == HOMOGENIZATION_none_ID) then - NofMyHomog = count(material_homog == homog) -diff --git a/src/hydrogenflux_cahnhilliard.f90 b/src/hydrogenflux_cahnhilliard.f90 -index db08bf5d..89479a9c 100644 ---- a/src/hydrogenflux_cahnhilliard.f90 -+++ b/src/hydrogenflux_cahnhilliard.f90 -@@ -84,8 +84,6 @@ subroutine hydrogenflux_cahnhilliard_init(fileUnit) - hydrogenflux_initialCh, & - material_partHomogenization, & - material_partPhase -- use numerics,only: & -- worldrank - - implicit none - integer(pInt), intent(in) :: fileUnit -@@ -98,11 +96,9 @@ subroutine hydrogenflux_cahnhilliard_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_cahnhilliard_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_cahnhilliard_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(hydrogenflux_type == HYDROGENFLUX_cahnhilliard_ID),pInt) - if (maxNinstance == 0_pInt) return -diff --git a/src/hydrogenflux_isoconc.f90 b/src/hydrogenflux_isoconc.f90 -index df5c01e6..bef2a843 100644 ---- a/src/hydrogenflux_isoconc.f90 -+++ b/src/hydrogenflux_isoconc.f90 -@@ -27,21 +27,17 @@ subroutine hydrogenflux_isoconc_init() - use IO, only: & - IO_timeStamp - use material -- use numerics, only: & -- worldrank - - implicit none - integer(pInt) :: & - homog, & - NofMyHomog - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_isoconc_label//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- hydrogenflux_'//HYDROGENFLUX_isoconc_label//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - -- initializeInstances: do homog = 1_pInt, material_Nhomogenization -+ initializeInstances: do homog = 1_pInt, material_Nhomogenization - - myhomog: if (hydrogenflux_type(homog) == HYDROGENFLUX_isoconc_ID) then - NofMyHomog = count(material_homog == homog) -diff --git a/src/kinematics_cleavage_opening.f90 b/src/kinematics_cleavage_opening.f90 -index 146918f5..fffa2616 100644 ---- a/src/kinematics_cleavage_opening.f90 -+++ b/src/kinematics_cleavage_opening.f90 -@@ -81,8 +81,6 @@ subroutine kinematics_cleavage_opening_init(fileUnit) - KINEMATICS_cleavage_opening_ID, & - material_Nphase, & - MATERIAL_partPhase -- use numerics,only: & -- worldrank - use lattice, only: & - lattice_maxNcleavageFamily, & - lattice_NcleavageSystem -@@ -97,11 +95,9 @@ subroutine kinematics_cleavage_opening_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_cleavage_opening_LABEL//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_cleavage_opening_LABEL//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(phase_kinematics == KINEMATICS_cleavage_opening_ID),pInt) - if (maxNinstance == 0_pInt) return -diff --git a/src/kinematics_slipplane_opening.f90 b/src/kinematics_slipplane_opening.f90 -index f32efa92..07b98aa2 100644 ---- a/src/kinematics_slipplane_opening.f90 -+++ b/src/kinematics_slipplane_opening.f90 -@@ -81,8 +81,6 @@ subroutine kinematics_slipplane_opening_init(fileUnit) - KINEMATICS_slipplane_opening_ID, & - material_Nphase, & - MATERIAL_partPhase -- use numerics,only: & -- worldrank - use lattice, only: & - lattice_maxNslipFamily, & - lattice_NslipSystem -@@ -97,11 +95,9 @@ subroutine kinematics_slipplane_opening_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_slipplane_opening_LABEL//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_slipplane_opening_LABEL//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(phase_kinematics == KINEMATICS_slipplane_opening_ID),pInt) - if (maxNinstance == 0_pInt) return -diff --git a/src/kinematics_thermal_expansion.f90 b/src/kinematics_thermal_expansion.f90 -index 30c267d3..e7cbca67 100644 ---- a/src/kinematics_thermal_expansion.f90 -+++ b/src/kinematics_thermal_expansion.f90 -@@ -71,8 +71,6 @@ subroutine kinematics_thermal_expansion_init(fileUnit) - KINEMATICS_thermal_expansion_ID, & - material_Nphase, & - MATERIAL_partPhase -- use numerics,only: & -- worldrank - - implicit none - integer(pInt), intent(in) :: fileUnit -@@ -83,11 +81,9 @@ subroutine kinematics_thermal_expansion_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_thermal_expansion_LABEL//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_thermal_expansion_LABEL//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(phase_kinematics == KINEMATICS_thermal_expansion_ID),pInt) - if (maxNinstance == 0_pInt) return -diff --git a/src/kinematics_vacancy_strain.f90 b/src/kinematics_vacancy_strain.f90 -index 791c0e3c..9558f506 100644 ---- a/src/kinematics_vacancy_strain.f90 -+++ b/src/kinematics_vacancy_strain.f90 -@@ -71,8 +71,6 @@ subroutine kinematics_vacancy_strain_init(fileUnit) - KINEMATICS_vacancy_strain_ID, & - material_Nphase, & - MATERIAL_partPhase -- use numerics,only: & -- worldrank - - implicit none - integer(pInt), intent(in) :: fileUnit -@@ -83,11 +81,9 @@ subroutine kinematics_vacancy_strain_init(fileUnit) - tag = '', & - line = '' - -- mainProcess: if (worldrank == 0) then -- write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_vacancy_strain_LABEL//' init -+>>>' -- write(6,'(a15,a)') ' Current time: ',IO_timeStamp() -+ write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_vacancy_strain_LABEL//' init -+>>>' -+ write(6,'(a15,a)') ' Current time: ',IO_timeStamp() - #include "compilation_info.f90" -- endif mainProcess - - maxNinstance = int(count(phase_kinematics == KINEMATICS_vacancy_strain_ID),pInt) - if (maxNinstance == 0_pInt) return diff --git a/src/mesh.f90 b/src/mesh.f90 index 666fe1e3..a314c22c 100644 --- a/src/mesh.f90 From 74c3e9c54287e15a26c05ca76fae95660b5b026a Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 22 Nov 2017 08:38:17 +0100 Subject: [PATCH 13/26] [skip ci] updated version information after successful test of v2.0.1-992-g20d8133 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7a1f21f80..5c3c2583f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-985-g90c617e +v2.0.1-992-g20d8133 From 3b96fac8bdda242f793d2930cf0c42977b9934d0 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 28 Nov 2017 10:59:53 -0500 Subject: [PATCH 14/26] added script to calculate numerical derivative of ASCIItable data --- processing/post/addDerivative.py | 121 +++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100755 processing/post/addDerivative.py diff --git a/processing/post/addDerivative.py b/processing/post/addDerivative.py new file mode 100755 index 000000000..85b5d2363 --- /dev/null +++ b/processing/post/addDerivative.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python2.7 +# -*- coding: UTF-8 no BOM -*- + +import os,sys,math +import numpy as np +from optparse import OptionParser +import damask + +scriptName = os.path.splitext(os.path.basename(__file__))[0] +scriptID = ' '.join([scriptName,damask.version]) + +def derivative(coordinates,what): + + result = np.empty_like(what) + + # use differentiation by interpolation + # as described in http://www2.math.umd.edu/~dlevy/classes/amsc466/lecture-notes/differentiation-chap.pdf + + result[1:-1,:] = + what[1:-1,:] * (2.*coordinates[1:-1]-coordinates[:-2]-coordinates[2:]) / \ + ((coordinates[1:-1]-coordinates[:-2])*(coordinates[1:-1]-coordinates[2:])) \ + + what[2:,:] * (coordinates[1:-1]-coordinates[:-2]) / \ + ((coordinates[2:]-coordinates[1:-1])*(coordinates[2:]-coordinates[:-2])) \ + + what[:-2,:] * (coordinates[1:-1]-coordinates[2:]) / \ + ((coordinates[:-2]-coordinates[1:-1])*(coordinates[:-2]-coordinates[2:])) \ + + result[0,:] = (what[0,:] - what[1,:]) / \ + (coordinates[0] - coordinates[1]) + result[-1,:] = (what[-1,:] - what[-2,:]) / \ + (coordinates[-1] - coordinates[-2]) + + return result + +# -------------------------------------------------------------------- +# MAIN +# -------------------------------------------------------------------- + +parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [file[s]]', description = """ +Add column(s) containing numerical derivative of requested column(s) with respect to given coordinates. + +""", version = scriptID) + +parser.add_option('-c','--coordinates', + dest = 'coordinates', + type = 'string', metavar='string', + help = 'heading of coordinate column') +parser.add_option('-l','--label', + dest = 'label', + action = 'extend', metavar = '', + help = 'heading of column(s) to differentiate') + + +(options,filenames) = parser.parse_args() + +if options.coordinates is None: + parser.error('no coordinate column specified.') +if options.label is None: + parser.error('no data column specified.') + +# --- loop over input files ------------------------------------------------------------------------- + +if filenames == []: filenames = [None] + +for name in filenames: + try: table = damask.ASCIItable(name = name, + buffered = False) + except: continue + damask.util.report(scriptName,name) + +# ------------------------------------------ read header ------------------------------------------ + + table.head_read() + +# ------------------------------------------ sanity checks ---------------------------------------- + + errors = [] + remarks = [] + columns = [] + dims = [] + + if table.label_dimension(options.coordinates) != 1: + errors.append('coordinate column {} is not scalar.'.format(options.coordinates)) + + for what in options.label: + dim = table.label_dimension(what) + if dim < 0: remarks.append('column {} not found...'.format(what)) + else: + dims.append(dim) + columns.append(table.label_index(what)) + table.labels_append('d({})/d({})'.format(what,options.coordinates) if dim == 1 else + ['{}_d({})/d({})'.format(i+1,what,options.coordinates) for i in range(dim)] ) # extend ASCII heade table.labels_append('norm{}({})'.format(options.norm.capitalize(),what)) # extend ASCII header with new labels + + if remarks != []: damask.util.croak(remarks) + if errors != []: + damask.util.croak(errors) + table.close(dismiss = True) + continue + +# ------------------------------------------ assemble header -------------------------------------- + + table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) + table.head_write() + +# ------------------------------------------ process data ------------------------------------------ + + table.data_readArray() + + mask = [] + for col,dim in zip(columns,dims): mask += range(col,col+dim) # isolate data columns to differentiate + + differentiated = derivative(table.data[:,table.label_index(options.coordinates)].reshape((len(table.data),1)), + table.data[:,mask]) # calculate numerical derivative + + table.data = np.hstack((table.data,differentiated)) + +# ------------------------------------------ output result ----------------------------------------- + + table.data_writeArray() + +# ------------------------------------------ output finalization ----------------------------------- + + table.close() # close ASCII tables From c33655b479d7d948fa7f85707bef0dfdfeba695f Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Tue, 28 Nov 2017 11:14:22 -0500 Subject: [PATCH 15/26] fixed pyflakes issues --- processing/post/addDerivative.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processing/post/addDerivative.py b/processing/post/addDerivative.py index 85b5d2363..dc97c09ea 100755 --- a/processing/post/addDerivative.py +++ b/processing/post/addDerivative.py @@ -1,7 +1,7 @@ #!/usr/bin/env python2.7 # -*- coding: UTF-8 no BOM -*- -import os,sys,math +import os,sys import numpy as np from optparse import OptionParser import damask @@ -87,7 +87,7 @@ for name in filenames: dims.append(dim) columns.append(table.label_index(what)) table.labels_append('d({})/d({})'.format(what,options.coordinates) if dim == 1 else - ['{}_d({})/d({})'.format(i+1,what,options.coordinates) for i in range(dim)] ) # extend ASCII heade table.labels_append('norm{}({})'.format(options.norm.capitalize(),what)) # extend ASCII header with new labels + ['{}_d({})/d({})'.format(i+1,what,options.coordinates) for i in range(dim)] ) # extend ASCII header with new labels if remarks != []: damask.util.croak(remarks) if errors != []: From beedd27a1615f81a502241ca729ceefe4e61b691 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 6 Dec 2017 18:40:23 +0100 Subject: [PATCH 16/26] updated to current Abaqus version and adjusted UMAT name --- examples/AbaqusStandard/SX_PX_compression.cae | Bin 98304 -> 114688 bytes examples/AbaqusStandard/SX_PX_compression.jnl | 24 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/examples/AbaqusStandard/SX_PX_compression.cae b/examples/AbaqusStandard/SX_PX_compression.cae index 39ef89a6c83b6bf2e8a147510f76fc57086cfab0..fed4472b3e088a975494c6e1261bae5e13c800ac 100644 GIT binary patch literal 114688 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 P?uaUWQ*I`y+ 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 x8;cdK@0)1-XJ^PvJW2Z4(v9F!MYzd Date: Mon, 11 Dec 2017 12:07:31 -0500 Subject: [PATCH 17/26] Add MSC.Marc to VTK conversion support marc_to_vtk.py takes Marc input file, subdivides it and creates a vtkUnstructuredGrid .vtu file with the resulting geometry. Currently supports hexahedron elements. vtk_addGridData.py adds nodal/cell data to some VTK grid. It is essentially vtk_addRectilinearGridData with support for unstructured grid (.vtu) format, which is marc_to_vtk.py's output. --- processing/post/marc_to_vtk.py | 167 +++++++++++++++++++++++ processing/post/vtk_addGridData.py | 206 +++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100755 processing/post/marc_to_vtk.py create mode 100755 processing/post/vtk_addGridData.py diff --git a/processing/post/marc_to_vtk.py b/processing/post/marc_to_vtk.py new file mode 100755 index 000000000..a232d1219 --- /dev/null +++ b/processing/post/marc_to_vtk.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python2.7 +# -*- coding: UTF-8 no BOM -*- + +import os,re +import argparse +import damask +import vtk, numpy as np + +scriptName = os.path.splitext(os.path.basename(__file__))[0] +scriptID = ' '.join([scriptName, damask.version]) + +parser = argparse.ArgumentParser(description='Convert from Marc input file format to VTK', version = scriptID) +parser.add_argument('filename', type=str, nargs='+', help='files to convert') + +args = parser.parse_args() +files = args.filename +if type(files) is str: + files = [files] + + +for f in files: + with open(f, 'r') as marcfile: + marctext = marcfile.read(); + # Extract connectivity chunk from file... + connectivity_text = re.findall(r'connectivity[\n\r]+(.*?)[\n\r]+[a-zA-Z]', marctext, flags=(re.MULTILINE | re.DOTALL))[0] + connectivity_lines = re.split(r'[\n\r]+', connectivity_text, flags=(re.MULTILINE | re.DOTALL)) + connectivity_header = connectivity_lines[0] + connectivity_lines = connectivity_lines[1:] + # Construct element map + elements = dict(map(lambda line: + ( + int(line[0:10]), # index + { + 'type': int(line[10:20]), + 'verts': list(map(int, re.split(r' +', line[20:].strip()))) + } + ), connectivity_lines)) + # Extract coordinate chunk from file + coordinates_text = re.findall(r'coordinates[\n\r]+(.*?)[\n\r]+[a-zA-Z]', marctext, flags=(re.MULTILINE | re.DOTALL))[0] + coordinates_lines = re.split(r'[\n\r]+', coordinates_text, flags=(re.MULTILINE | re.DOTALL)) + coordinates_header = coordinates_lines[0] + coordinates_lines = coordinates_lines[1:] + # marc input file does not use "e" in scientific notation, this adds it and converts + fl_format = lambda string: float(re.sub(r'(\d)([\+\-])', r'\1e\2', string)) + # Construct coordinate map + coordinates = dict(map(lambda line: + ( + int(line[0:10]), + np.array([ + fl_format(line[10:30]), + fl_format(line[30:50]), + fl_format(line[50:70]) + ]) + ), coordinates_lines)) + + # Subdivide volumes + grid = vtk.vtkUnstructuredGrid() + vertex_count = len(coordinates) + edge_to_vert = dict() # when edges are subdivided, a new vertex in the middle is produced and placed in here + ordered_pair = lambda a, b: (a, b) if a < b else (b, a) # edges are bidirectional + + def subdivide_edge(vert1, vert2): + edge = ordered_pair(vert1, vert2) + + if edge in edge_to_vert: + return edge_to_vert[edge] + + newvert = len(coordinates) + 1 + coordinates[newvert] = 0.5 * (coordinates[vert1] + coordinates[vert2]) # Average + edge_to_vert[edge] = newvert; + return newvert; + + + + for el_id in range(1, len(elements) + 1): + el = elements[el_id] + if el['type'] == 7: + # Hexahedron, subdivided + + # There may be a better way to iterate over these, but this is consistent + # with the ordering scheme provided at https://damask.mpie.de/pub/Documentation/ElementType + + subverts = np.zeros((3,3,3), dtype=int) + # Get corners + subverts[0, 0, 0] = el['verts'][0] + subverts[2, 0, 0] = el['verts'][1] + subverts[2, 2, 0] = el['verts'][2] + subverts[0, 2, 0] = el['verts'][3] + subverts[0, 0, 2] = el['verts'][4] + subverts[2, 0, 2] = el['verts'][5] + subverts[2, 2, 2] = el['verts'][6] + subverts[0, 2, 2] = el['verts'][7] + + # lower edges + subverts[1, 0, 0] = subdivide_edge(subverts[0, 0, 0], subverts[2, 0, 0]) + subverts[2, 1, 0] = subdivide_edge(subverts[2, 0, 0], subverts[2, 2, 0]) + subverts[1, 2, 0] = subdivide_edge(subverts[2, 2, 0], subverts[0, 2, 0]) + subverts[0, 1, 0] = subdivide_edge(subverts[0, 2, 0], subverts[0, 0, 0]) + + # middle edges + subverts[0, 0, 1] = subdivide_edge(subverts[0, 0, 0], subverts[0, 0, 2]) + subverts[2, 0, 1] = subdivide_edge(subverts[2, 0, 0], subverts[2, 0, 2]) + subverts[2, 2, 1] = subdivide_edge(subverts[2, 2, 0], subverts[2, 2, 2]) + subverts[0, 2, 1] = subdivide_edge(subverts[0, 2, 0], subverts[0, 2, 2]) + + # top edges + subverts[1, 0, 2] = subdivide_edge(subverts[0, 0, 2], subverts[2, 0, 2]) + subverts[2, 1, 2] = subdivide_edge(subverts[2, 0, 2], subverts[2, 2, 2]) + subverts[1, 2, 2] = subdivide_edge(subverts[2, 2, 2], subverts[0, 2, 2]) + subverts[0, 1, 2] = subdivide_edge(subverts[0, 2, 2], subverts[0, 0, 2]) + + # then faces... The edge_to_vert addition is due to there being two ways + # to calculate a face, depending which opposite vertices are used to subdivide + subverts[1, 1, 0] = subdivide_edge(subverts[1, 0, 0], subverts[1, 2, 0]) + edge_to_vert[ordered_pair(subverts[0, 1, 0], subverts[2, 1, 0])] = subverts[1, 1, 0] + + subverts[1, 0, 1] = subdivide_edge(subverts[1, 0, 0], subverts[1, 0, 2]) + edge_to_vert[ordered_pair(subverts[0, 0, 1], subverts[2, 0, 1])] = subverts[1, 0, 1] + + subverts[2, 1, 1] = subdivide_edge(subverts[2, 1, 0], subverts[2, 1, 2]) + edge_to_vert[ordered_pair(subverts[2, 0, 1], subverts[2, 2, 1])] = subverts[2, 1, 1] + + subverts[1, 2, 1] = subdivide_edge(subverts[1, 2, 0], subverts[1, 2, 2]) + edge_to_vert[ordered_pair(subverts[0, 2, 1], subverts[2, 2, 1])] = subverts[1, 2, 1] + + subverts[0, 1, 1] = subdivide_edge(subverts[0, 1, 0], subverts[0, 1, 2]) + edge_to_vert[ordered_pair(subverts[0, 0, 1], subverts[0, 2, 1])] = subverts[0, 1, 1] + + subverts[1, 1, 2] = subdivide_edge(subverts[1, 0, 2], subverts[1, 2, 2]) + edge_to_vert[ordered_pair(subverts[0, 1, 2], subverts[2, 1, 2])] = subverts[1, 1, 2] + + # and finally the center. There are three ways to calculate, but elements should + # not intersect, so the edge_to_vert part isn't needed here. + subverts[1, 1, 1] = subdivide_edge(subverts[1, 1, 0], subverts[1, 1, 2]) + + + # Now make the hexahedron subelements + # order in which vtk expects vertices for a hexahedron + order = np.array([(0,0,0),(1,0,0),(1,1,0),(0,1,0),(0,0,1),(1,0,1),(1,1,1),(0,1,1)]) + for z in range(2): + for y in range(2): + for x in range(2): + hex_ = vtk.vtkHexahedron() + for vert_id in range(8): + coord = order[vert_id] + (x, y, z) + hex_.GetPointIds().SetId(vert_id, subverts[coord[0], coord[1], coord[2]] - 1) # minus one, since vtk starts at zero but marc starts at one + grid.InsertNextCell(hex_.GetCellType(), hex_.GetPointIds()) + + + else: + damask.util.croak('Unsupported Marc element type: {} (skipping)'.format(el['type'])) + + # Load all points + points = vtk.vtkPoints() + for point in range(1, len(coordinates) + 1): # marc indices start at 1 + points.InsertNextPoint(coordinates[point].tolist()) + + grid.SetPoints(points) + + # grid now contains the elements from the given marc file + writer = vtk.vtkXMLUnstructuredGridWriter() + writer.SetFileName(re.sub(r'\..+', ".vtu", f)) # *.vtk extension does not work in paraview + #writer.SetCompressorTypeToZLib() + + if vtk.VTK_MAJOR_VERSION <= 5: writer.SetInput(grid) + else: writer.SetInputData(grid) + writer.Write() diff --git a/processing/post/vtk_addGridData.py b/processing/post/vtk_addGridData.py new file mode 100755 index 000000000..e0c274dc7 --- /dev/null +++ b/processing/post/vtk_addGridData.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python2.7 +# -*- coding: UTF-8 no BOM -*- + +import os,vtk +import damask +from vtk.util import numpy_support +from collections import defaultdict +from optparse import OptionParser + +scriptName = os.path.splitext(os.path.basename(__file__))[0] +scriptID = ' '.join([scriptName,damask.version]) + +# -------------------------------------------------------------------- +# MAIN +# -------------------------------------------------------------------- + +msg = "Add scalars, vectors, and/or an RGB tuple from" +msg += "an ASCIItable to existing VTK grid (.vtr/.vtk/.vtu)." +parser = OptionParser(option_class=damask.extendableOption, + usage='%prog options [file[s]]', + description = msg, + version = scriptID) + +parser.add_option( '--vtk', + dest = 'vtk', + type = 'string', metavar = 'string', + help = 'VTK file name') +parser.add_option( '--inplace', + dest = 'inplace', + action = 'store_true', + help = 'modify VTK file in-place') +parser.add_option('-r', '--render', + dest = 'render', + action = 'store_true', + help = 'open output in VTK render window') +parser.add_option('-d', '--data', + dest = 'data', + action = 'extend', metavar = '', + help = 'scalar/vector value(s) label(s)') +parser.add_option('-t', '--tensor', + dest = 'tensor', + action = 'extend', metavar = '', + help = 'tensor (3x3) value label(s)') +parser.add_option('-c', '--color', + dest = 'color', + action = 'extend', metavar = '', + help = 'RGB color tuple label') + +parser.set_defaults(data = [], + tensor = [], + color = [], + inplace = False, + render = False, +) + +(options, filenames) = parser.parse_args() + +if not options.vtk: parser.error('No VTK file specified.') +if not os.path.exists(options.vtk): parser.error('VTK file does not exist.') + +if os.path.splitext(options.vtk)[1] == '.vtr': + reader = vtk.vtkXMLRectilinearGridReader() + reader.SetFileName(options.vtk) + reader.Update() + rGrid = reader.GetOutput() + writer = vtk.vtkXMLRectilinearGridWriter() + writer.SetFileName(os.path.splitext(options.vtk)[0]+('.vtr' if options.inplace else '_added.vtr')) +elif os.path.splitext(options.vtk)[1] == '.vtk': + reader = vtk.vtkGenericDataObjectReader() + reader.SetFileName(options.vtk) + reader.Update() + rGrid = reader.GetRectilinearGridOutput() + writer = vtk.vtkXMLRectilinearGridWriter() + writer.SetFileName(os.path.splitext(options.vtk)[0]+('.vtr' if options.inplace else '_added.vtr')) +elif os.path.splitext(options.vtk)[1] == '.vtu': + reader = vtk.vtkXMLUnstructuredGridReader() + reader.SetFileName(options.vtk) + reader.Update() + rGrid = reader.GetOutput() + writer = vtk.vtkXMLUnstructuredGridWriter() + writer.SetFileName(os.path.splitext(options.vtk)[0]+('.vtu' if options.inplace else '_added.vtu')) +else: + parser.error('Unsupported VTK file type extension.') + +Npoints = rGrid.GetNumberOfPoints() +Ncells = rGrid.GetNumberOfCells() + +damask.util.croak('{}: {} points and {} cells...'.format(options.vtk,Npoints,Ncells)) + +# --- loop over input files ------------------------------------------------------------------------- + +if filenames == []: filenames = [None] + +for name in filenames: + try: table = damask.ASCIItable(name = name, + buffered = False, + readonly = True) + except: continue + damask.util.report(scriptName, name) + +# --- interpret header ---------------------------------------------------------------------------- + + table.head_read() + + remarks = [] + errors = [] + VTKarray = {} + active = defaultdict(list) + + for datatype,dimension,label in [['data',99,options.data], + ['tensor',9,options.tensor], + ['color' ,3,options.color], + ]: + for i,dim in enumerate(table.label_dimension(label)): + me = label[i] + if dim == -1: remarks.append('{} "{}" not found...'.format(datatype,me)) + elif dim > dimension: remarks.append('"{}" not of dimension {}...'.format(me,dimension)) + else: + remarks.append('adding {} "{}"...'.format(datatype,me)) + active[datatype].append(me) + + if remarks != []: damask.util.croak(remarks) + if errors != []: + damask.util.croak(errors) + table.close(dismiss = True) + continue + +# ------------------------------------------ process data --------------------------------------- + + table.data_readArray([item for sublist in active.values() for item in sublist]) # read all requested data + + for datatype,labels in active.items(): # loop over scalar,color + for me in labels: # loop over all requested items + VTKtype = vtk.VTK_DOUBLE + VTKdata = table.data[:, table.label_indexrange(me)].copy() # copy to force contiguous layout + + if datatype == 'color': + VTKtype = vtk.VTK_UNSIGNED_CHAR + VTKdata = (VTKdata*255).astype(int) # translate to 0..255 UCHAR + elif datatype == 'tensor': + VTKdata[:,1] = VTKdata[:,3] = 0.5*(VTKdata[:,1]+VTKdata[:,3]) + VTKdata[:,2] = VTKdata[:,6] = 0.5*(VTKdata[:,2]+VTKdata[:,6]) + VTKdata[:,5] = VTKdata[:,7] = 0.5*(VTKdata[:,5]+VTKdata[:,7]) + + VTKarray[me] = numpy_support.numpy_to_vtk(num_array=VTKdata,deep=True,array_type=VTKtype) + VTKarray[me].SetName(me) + + table.close() # close input ASCII table + +# ------------------------------------------ add data --------------------------------------- + + if len(table.data) == Npoints: mode = 'point' + elif len(table.data) == Ncells: mode = 'cell' + else: + damask.util.croak('Data count is incompatible with grid...') + continue + + damask.util.croak('{} mode...'.format(mode)) + + for datatype,labels in active.items(): # loop over scalar,color + if datatype == 'color': + if mode == 'cell': rGrid.GetCellData().SetScalars(VTKarray[active['color'][0]]) + elif mode == 'point': rGrid.GetPointData().SetScalars(VTKarray[active['color'][0]]) + for me in labels: # loop over all requested items + if mode == 'cell': rGrid.GetCellData().AddArray(VTKarray[me]) + elif mode == 'point': rGrid.GetPointData().AddArray(VTKarray[me]) + + rGrid.Modified() + if vtk.VTK_MAJOR_VERSION <= 5: rGrid.Update() + +# ------------------------------------------ output result --------------------------------------- + + writer.SetDataModeToBinary() + writer.SetCompressorTypeToZLib() + if vtk.VTK_MAJOR_VERSION <= 5: writer.SetInput(rGrid) + else: writer.SetInputData(rGrid) + writer.Write() + +# ------------------------------------------ render result --------------------------------------- + +if options.render: + mapper = vtk.vtkDataSetMapper() + mapper.SetInputData(rGrid) + actor = vtk.vtkActor() + actor.SetMapper(mapper) + +# Create the graphics structure. The renderer renders into the +# render window. The render window interactor captures mouse events +# and will perform appropriate camera or actor manipulation +# depending on the nature of the events. + + ren = vtk.vtkRenderer() + + renWin = vtk.vtkRenderWindow() + renWin.AddRenderer(ren) + + ren.AddActor(actor) + ren.SetBackground(1, 1, 1) + renWin.SetSize(200, 200) + + iren = vtk.vtkRenderWindowInteractor() + iren.SetRenderWindow(renWin) + + iren.Initialize() + renWin.Render() + iren.Start() From e8a2bff38312220ca5e41c5b4418bf870df9f37f Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 12 Dec 2017 02:34:07 +0100 Subject: [PATCH 18/26] [skip ci] updated version information after successful test of v2.0.1-998-ga03bb1e --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5c3c2583f..24c2a2961 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-992-g20d8133 +v2.0.1-998-ga03bb1e From e02a0d32be2d130bb593261c2079b1a1951a6b74 Mon Sep 17 00:00:00 2001 From: ChuanlaiLiu Date: Tue, 19 Dec 2017 12:59:10 +0800 Subject: [PATCH 19/26] debug corrected index for stiffness tensor, phase not instance --- src/plastic_dislotwin.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plastic_dislotwin.f90 b/src/plastic_dislotwin.f90 index 50b14bdf9..d6c73e8f3 100644 --- a/src/plastic_dislotwin.f90 +++ b/src/plastic_dislotwin.f90 @@ -1029,7 +1029,7 @@ subroutine plastic_dislotwin_init(fileUnit) do p = 1_pInt,3_pInt; do q = 1_pInt,3_pInt; do r = 1_pInt,3_pInt; do s = 1_pInt,3_pInt plastic_dislotwin_Ctwin3333(l,m,n,o,index_myFamily+j,instance) = & plastic_dislotwin_Ctwin3333(l,m,n,o,index_myFamily+j,instance) + & - lattice_C3333(p,q,r,s,instance) * & + lattice_C3333(p,q,r,s,phase) * & lattice_Qtwin(l,p,index_otherFamily+j,phase) * & lattice_Qtwin(m,q,index_otherFamily+j,phase) * & lattice_Qtwin(n,r,index_otherFamily+j,phase) * & @@ -1087,7 +1087,7 @@ subroutine plastic_dislotwin_init(fileUnit) do p = 1_pInt,3_pInt; do q = 1_pInt,3_pInt; do r = 1_pInt,3_pInt; do s = 1_pInt,3_pInt plastic_dislotwin_Ctrans3333(l,m,n,o,index_myFamily+j,instance) = & plastic_dislotwin_Ctrans3333(l,m,n,o,index_myFamily+j,instance) + & - lattice_trans_C3333(p,q,r,s,instance) * & + lattice_trans_C3333(p,q,r,s,phase) * & lattice_Qtrans(l,p,index_otherFamily+j,phase) * & lattice_Qtrans(m,q,index_otherFamily+j,phase) * & lattice_Qtrans(n,r,index_otherFamily+j,phase) * & From 515056e9fffc2aa57c1c0ae7731e558235991adb Mon Sep 17 00:00:00 2001 From: Test User Date: Tue, 19 Dec 2017 14:02:28 +0100 Subject: [PATCH 20/26] [skip ci] updated version information after successful test of v2.0.1-1000-ge02a0d3 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 24c2a2961..95d7cbcda 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-998-ga03bb1e +v2.0.1-1000-ge02a0d3 From 7149f9599fb355e9e1d6ee2905a13395b92be335 Mon Sep 17 00:00:00 2001 From: Franz Roters Date: Wed, 10 Jan 2018 17:13:25 +0100 Subject: [PATCH 21/26] changes towards supporting new Marc2017 input file format still not working --- src/IO.f90 | 330 +++++++++++--------- src/mesh.f90 | 832 ++++++++++++++++++++++++++++----------------------- 2 files changed, 637 insertions(+), 525 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index 224fad8c4..a6c3a7da8 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -9,7 +9,7 @@ module IO use prec, only: & pInt, & pReal - + implicit none private character(len=5), parameter, public :: & @@ -50,6 +50,7 @@ module IO IO_skipChunks, & IO_extractValue, & IO_countDataLines, & + IO_countNumericalDataLines, & IO_countContinuousIntValues, & IO_continuousIntValues, & IO_error, & @@ -61,7 +62,7 @@ module IO IO_open_inputFile, & IO_open_logFile #endif -#ifdef Abaqus +#ifdef Abaqus public :: & IO_abaqus_hasNoPart #endif @@ -69,7 +70,7 @@ module IO IO_fixedFloatValue, & IO_verifyFloatValue, & IO_verifyIntValue -#ifdef Abaqus +#ifdef Abaqus private :: & abaqus_assembleInputFile #endif @@ -86,7 +87,7 @@ subroutine IO_init compiler_version, & compiler_options #endif - + implicit none write(6,'(/,a)') ' <<<+- IO init -+>>>' @@ -101,7 +102,7 @@ end subroutine IO_init !! Recursion is triggered by "{path/to/inputfile}" in a line !-------------------------------------------------------------------------------------------------- recursive function IO_read(fileUnit,reset) result(line) - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit logical, intent(in), optional :: reset @@ -131,7 +132,7 @@ recursive function IO_read(fileUnit,reset) result(line) unitOn(1) = fileUnit read(unitOn(stack),'(a65536)',END=100) line - + input = IO_getTag(line,'{','}') !-------------------------------------------------------------------------------------------------- @@ -139,7 +140,7 @@ recursive function IO_read(fileUnit,reset) result(line) if (input == '') return ! regular line !-------------------------------------------------------------------------------------------------- -! recursion case +! recursion case if (stack >= 10_pInt) call IO_error(104_pInt,ext_msg=input) ! recursion limit reached inquire(UNIT=unitOn(stack),NAME=path) ! path of current file @@ -154,9 +155,9 @@ recursive function IO_read(fileUnit,reset) result(line) if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=pathOn(stack)) line = IO_read(fileUnit) - + return - + !-------------------------------------------------------------------------------------------------- ! end of file case 100 if (stack > 1_pInt) then ! can go back to former file @@ -175,13 +176,13 @@ end function IO_read !! error message !-------------------------------------------------------------------------------------------------- subroutine IO_checkAndRewind(fileUnit) - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit logical :: fileOpened character(len=15) :: fileRead - inquire(unit=fileUnit, opened=fileOpened, read=fileRead) + inquire(unit=fileUnit, opened=fileOpened, read=fileRead) if (.not. fileOpened .or. trim(fileRead)/='YES') call IO_error(102_pInt) rewind(fileUnit) @@ -189,7 +190,7 @@ end subroutine IO_checkAndRewind !-------------------------------------------------------------------------------------------------- -!> @brief opens existing file for reading to given unit. Path to file is relative to working +!> @brief opens existing file for reading to given unit. Path to file is relative to working !! directory !> @details like IO_open_file_stat, but error is handled via call to IO_error and not via return !! value @@ -197,47 +198,47 @@ end subroutine IO_checkAndRewind subroutine IO_open_file(fileUnit,relPath) use DAMASK_interface, only: & getSolverWorkingDirectoryName - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit character(len=*), intent(in) :: relPath !< relative path from working directory integer(pInt) :: myStat character(len=1024) :: path - + path = trim(getSolverWorkingDirectoryName())//relPath open(fileUnit,status='old',iostat=myStat,file=path) if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) - + end subroutine IO_open_file !-------------------------------------------------------------------------------------------------- -!> @brief opens existing file for reading to given unit. Path to file is relative to working +!> @brief opens existing file for reading to given unit. Path to file is relative to working !! directory !> @details Like IO_open_file, but error is handled via return value and not via call to IO_error !-------------------------------------------------------------------------------------------------- logical function IO_open_file_stat(fileUnit,relPath) use DAMASK_interface, only: & getSolverWorkingDirectoryName - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit character(len=*), intent(in) :: relPath !< relative path from working directory integer(pInt) :: myStat character(len=1024) :: path - + path = trim(getSolverWorkingDirectoryName())//relPath open(fileUnit,status='old',iostat=myStat,file=path) IO_open_file_stat = (myStat == 0_pInt) - + end function IO_open_file_stat !-------------------------------------------------------------------------------------------------- -!> @brief opens existing file for reading to given unit. File is named after solver job name -!! plus given extension and located in current working directory +!> @brief opens existing file for reading to given unit. File is named after solver job name +!! plus given extension and located in current working directory !> @details like IO_open_jobFile_stat, but error is handled via call to IO_error and not via return !! value !-------------------------------------------------------------------------------------------------- @@ -256,14 +257,14 @@ subroutine IO_open_jobFile(fileUnit,ext) path = trim(getSolverWorkingDirectoryName())//trim(getSolverJobName())//'.'//ext open(fileUnit,status='old',iostat=myStat,file=path) if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) - + end subroutine IO_open_jobFile !-------------------------------------------------------------------------------------------------- -!> @brief opens existing file for reading to given unit. File is named after solver job name -!! plus given extension and located in current working directory -!> @details Like IO_open_jobFile, but error is handled via return value and not via call to +!> @brief opens existing file for reading to given unit. File is named after solver job name +!! plus given extension and located in current working directory +!> @details Like IO_open_jobFile, but error is handled via return value and not via call to !! IO_error !-------------------------------------------------------------------------------------------------- logical function IO_open_jobFile_stat(fileUnit,ext) @@ -303,7 +304,7 @@ subroutine IO_open_inputFile(fileUnit,modelName) character(len=1024) :: path #ifdef Abaqus integer(pInt) :: fileType - + fileType = 1_pInt ! assume .pes path = trim(getSolverWorkingDirectoryName())//trim(modelName)//inputFileExtension(fileType) ! attempt .pes, if it exists: it should be used open(fileUnit+1,status='old',iostat=myStat,file=path) @@ -313,12 +314,12 @@ subroutine IO_open_inputFile(fileUnit,modelName) open(fileUnit+1,status='old',iostat=myStat,file=path) endif if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) - + path = trim(getSolverWorkingDirectoryName())//trim(modelName)//inputFileExtension(fileType)//'_assembly' open(fileUnit,iostat=myStat,file=path) if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) if (.not.abaqus_assembleInputFile(fileUnit,fileUnit+1_pInt)) call IO_error(103_pInt) ! strip comments and concatenate any "include"s - close(fileUnit+1_pInt) + close(fileUnit+1_pInt) #endif #ifdef Marc4DAMASK path = trim(getSolverWorkingDirectoryName())//trim(modelName)//inputFileExtension @@ -330,8 +331,8 @@ end subroutine IO_open_inputFile !-------------------------------------------------------------------------------------------------- -!> @brief opens existing FEM log file for reading to given unit. File is named after solver job -!! name and located in current working directory +!> @brief opens existing FEM log file for reading to given unit. File is named after solver job +!! name and located in current working directory !-------------------------------------------------------------------------------------------------- subroutine IO_open_logFile(fileUnit) use DAMASK_interface, only: & @@ -354,14 +355,14 @@ end subroutine IO_open_logFile !-------------------------------------------------------------------------------------------------- -!> @brief opens ASCII file to given unit for writing. File is named after solver job name plus +!> @brief opens ASCII file to given unit for writing. File is named after solver job name plus !! given extension and located in current working directory !-------------------------------------------------------------------------------------------------- subroutine IO_write_jobFile(fileUnit,ext) use DAMASK_interface, only: & getSolverWorkingDirectoryName, & getSolverJobName - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit character(len=*), intent(in) :: ext !< extension of file @@ -372,19 +373,19 @@ subroutine IO_write_jobFile(fileUnit,ext) path = trim(getSolverWorkingDirectoryName())//trim(getSolverJobName())//'.'//ext open(fileUnit,status='replace',iostat=myStat,file=path) if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) - + end subroutine IO_write_jobFile !-------------------------------------------------------------------------------------------------- -!> @brief opens binary file containing array of pReal numbers to given unit for writing. File is +!> @brief opens binary file containing array of pReal numbers to given unit for writing. File is !! named after solver job name plus given extension and located in current working directory !-------------------------------------------------------------------------------------------------- subroutine IO_write_jobRealFile(fileUnit,ext,recMultiplier) - use DAMASK_interface, only: & + use DAMASK_interface, only: & getSolverWorkingDirectoryName, & getSolverJobName - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit character(len=*), intent(in) :: ext !< extension of file @@ -403,19 +404,19 @@ subroutine IO_write_jobRealFile(fileUnit,ext,recMultiplier) endif if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) - + end subroutine IO_write_jobRealFile !-------------------------------------------------------------------------------------------------- -!> @brief opens binary file containing array of pInt numbers to given unit for writing. File is +!> @brief opens binary file containing array of pInt numbers to given unit for writing. File is !! named after solver job name plus given extension and located in current working directory !-------------------------------------------------------------------------------------------------- subroutine IO_write_jobIntFile(fileUnit,ext,recMultiplier) use DAMASK_interface, only: & getSolverWorkingDirectoryName, & getSolverJobName - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit character(len=*), intent(in) :: ext !< extension of file @@ -434,21 +435,21 @@ subroutine IO_write_jobIntFile(fileUnit,ext,recMultiplier) endif if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) - + end subroutine IO_write_jobIntFile !-------------------------------------------------------------------------------------------------- -!> @brief opens binary file containing array of pReal numbers to given unit for reading. File is +!> @brief opens binary file containing array of pReal numbers to given unit for reading. File is !! located in current working directory !-------------------------------------------------------------------------------------------------- subroutine IO_read_realFile(fileUnit,ext,modelName,recMultiplier) use DAMASK_interface, only: & getSolverWorkingDirectoryName - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit - character(len=*), intent(in) :: ext, & !< extension of file + character(len=*), intent(in) :: ext, & !< extension of file modelName !< model name, in case of restart not solver job name integer(pInt), intent(in), optional :: recMultiplier !< record length (multiple of pReal Numbers, if not given set to one) @@ -457,28 +458,28 @@ subroutine IO_read_realFile(fileUnit,ext,modelName,recMultiplier) path = trim(getSolverWorkingDirectoryName())//trim(modelName)//'.'//ext if (present(recMultiplier)) then - open(fileUnit,status='old',form='unformatted',access='direct', & + open(fileUnit,status='old',form='unformatted',access='direct', & recl=pReal*recMultiplier,iostat=myStat,file=path) else open(fileUnit,status='old',form='unformatted',access='direct', & recl=pReal,iostat=myStat,file=path) endif if (myStat /= 0_pInt) call IO_error(100_pInt,el=myStat,ext_msg=path) - + end subroutine IO_read_realFile !-------------------------------------------------------------------------------------------------- -!> @brief opens binary file containing array of pInt numbers to given unit for reading. File is +!> @brief opens binary file containing array of pInt numbers to given unit for reading. File is !! located in current working directory !-------------------------------------------------------------------------------------------------- subroutine IO_read_intFile(fileUnit,ext,modelName,recMultiplier) use DAMASK_interface, only: & getSolverWorkingDirectoryName - + implicit none integer(pInt), intent(in) :: fileUnit !< file unit - character(len=*), intent(in) :: ext, & !< extension of file + character(len=*), intent(in) :: ext, & !< extension of file modelName !< model name, in case of restart not solver job name integer(pInt), intent(in), optional :: recMultiplier !< record length (multiple of pReal Numbers, if not given set to one) @@ -487,14 +488,14 @@ subroutine IO_read_intFile(fileUnit,ext,modelName,recMultiplier) path = trim(getSolverWorkingDirectoryName())//trim(modelName)//'.'//ext if (present(recMultiplier)) then - open(fileUnit,status='old',form='unformatted',access='direct', & + open(fileUnit,status='old',form='unformatted',access='direct', & recl=pInt*recMultiplier,iostat=myStat,file=path) else open(fileUnit,status='old',form='unformatted',access='direct', & recl=pInt,iostat=myStat,file=path) endif if (myStat /= 0) call IO_error(100_pInt,ext_msg=path) - + end subroutine IO_read_intFile @@ -509,9 +510,9 @@ logical function IO_abaqus_hasNoPart(fileUnit) integer(pInt), allocatable, dimension(:) :: chunkPos character(len=65536) :: line - + IO_abaqus_hasNoPart = .true. - + 610 FORMAT(A65536) rewind(fileUnit) do @@ -522,7 +523,7 @@ logical function IO_abaqus_hasNoPart(fileUnit) exit endif enddo - + 620 end function IO_abaqus_hasNoPart #endif @@ -537,7 +538,7 @@ function IO_hybridIA(Nast,ODFfileName) integer(pInt), intent(in) :: Nast !< number of samples? real(pReal), dimension(3,Nast) :: IO_hybridIA character(len=*), intent(in) :: ODFfileName !< name of ODF file including total path - + !-------------------------------------------------------------------------------------------------- ! math module is not available real(pReal), parameter :: PI = 3.141592653589793_pReal @@ -561,7 +562,7 @@ function IO_hybridIA(Nast,ODFfileName) write(6,'(/,a,/)',advance='no') ' Using linear ODF file: '//trim(ODFfileName) !-------------------------------------------------------------------------------------------------- -! parse header of ODF file +! parse header of ODF file call IO_open_file(FILEUNIT,ODFfileName) headerLength = 0_pInt line=IO_read(FILEUNIT) @@ -579,7 +580,7 @@ function IO_hybridIA(Nast,ODFfileName) line=IO_read(FILEUNIT) enddo columns = 0_pInt - chunkPos = IO_stringPos(line) + chunkPos = IO_stringPos(line) do i = 1_pInt, chunkPos(1) select case ( IO_lc(IO_StringValue(line,chunkPos,i,.true.)) ) case ('phi1') @@ -603,7 +604,7 @@ function IO_hybridIA(Nast,ODFfileName) line=IO_read(FILEUNIT) do while (trim(line) /= IO_EOF) - chunkPos = IO_stringPos(line) + chunkPos = IO_stringPos(line) eulers=[IO_floatValue(line,chunkPos,columns(1)),& IO_floatValue(line,chunkPos,columns(2)),& IO_floatValue(line,chunkPos,columns(3))] @@ -646,7 +647,7 @@ function IO_hybridIA(Nast,ODFfileName) do phi1=1_pInt,steps(1); do Phi=1_pInt,steps(2); do phi2=1_pInt,steps(3) line=IO_read(FILEUNIT) - chunkPos = IO_stringPos(line) + chunkPos = IO_stringPos(line) eulers=[IO_floatValue(line,chunkPos,columns(1)),& ! read in again for consistency check only IO_floatValue(line,chunkPos,columns(2)),& IO_floatValue(line,chunkPos,columns(3))]*INRAD @@ -661,16 +662,16 @@ function IO_hybridIA(Nast,ODFfileName) prob = 0.0_pReal endif dV_V(phi2,Phi,phi1) = prob*dg_0*sin((real(Phi-1_pInt,pReal)+center)*deltas(2)) - enddo; enddo; enddo + enddo; enddo; enddo close(FILEUNIT) dV_V = dV_V/sum_dV_V ! normalize to 1 - + !-------------------------------------------------------------------------------------------------- ! now fix bounds Nset = max(Nast,NnonZero) ! if less than non-zero voxel count requested, sample at least that much lowerC = 0.0_pReal upperC = real(Nset, pReal) - + do while (hybridIA_reps(dV_V,steps,upperC) < Nset) lowerC = upperC upperC = upperC*2.0_pReal @@ -717,25 +718,25 @@ function IO_hybridIA(Nast,ODFfileName) IO_hybridIA(3,i) = deltas(3)*(real(mod(bin ,steps(3)),pReal)+center) ! phi2 binSet(j) = binSet(i) enddo - + contains !-------------------------------------------------------------------------------------------------- !> @brief counts hybrid IA repetitions !-------------------------------------------------------------------------------------------------- integer(pInt) pure function hybridIA_reps(dV_V,steps,C) - + implicit none integer(pInt), intent(in), dimension(3) :: steps !< number of bins in Euler space real(pReal), intent(in), dimension(steps(3),steps(2),steps(1)) :: dV_V !< needs description real(pReal), intent(in) :: C !< needs description - + integer(pInt) :: phi1,Phi,phi2 - + hybridIA_reps = 0_pInt do phi1=1_pInt,steps(1); do Phi =1_pInt,steps(2); do phi2=1_pInt,steps(3) hybridIA_reps = hybridIA_reps+nint(C*dV_V(phi2,Phi,phi1), pInt) enddo; enddo; enddo - + end function hybridIA_reps end function IO_hybridIA @@ -753,11 +754,11 @@ logical pure function IO_isBlank(string) character(len=*), parameter :: comment = achar(35) ! comment id '#' integer :: posNonBlank, posComment ! no pInt - + posNonBlank = verify(string,blankChar) posComment = scan(string,comment) IO_isBlank = posNonBlank == 0 .or. posNonBlank == posComment - + end function IO_isBlank @@ -769,8 +770,8 @@ pure function IO_getTag(string,openChar,closeChar) implicit none character(len=*), intent(in) :: string !< string to check for tag character(len=len_trim(string)) :: IO_getTag - - character(len=*), intent(in) :: openChar, & !< indicates beginning of tag + + character(len=*), intent(in) :: openChar, & !< indicates beginning of tag closeChar !< indicates end of tag character(len=*), parameter :: SEP=achar(32)//achar(9)//achar(10)//achar(13) ! whitespaces @@ -780,7 +781,7 @@ pure function IO_getTag(string,openChar,closeChar) IO_getTag = '' left = scan(string,openChar) right = scan(string,closeChar) - + if (left == verify(string,SEP) .and. right > left) & ! openChar is first and closeChar occurs IO_getTag = string(left+1:right-1) @@ -793,7 +794,7 @@ end function IO_getTag integer(pInt) function IO_countSections(fileUnit,part) implicit none - integer(pInt), intent(in) :: fileUnit !< file handle + integer(pInt), intent(in) :: fileUnit !< file handle character(len=*), intent(in) :: part !< part name in which sections are counted character(len=65536) :: line @@ -811,14 +812,14 @@ integer(pInt) function IO_countSections(fileUnit,part) if (IO_isBlank(line)) cycle ! skip empty lines if (IO_getTag(line,'<','>') /= '') then ! stop at next part line = IO_read(fileUnit, .true.) ! reset IO_read - exit + exit endif if (IO_getTag(line,'[',']') /= '') & ! found [section] identifier IO_countSections = IO_countSections + 1_pInt enddo end function IO_countSections - + !-------------------------------------------------------------------------------------------------- !> @brief returns array of tag counts within for at most N [sections] @@ -828,7 +829,7 @@ function IO_countTagInPart(fileUnit,part,tag,Nsections) implicit none integer(pInt), intent(in) :: Nsections !< maximum number of sections in which tag is searched for integer(pInt), dimension(Nsections) :: IO_countTagInPart - integer(pInt), intent(in) :: fileUnit !< file handle + integer(pInt), intent(in) :: fileUnit !< file handle character(len=*),intent(in) :: part, & !< part in which tag is searched for tag !< tag to search for @@ -837,12 +838,12 @@ function IO_countTagInPart(fileUnit,part,tag,Nsections) integer(pInt), allocatable, dimension(:) :: chunkPos integer(pInt) :: section character(len=65536) :: line - + line = '' counter = 0_pInt section = 0_pInt - rewind(fileUnit) + rewind(fileUnit) do while (trim(line) /= IO_EOF .and. IO_lc(IO_getTag(line,'<','>')) /= part) ! search for part line = IO_read(fileUnit) enddo @@ -852,14 +853,14 @@ function IO_countTagInPart(fileUnit,part,tag,Nsections) if (IO_isBlank(line)) cycle ! skip empty lines if (IO_getTag(line,'<','>') /= '') then ! stop at next part line = IO_read(fileUnit, .true.) ! reset IO_read - exit + exit endif if (IO_getTag(line,'[',']') /= '') section = section + 1_pInt ! found [section] identifier if (section > 0) then chunkPos = IO_stringPos(line) if (tag == trim(IO_lc(IO_stringValue(line,chunkPos,1_pInt)))) & ! match counter(section) = counter(section) + 1_pInt - endif + endif enddo IO_countTagInPart = counter @@ -875,7 +876,7 @@ function IO_spotTagInPart(fileUnit,part,tag,Nsections) implicit none integer(pInt), intent(in) :: Nsections !< maximum number of sections in which tag is searched for logical, dimension(Nsections) :: IO_spotTagInPart - integer(pInt), intent(in) :: fileUnit !< file handle + integer(pInt), intent(in) :: fileUnit !< file handle character(len=*),intent(in) :: part, & !< part in which tag is searched for tag !< tag to search for @@ -898,11 +899,11 @@ function IO_spotTagInPart(fileUnit,part,tag,Nsections) if (IO_isBlank(line)) cycle ! skip empty lines if (IO_getTag(line,'<','>') /= '') then ! stop at next part line = IO_read(fileUnit, .true.) ! reset IO_read - exit + exit endif if (IO_getTag(line,'[',']') /= '') section = section + 1_pInt ! found [section] identifier if (section > 0_pInt) then - chunkPos = IO_stringPos(line) + chunkPos = IO_stringPos(line) if (tag == trim(IO_lc(IO_stringValue(line,chunkPos,1_pInt)))) & ! match IO_spotTagInPart(section) = .true. endif @@ -917,7 +918,7 @@ function IO_spotTagInPart(fileUnit,part,tag,Nsections) logical function IO_globalTagInPart(fileUnit,part,tag) implicit none - integer(pInt), intent(in) :: fileUnit !< file handle + integer(pInt), intent(in) :: fileUnit !< file handle character(len=*),intent(in) :: part, & !< part in which tag is searched for tag !< tag to search for @@ -940,21 +941,21 @@ logical function IO_globalTagInPart(fileUnit,part,tag) if (IO_isBlank(line)) cycle ! skip empty lines if (IO_getTag(line,'<','>') /= '') then ! stop at next part line = IO_read(fileUnit, .true.) ! reset IO_read - exit + exit endif if (IO_getTag(line,'[',']') /= '') section = section + 1_pInt ! found [section] identifier if (section == 0_pInt) then chunkPos = IO_stringPos(line) if (tag == trim(IO_lc(IO_stringValue(line,chunkPos,1_pInt)))) & ! match IO_globalTagInPart = .true. - endif + endif enddo end function IO_globalTagInPart !-------------------------------------------------------------------------------------------------- -!> @brief locates all space-separated chunks in given string and returns array containing number +!> @brief locates all space-separated chunks in given string and returns array containing number !! them and the left/right position to be used by IO_xxxVal !! Array size is dynamically adjusted to number of chunks found in string !! IMPORTANT: first element contains number of chunks! @@ -964,13 +965,13 @@ pure function IO_stringPos(string) implicit none integer(pInt), dimension(:), allocatable :: IO_stringPos character(len=*), intent(in) :: string !< string in which chunk positions are searched for - + character(len=*), parameter :: SEP=achar(44)//achar(32)//achar(9)//achar(10)//achar(13) ! comma and whitespaces integer :: left, right ! no pInt (verify and scan return default integer) allocate(IO_stringPos(1), source=0_pInt) right = 0 - + do while (verify(string(right+1:),SEP)>0) left = right + verify(string(right+1:),SEP) right = left + scan(string(left:),SEP) - 2 @@ -986,7 +987,7 @@ end function IO_stringPos !> @brief reads string value at myChunk from string !-------------------------------------------------------------------------------------------------- function IO_stringValue(string,chunkPos,myChunk,silent) - + implicit none integer(pInt), dimension(:), intent(in) :: chunkPos !< positions of start and end of each tag/chunk in given string integer(pInt), intent(in) :: myChunk !< position number of desired chunk @@ -997,13 +998,13 @@ function IO_stringValue(string,chunkPos,myChunk,silent) character(len=16), parameter :: MYNAME = 'IO_stringValue: ' logical :: warn - + if (.not. present(silent)) then warn = .false. else warn = silent endif - + IO_stringValue = '' valuePresent: if (myChunk > chunkPos(1) .or. myChunk < 1_pInt) then if (warn) call IO_warning(201,el=myChunk,ext_msg=MYNAME//trim(string)) @@ -1018,11 +1019,11 @@ end function IO_stringValue !> @brief reads string value at myChunk from fixed format string !-------------------------------------------------------------------------------------------------- pure function IO_fixedStringValue (string,ends,myChunk) - + implicit none integer(pInt), intent(in) :: myChunk !< position number of desired chunk integer(pInt), dimension(:), intent(in) :: ends !< positions of end of each tag/chunk in given string - character(len=ends(myChunk+1)-ends(myChunk)) :: IO_fixedStringValue + character(len=ends(myChunk+1)-ends(myChunk)) :: IO_fixedStringValue character(len=*), intent(in) :: string !< raw input with known ends of each chunk IO_fixedStringValue = string(ends(myChunk)+1:ends(myChunk+1)) @@ -1059,7 +1060,7 @@ end function IO_floatValue !> @brief reads float value at myChunk from fixed format string !-------------------------------------------------------------------------------------------------- real(pReal) function IO_fixedFloatValue (string,ends,myChunk) - + implicit none character(len=*), intent(in) :: string !< raw input with known ends of each chunk integer(pInt), intent(in) :: myChunk !< position number of desired chunk @@ -1086,11 +1087,11 @@ real(pReal) function IO_fixedNoEFloatValue (string,ends,myChunk) character(len=22), parameter :: MYNAME = 'IO_fixedNoEFloatValue ' character(len=13), parameter :: VALIDBASE = '0123456789.+-' character(len=12), parameter :: VALIDEXP = '0123456789+-' - + real(pReal) :: base integer(pInt) :: expon integer :: pos_exp - + pos_exp = scan(string(ends(myChunk)+1:ends(myChunk+1)),'+-',back=.true.) hasExponent: if (pos_exp > 1) then base = IO_verifyFloatValue(trim(adjustl(string(ends(myChunk)+1_pInt:ends(myChunk)+pos_exp-1_pInt))),& @@ -1135,7 +1136,7 @@ end function IO_intValue !> @brief reads integer value at myChunk from fixed format string !-------------------------------------------------------------------------------------------------- integer(pInt) function IO_fixedIntValue(string,ends,myChunk) - + implicit none character(len=*), intent(in) :: string !< raw input with known ends of each chunk integer(pInt), intent(in) :: myChunk !< position number of desired chunk @@ -1159,8 +1160,8 @@ pure function IO_lc(string) character(len=len(string)) :: IO_lc character(26), parameter :: LOWER = 'abcdefghijklmnopqrstuvwxyz' - character(26), parameter :: UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - + character(26), parameter :: UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + integer :: i,n ! no pInt (len returns default integer) IO_lc = string @@ -1178,8 +1179,8 @@ end function IO_lc subroutine IO_skipChunks(fileUnit,N) implicit none - integer(pInt), intent(in) :: fileUnit, & !< file handle - N !< minimum number of chunks to skip + integer(pInt), intent(in) :: fileUnit, & !< file handle + N !< minimum number of chunks to skip integer(pInt) :: remainingChunks character(len=65536) :: line @@ -1198,7 +1199,7 @@ end subroutine IO_skipChunks !> @brief extracts string value from key=value pair and check whether key matches !-------------------------------------------------------------------------------------------------- character(len=300) pure function IO_extractValue(pair,key) - + implicit none character(len=*), intent(in) :: pair, & !< key=value pair key !< key to be expected @@ -1221,8 +1222,8 @@ end function IO_extractValue integer(pInt) function IO_countDataLines(fileUnit) implicit none - integer(pInt), intent(in) :: fileUnit !< file handle - + integer(pInt), intent(in) :: fileUnit !< file handle + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=65536) :: line, & @@ -1236,7 +1237,7 @@ integer(pInt) function IO_countDataLines(fileUnit) chunkPos = IO_stringPos(line) tmp = IO_lc(IO_stringValue(line,chunkPos,1_pInt)) if (tmp(1:1) == '*' .and. tmp(2:2) /= '*') then ! found keyword - line = IO_read(fileUnit, .true.) ! reset IO_read + line = IO_read(fileUnit, .true.) ! reset IO_read exit else if (tmp(2:2) /= '*') IO_countDataLines = IO_countDataLines + 1_pInt @@ -1246,7 +1247,38 @@ integer(pInt) function IO_countDataLines(fileUnit) end function IO_countDataLines - + +!-------------------------------------------------------------------------------------------------- +!> @brief count lines containig data up to next *keyword +!-------------------------------------------------------------------------------------------------- +integer(pInt) function IO_countNumericalDataLines(fileUnit) + + implicit none + integer(pInt), intent(in) :: fileUnit !< file handle + + + integer(pInt), allocatable, dimension(:) :: chunkPos + character(len=65536) :: line, & + tmp + + IO_countNumericalDataLines = 0_pInt + line = '' + + do while (trim(line) /= IO_EOF) + line = IO_read(fileUnit) + chunkPos = IO_stringPos(line) + tmp = IO_lc(IO_stringValue(line,chunkPos,1_pInt)) + if (scan(tmp,"abcdefghijklmnopqrstuvwxyz")/=0) then ! found keyword + line = IO_read(fileUnit, .true.) ! reset IO_read + exit + else + IO_countNumericalDataLines = IO_countNumericalDataLines + 1_pInt + endif + enddo + backspace(fileUnit) + +end function IO_countNumericalDataLines + !-------------------------------------------------------------------------------------------------- !> @brief count items in consecutive lines depending on lines !> @details Marc: ints concatenated by "c" as last char or range of values a "to" b @@ -1257,8 +1289,8 @@ integer(pInt) function IO_countContinuousIntValues(fileUnit) implicit none integer(pInt), intent(in) :: fileUnit - -#ifdef Abaqus + +#ifdef Abaqus integer(pInt) :: l,c #endif integer(pInt), allocatable, dimension(:) :: chunkPos @@ -1272,22 +1304,22 @@ integer(pInt) function IO_countContinuousIntValues(fileUnit) line = IO_read(fileUnit) chunkPos = IO_stringPos(line) if (chunkPos(1) < 1_pInt) then ! empty line - line = IO_read(fileUnit, .true.) ! reset IO_read + line = IO_read(fileUnit, .true.) ! reset IO_read exit elseif (IO_lc(IO_stringValue(line,chunkPos,2_pInt)) == 'to' ) then ! found range indicator IO_countContinuousIntValues = 1_pInt + abs( IO_intValue(line,chunkPos,3_pInt) & - IO_intValue(line,chunkPos,1_pInt)) - line = IO_read(fileUnit, .true.) ! reset IO_read + line = IO_read(fileUnit, .true.) ! reset IO_read exit ! only one single range indicator allowed else if (IO_lc(IO_stringValue(line,chunkPos,2_pInt)) == 'of' ) then ! found multiple entries indicator IO_countContinuousIntValues = IO_intValue(line,chunkPos,1_pInt) - line = IO_read(fileUnit, .true.) ! reset IO_read + line = IO_read(fileUnit, .true.) ! reset IO_read exit ! only one single multiplier allowed else IO_countContinuousIntValues = IO_countContinuousIntValues+chunkPos(1)-1_pInt ! add line's count when assuming 'c' if ( IO_lc(IO_stringValue(line,chunkPos,chunkPos(1))) /= 'c' ) then ! line finished, read last value IO_countContinuousIntValues = IO_countContinuousIntValues+1_pInt - line = IO_read(fileUnit, .true.) ! reset IO_read + line = IO_read(fileUnit, .true.) ! reset IO_read exit ! data ended endif endif @@ -1297,7 +1329,7 @@ integer(pInt) function IO_countContinuousIntValues(fileUnit) do l = 1_pInt,c backspace(fileUnit) ! ToDo: substitute by rewind? enddo - + l = 1_pInt do while (trim(line) /= IO_EOF .and. l <= c) ! ToDo: is this correct l = l + 1_pInt @@ -1313,7 +1345,7 @@ end function IO_countContinuousIntValues !-------------------------------------------------------------------------------------------------- -!> @brief return integer list corrsponding to items in consecutive lines. +!> @brief return integer list corresponding to items in consecutive lines. !! First integer in array is counter !> @details Marc: ints concatenated by "c" as last char, range of a "to" b, or named set !! Abaqus: triplet of start,stop,inc or named set @@ -1324,7 +1356,7 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) implicit none integer(pInt), intent(in) :: maxN integer(pInt), dimension(1+maxN) :: IO_continuousIntValues - + integer(pInt), intent(in) :: fileUnit, & lookupMaxN integer(pInt), dimension(:,:), intent(in) :: lookupMap @@ -1358,7 +1390,7 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) else if (chunkPos(1) > 2_pInt .and. IO_lc(IO_stringValue(line,chunkPos,2_pInt)) == 'to' ) then ! found range indicator first = IO_intValue(line,chunkPos,1_pInt) last = IO_intValue(line,chunkPos,3_pInt) - do i = first, last, sign(1_pInt,last-first) + do i = first, last, sign(1_pInt,last-first) IO_continuousIntValues(1) = IO_continuousIntValues(1) + 1_pInt IO_continuousIntValues(1+IO_continuousIntValues(1)) = i enddo @@ -1384,7 +1416,7 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) do l = 1_pInt,c backspace(fileUnit) enddo - + !-------------------------------------------------------------------------------------------------- ! check if the element values in the elset are auto generated backspace(fileUnit) @@ -1393,7 +1425,7 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) do i = 1_pInt,chunkPos(1) if (IO_lc(IO_stringValue(line,chunkPos,i)) == 'generate') rangeGeneration = .true. enddo - + do l = 1_pInt,c read(fileUnit,'(A65536)',end=100) line chunkPos = IO_stringPos(line) @@ -1436,7 +1468,7 @@ pure function IO_intOut(intToPrint) character(len=19) :: N_Digits ! maximum digits for 64 bit integer character(len=40) :: IO_intOut integer(pInt), intent(in) :: intToPrint - + write(N_Digits, '(I19.19)') 1_pInt + int(log10(real(intToPrint)),pInt) IO_intOut = 'I'//trim(N_Digits)//'.'//trim(N_Digits) @@ -1451,7 +1483,7 @@ function IO_timeStamp() implicit none character(len=10) :: IO_timeStamp integer(pInt), dimension(8) :: values - + call DATE_AND_TIME(VALUES=values) write(IO_timeStamp,'(i2.2,a1,i2.2,a1,i2.2)') values(5),':',values(6),':',values(7) @@ -1468,11 +1500,11 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) integer(pInt), intent(in) :: error_ID integer(pInt), optional, intent(in) :: el,ip,g,instance character(len=*), optional, intent(in) :: ext_msg - + external :: quit character(len=1024) :: msg character(len=1024) :: formatString - + select case (error_ID) !-------------------------------------------------------------------------------------------------- @@ -1494,7 +1526,7 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) msg = '{input} recursion limit reached' case (105_pInt) msg = 'unknown output:' - + !-------------------------------------------------------------------------------------------------- ! lattice error messages case (130_pInt) @@ -1540,7 +1572,7 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) !-------------------------------------------------------------------------------------------------- ! plasticity error messages case (200_pInt) - msg = 'unknown elasticity specified:' + msg = 'unknown elasticity specified:' case (201_pInt) msg = 'unknown plasticity specified:' @@ -1550,12 +1582,12 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) msg = 'material parameter out of bounds:' !-------------------------------------------------------------------------------------------------- -! numerics error messages +! numerics error messages case (300_pInt) msg = 'unknown numerics parameter:' case (301_pInt) msg = 'numerics parameter out of bounds:' - + !-------------------------------------------------------------------------------------------------- ! math errors case (400_pInt) @@ -1577,7 +1609,7 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) ! homogenization errors case (500_pInt) msg = 'unknown homogenization specified' - + !-------------------------------------------------------------------------------------------------- ! user errors case (600_pInt) @@ -1638,7 +1670,7 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) msg = 'PETSc: SNES_DIVERGED_FNORM_NAN' case (894_pInt) msg = 'MPI error' - + !------------------------------------------------------------------------------------------------- ! error messages related to parsing of Abaqus input file case (900_pInt) @@ -1660,9 +1692,9 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) case (908_pInt) msg = 'size of mesh_mapFEtoCPnode in mesh_abaqus_map_nodes' case (909_pInt) - msg = 'size of mesh_node in mesh_abaqus_build_nodes not equal to mesh_Nnodes' - - + msg = 'size of mesh_node in mesh_abaqus_build_nodes not equal to mesh_Nnodes' + + !------------------------------------------------------------------------------------------------- ! general error messages case (666_pInt) @@ -1671,7 +1703,7 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) msg = 'unknown error number...' end select - + !$OMP CRITICAL (write2out) write(0,'(/,a)') ' ┌'//IO_DIVIDER//'┐' write(0,'(a,24x,a,40x,a)') ' │','error', '│' @@ -1711,7 +1743,7 @@ subroutine IO_warning(warning_ID,el,ip,g,ext_msg) integer(pInt), intent(in) :: warning_ID integer(pInt), optional, intent(in) :: el,ip,g character(len=*), optional, intent(in) :: ext_msg - + character(len=1024) :: msg character(len=1024) :: formatString @@ -1759,7 +1791,7 @@ subroutine IO_warning(warning_ID,el,ip,g,ext_msg) case default msg = 'unknown warning number' end select - + !$OMP CRITICAL (write2out) write(6,'(/,a)') ' ┌'//IO_DIVIDER//'┐' write(6,'(a,24x,a,38x,a)') ' │','warning', '│' @@ -1788,20 +1820,20 @@ end subroutine IO_warning !-------------------------------------------------------------------------------------------------- -! internal helper functions +! internal helper functions !-------------------------------------------------------------------------------------------------- !> @brief returns verified integer value in given string !-------------------------------------------------------------------------------------------------- integer(pInt) function IO_verifyIntValue (string,validChars,myName) - + implicit none - character(len=*), intent(in) :: string, & !< string for conversion to int value. Must not contain spaces! + character(len=*), intent(in) :: string, & !< string for conversion to int value. Must not contain spaces! validChars, & !< valid characters in string myName !< name of caller function (for debugging) integer(pInt) :: readStatus, invalidWhere !character(len=len(trim(string))) :: trimmed does not work with ifort 14.0.1 - + IO_verifyIntValue = 0_pInt invalidWhere = verify(string,validChars) @@ -1815,7 +1847,7 @@ integer(pInt) function IO_verifyIntValue (string,validChars,myName) if (readStatus /= 0_pInt) & ! error during string to float conversion call IO_warning(203_pInt,ext_msg=myName//'"'//string(1_pInt:invalidWhere-1_pInt)//'"') endif - + end function IO_verifyIntValue @@ -1823,15 +1855,15 @@ end function IO_verifyIntValue !> @brief returns verified float value in given string !-------------------------------------------------------------------------------------------------- real(pReal) function IO_verifyFloatValue (string,validChars,myName) - + implicit none - character(len=*), intent(in) :: string, & !< string for conversion to int value. Must not contain spaces! + character(len=*), intent(in) :: string, & !< string for conversion to int value. Must not contain spaces! validChars, & !< valid characters in string myName !< name of caller function (for debugging) integer(pInt) :: readStatus, invalidWhere !character(len=len(trim(string))) :: trimmed does not work with ifort 14.0.1 - + IO_verifyFloatValue = 0.0_pReal invalidWhere = verify(string,validChars) @@ -1845,12 +1877,12 @@ real(pReal) function IO_verifyFloatValue (string,validChars,myName) if (readStatus /= 0_pInt) & ! error during string to float conversion call IO_warning(203_pInt,ext_msg=myName//'"'//string(1_pInt:invalidWhere-1_pInt)//'"') endif - + end function IO_verifyFloatValue - -#ifdef Abaqus + +#ifdef Abaqus !-------------------------------------------------------------------------------------------------- -!> @brief create a new input file for abaqus simulations by removing all comment lines and +!> @brief create a new input file for abaqus simulations by removing all comment lines and !> including "include"s !-------------------------------------------------------------------------------------------------- recursive function abaqus_assembleInputFile(unit1,unit2) result(createSuccess) @@ -1860,7 +1892,7 @@ recursive function abaqus_assembleInputFile(unit1,unit2) result(createSuccess) implicit none integer(pInt), intent(in) :: unit1, & unit2 - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=65536) :: line,fname @@ -1894,10 +1926,10 @@ recursive function abaqus_assembleInputFile(unit1,unit2) result(createSuccess) write(unit1,'(A)') trim(line) endif enddo - + 220 createSuccess = .true. return - + 200 createSuccess =.false. end function abaqus_assembleInputFile diff --git a/src/mesh.f90 b/src/mesh.f90 index 666fe1e33..d20b61bb4 100644 --- a/src/mesh.f90 +++ b/src/mesh.f90 @@ -4,7 +4,7 @@ !> @author Christoph Koords, Max-Planck-Institut für Eisenforschung GmbH !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH !> @author Krishna Komerla, Max-Planck-Institut für Eisenforschung GmbH -!> @brief Sets up the mesh for the solvers MSC.Marc, Abaqus and the spectral solver +!> @brief Sets up the mesh for the solvers MSC.Marc, Abaqus and the spectral solver !-------------------------------------------------------------------------------------------------- module mesh use, intrinsic :: iso_c_binding @@ -45,7 +45,7 @@ module mesh mesh_element, & !< FEid, type(internal representation), material, texture, node indices as CP IDs mesh_sharedElem, & !< entryCount and list of elements containing node mesh_nodeTwins !< node twins are surface nodes that lie exactly on opposite sides of the mesh (surfaces nodes with equal coordinate values in two dimensions) - + integer(pInt), dimension(:,:,:,:), allocatable, public, protected :: & mesh_ipNeighborhood !< 6 or less neighboring IPs as [element_num, IP_index, neighbor_index that points to me] @@ -55,31 +55,33 @@ module mesh real(pReal), dimension(:,:), allocatable, public :: & mesh_node, & !< node x,y,z coordinates (after deformation! ONLY FOR MARC!!!) mesh_cellnode !< cell node x,y,z coordinates (after deformation! ONLY FOR MARC!!!) - + real(pReal), dimension(:,:), allocatable, public, protected :: & mesh_ipVolume, & !< volume associated with IP (initially!) mesh_node0 !< node x,y,z coordinates (initially!) real(pReal), dimension(:,:,:), allocatable, public, protected :: & mesh_ipArea !< area of interface to neighboring IP (initially!) - + real(pReal), dimension(:,:,:), allocatable, public :: & mesh_ipCoordinates !< IP x,y,z coordinates (after deformation!) - real(pReal),dimension(:,:,:,:), allocatable, public, protected :: & + real(pReal),dimension(:,:,:,:), allocatable, public, protected :: & mesh_ipAreaNormal !< area normal of interface to neighboring IP (initially!) - + logical, dimension(3), public, protected :: mesh_periodicSurface !< flag indicating periodic outer surfaces (used for fluxes) #ifdef Marc4DAMASK - integer(pInt), private :: & + integer(pInt), private :: & + MarcVersion, & !< Version of input file format (Marc only) hypoelasticTableStyle, & !< Table style (Marc only) - initialcondTableStyle !< Table style (Marc only) + initialcondTableStyle, & !< Table style (Marc only) + Marc_matNumber !< Material number of hypoelastic material (Marc only) #endif - + integer(pInt), dimension(2), private :: & mesh_maxValStateVar = 0_pInt - + #ifndef Spectral character(len=64), dimension(:), allocatable, private :: & mesh_nameElemSet, & !< names of elementSet @@ -104,13 +106,13 @@ module mesh FE_ipNeighbor, & !< +x,-x,+y,-y,+z,-z list of intra-element IPs and(negative) neighbor faces per own IP in a specific type of element FE_cell, & !< list of intra-element cell node IDs that constitute the cells in a specific type of element geometry FE_cellface !< list of intra-cell cell node IDs that constitute the cell faces of a specific type of cell - + real(pReal), dimension(:,:,:), allocatable, private :: & FE_cellnodeParentnodeWeights !< list of node weights for the generation of cell nodes - + integer(pInt), dimension(:,:,:,:), allocatable, private :: & FE_subNodeOnIPFace - + #ifdef Abaqus logical, private :: noPart !< for cases where the ABAQUS input file does not use part/assembly information #endif @@ -137,7 +139,7 @@ module mesh FE_maxNcellnodesPerCell = 8_pInt, & FE_maxNcellfaces = 6_pInt, & FE_maxNcellnodesPerCellface = 4_pInt - + integer(pInt), dimension(FE_Nelemtypes), parameter, public :: FE_geomtype = & !< geometry type of particular element type int([ & 1, & ! element 6 (2D 3node 1ip) @@ -241,7 +243,7 @@ module mesh 4,4,4,4,4,4, & ! element 117 (3D 8node 1ip) 4,4,4,4,4,4, & ! element 7 (3D 8node 8ip) 4,4,4,4,4,4 & ! element 21 (3D 20node 27ip) - ],pInt),[FE_maxNipNeighbors,FE_Ngeomtypes]) + ],pInt),[FE_maxNipNeighbors,FE_Ngeomtypes]) integer(pInt), dimension(FE_maxNmatchingNodesPerFace,FE_maxNfaces,FE_Ngeomtypes), & parameter, private :: FE_face = & !< List of node indices on each face of a specific type of element geometry @@ -375,7 +377,7 @@ module mesh 4 & ! element 21 (3D 20node 27ip) ],pInt) - + integer(pInt), dimension(FE_Nelemtypes), parameter, private :: MESH_VTKELEMTYPE = & int([ & 5, & ! element 6 (2D 3node 1ip) @@ -428,13 +430,15 @@ module mesh mesh_spectral_build_elements, & mesh_spectral_build_ipNeighborhood, & #elif defined Marc4DAMASK + mesh_marc_get_fileFormat, & mesh_marc_get_tableStyles, & + mesh_marc_get_matNumber, & mesh_marc_count_nodesAndElements, & mesh_marc_count_elementSets, & mesh_marc_map_elementSets, & mesh_marc_count_cpElements, & mesh_marc_map_Elements, & - mesh_marc_map_nodes, & + mesh_marc_map_nodes, & mesh_marc_build_nodes, & mesh_marc_count_cpSizes, & mesh_marc_build_elements, & @@ -450,7 +454,7 @@ module mesh mesh_abaqus_build_nodes, & mesh_abaqus_count_cpSizes, & mesh_abaqus_build_elements, & -#endif +#endif #ifndef Spectral mesh_build_nodeTwins, & mesh_build_sharedElems, & @@ -508,7 +512,7 @@ subroutine mesh_init(ip,el) #endif FEsolving_execIP, & calcMode - + implicit none #ifdef Spectral integer(C_INTPTR_T) :: devNull, local_K, local_K_offset @@ -518,7 +522,7 @@ subroutine mesh_init(ip,el) integer(pInt), intent(in) :: el, ip integer(pInt) :: j logical :: myDebug - + external :: MPI_comm_size write(6,'(/,a)') ' <<<+- mesh init -+>>>' @@ -546,7 +550,7 @@ subroutine mesh_init(ip,el) if (allocated(FE_subNodeOnIPFace)) deallocate(FE_subNodeOnIPFace) call mesh_build_FEdata ! get properties of the different types of elements mesh_unitlength = numerics_unitlength ! set physical extent of a length unit in mesh - + myDebug = (iand(debug_level(debug_mesh),debug_levelBasic) /= 0_pInt) #ifdef Spectral @@ -579,8 +583,14 @@ subroutine mesh_init(ip,el) #elif defined Marc4DAMASK call IO_open_inputFile(FILEUNIT,modelName) ! parse info from input file... if (myDebug) write(6,'(a)') ' Opened input file'; flush(6) + call mesh_marc_get_fileFormat(FILEUNIT) + if (myDebug) write(6,'(a)') ' Got input file format'; flush(6) call mesh_marc_get_tableStyles(FILEUNIT) if (myDebug) write(6,'(a)') ' Got table styles'; flush(6) + if (MarcVersion > 12) then + call mesh_marc_get_matNumber(FILEUNIT) + if (myDebug) write(6,'(a)') ' Got hypoleastic material number'; flush(6) + endif call mesh_marc_count_nodesAndElements(FILEUNIT) if (myDebug) write(6,'(a)') ' Counted nodes/elements'; flush(6) call mesh_marc_count_elementSets(FILEUNIT) @@ -662,12 +672,12 @@ subroutine mesh_init(ip,el) call IO_error(602_pInt,ext_msg='element') ! selected element does not exist if (debug_i < 1 .or. debug_i > FE_Nips(FE_geomtype(mesh_element(2_pInt,debug_e)))) & call IO_error(602_pInt,ext_msg='IP') ! selected element does not have requested IP - + FEsolving_execElem = [ 1_pInt,mesh_NcpElems ] ! parallel loop bounds set to comprise all DAMASK elements if (allocated(FEsolving_execIP)) deallocate(FEsolving_execIP) allocate(FEsolving_execIP(2_pInt,mesh_NcpElems)); FEsolving_execIP = 1_pInt ! parallel loop bounds set to comprise from first IP... forall (j = 1_pInt:mesh_NcpElems) FEsolving_execIP(2,j) = FE_Nips(FE_geomtype(mesh_element(2,j))) ! ...up to own IP count for each element - + if (allocated(calcMode)) deallocate(calcMode) allocate(calcMode(mesh_maxNips,mesh_NcpElems)) calcMode = .false. ! pretend to have collected what first call is asking (F = I) @@ -688,10 +698,10 @@ integer(pInt) function mesh_FEasCP(what,myID) implicit none character(len=*), intent(in) :: what integer(pInt), intent(in) :: myID - + integer(pInt), dimension(:,:), pointer :: lookupMap integer(pInt) :: lower,upper,center - + mesh_FEasCP = 0_pInt select case(IO_lc(what(1:4))) case('elem') @@ -701,10 +711,10 @@ integer(pInt) function mesh_FEasCP(what,myID) case default return endselect - + lower = 1_pInt upper = int(size(lookupMap,2_pInt),pInt) - + if (lookupMap(1_pInt,lower) == myID) then ! check at bounds QUESTION is it valid to extend bounds by 1 and just do binary search w/o init check at bounds? mesh_FEasCP = lookupMap(2_pInt,lower) return @@ -723,19 +733,19 @@ integer(pInt) function mesh_FEasCP(what,myID) exit endif enddo binarySearch - + end function mesh_FEasCP !-------------------------------------------------------------------------------------------------- !> @brief Split CP elements into cells. -!> @details Build a mapping between cells and the corresponding cell nodes ('mesh_cell'). -!> Cell nodes that are also matching nodes are unique in the list of cell nodes, +!> @details Build a mapping between cells and the corresponding cell nodes ('mesh_cell'). +!> Cell nodes that are also matching nodes are unique in the list of cell nodes, !> all others (currently) might be stored more than once. !> Also allocates the 'mesh_node' array. !-------------------------------------------------------------------------------------------------- subroutine mesh_build_cellconnectivity - + implicit none integer(pInt), dimension(:), allocatable :: & matchingNode2cellnode @@ -744,14 +754,14 @@ subroutine mesh_build_cellconnectivity integer(pInt), dimension(mesh_maxNcellnodes) :: & localCellnode2globalCellnode integer(pInt) :: & - e,t,g,c,n,i, & + e,t,g,c,n,i, & matchingNodeID, & localCellnodeID - + allocate(mesh_cell(FE_maxNcellnodesPerCell,mesh_maxNips,mesh_NcpElems), source=0_pInt) allocate(matchingNode2cellnode(mesh_Nnodes), source=0_pInt) allocate(cellnodeParent(2_pInt,mesh_maxNcellnodes*mesh_NcpElems), source=0_pInt) - + !-------------------------------------------------------------------------------------------------- ! Count cell nodes (including duplicates) and generate cell connectivity list mesh_Ncellnodes = 0_pInt @@ -796,28 +806,28 @@ subroutine mesh_build_cellconnectivity deallocate(matchingNode2cellnode) deallocate(cellnodeParent) - + end subroutine mesh_build_cellconnectivity !-------------------------------------------------------------------------------------------------- !> @brief Calculate position of cellnodes from the given position of nodes -!> Build list of cellnodes' coordinates. +!> Build list of cellnodes' coordinates. !> Cellnode coordinates are calculated from a weighted sum of node coordinates. !-------------------------------------------------------------------------------------------------- function mesh_build_cellnodes(nodes,Ncellnodes) - + implicit none integer(pInt), intent(in) :: Ncellnodes !< requested number of cellnodes real(pReal), dimension(3,mesh_Nnodes), intent(in) :: nodes real(pReal), dimension(3,Ncellnodes) :: mesh_build_cellnodes integer(pInt) :: & - e,t,n,m, & + e,t,n,m, & localCellnodeID real(pReal), dimension(3) :: & myCoords - + mesh_build_cellnodes = 0.0_pReal !$OMP PARALLEL DO PRIVATE(e,localCellnodeID,t,myCoords) do n = 1_pInt,Ncellnodes ! loop over cell nodes @@ -842,23 +852,23 @@ end function mesh_build_cellnodes !> 2D cells assume an element depth of one in order to calculate the volume. !> For the hexahedral cell we subdivide the cell into subvolumes of pyramidal !> shape with a cell face as basis and the central ip at the tip. This subvolume is -!> calculated as an average of four tetrahedals with three corners on the cell face +!> calculated as an average of four tetrahedals with three corners on the cell face !> and one corner at the central ip. !-------------------------------------------------------------------------------------------------- subroutine mesh_build_ipVolumes use math, only: & math_volTetrahedron, & math_areaTriangle - + implicit none integer(pInt) :: e,t,g,c,i,m,f,n real(pReal), dimension(FE_maxNcellnodesPerCellface,FE_maxNcellfaces) :: subvolume if (.not. allocated(mesh_ipVolume)) then allocate(mesh_ipVolume(mesh_maxNips,mesh_NcpElems)) - mesh_ipVolume = 0.0_pReal + mesh_ipVolume = 0.0_pReal endif - + !$OMP PARALLEL DO PRIVATE(t,g,c,m,subvolume) do e = 1_pInt,mesh_NcpElems ! loop over cpElems t = mesh_element(2_pInt,e) ! get element type @@ -871,7 +881,7 @@ subroutine mesh_build_ipVolumes mesh_ipVolume(i,e) = math_areaTriangle(mesh_cellnode(1:3,mesh_cell(1,i,e)), & mesh_cellnode(1:3,mesh_cell(2,i,e)), & mesh_cellnode(1:3,mesh_cell(3,i,e))) - + case (2_pInt) ! 2D 4node forall (i = 1_pInt:FE_Nips(g)) & ! loop over ips=cells in this element mesh_ipVolume(i,e) = math_areaTriangle(mesh_cellnode(1:3,mesh_cell(1,i,e)), & ! here we assume a planar shape, so division in two triangles suffices @@ -916,19 +926,19 @@ end subroutine mesh_build_ipVolumes ! so in this case the ip coordinates are always calculated on the basis of this subroutine. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! FOR THE MOMENT THIS SUBROUTINE ACTUALLY CALCULATES THE CELL CENTER AND NOT THE IP COORDINATES, -! AS THE IP IS NOT (ALWAYS) LOCATED IN THE CENTER OF THE IP VOLUME. -! HAS TO BE CHANGED IN A LATER VERSION. +! AS THE IP IS NOT (ALWAYS) LOCATED IN THE CENTER OF THE IP VOLUME. +! HAS TO BE CHANGED IN A LATER VERSION. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !-------------------------------------------------------------------------------------------------- subroutine mesh_build_ipCoordinates - + implicit none integer(pInt) :: e,t,g,c,i,n real(pReal), dimension(3) :: myCoords if (.not. allocated(mesh_ipCoordinates)) & allocate(mesh_ipCoordinates(3,mesh_maxNips,mesh_NcpElems),source=0.0_pReal) - + !$OMP PARALLEL DO PRIVATE(t,g,c,myCoords) do e = 1_pInt,mesh_NcpElems ! loop over cpElems t = mesh_element(2_pInt,e) ! get element type @@ -951,13 +961,13 @@ end subroutine mesh_build_ipCoordinates !> @brief Calculates cell center coordinates. !-------------------------------------------------------------------------------------------------- pure function mesh_cellCenterCoordinates(ip,el) - + implicit none integer(pInt), intent(in) :: el, & !< element number ip !< integration point number real(pReal), dimension(3) :: mesh_cellCenterCoordinates !< x,y,z coordinates of the cell center of the requested IP cell integer(pInt) :: t,g,c,n - + t = mesh_element(2_pInt,el) ! get element type g = FE_geomtype(t) ! get geometry type c = FE_celltype(g) ! get cell type @@ -972,7 +982,7 @@ pure function mesh_cellCenterCoordinates(ip,el) #ifdef Spectral !-------------------------------------------------------------------------------------------------- -!> @brief Reads grid information from geometry file. If fileUnit is given, +!> @brief Reads grid information from geometry file. If fileUnit is given, !! assumes an opened file, otherwise tries to open the one specified in geometryFile !-------------------------------------------------------------------------------------------------- function mesh_spectral_getGrid(fileUnit) @@ -987,7 +997,7 @@ function mesh_spectral_getGrid(fileUnit) IO_error use DAMASK_interface, only: & geometryFile - + implicit none integer(pInt), dimension(3) :: mesh_spectral_getGrid integer(pInt), intent(in), optional :: fileUnit @@ -998,7 +1008,7 @@ function mesh_spectral_getGrid(fileUnit) keyword integer(pInt) :: i, j, myFileUnit logical :: gotGrid = .false. - + mesh_spectral_getGrid = -1_pInt if(.not. present(fileUnit)) then myFileUnit = 289_pInt @@ -1006,7 +1016,7 @@ function mesh_spectral_getGrid(fileUnit) else myFileUnit = fileUnit endif - + call IO_checkAndRewind(myFileUnit) read(myFileUnit,'(a1024)') line @@ -1020,7 +1030,7 @@ function mesh_spectral_getGrid(fileUnit) rewind(myFileUnit) do i = 1_pInt, headerLength read(myFileUnit,'(a1024)') line - chunkPos = IO_stringPos(line) + chunkPos = IO_stringPos(line) select case ( IO_lc(IO_StringValue(line,chunkPos,1_pInt,.true.)) ) case ('grid') gotGrid = .true. @@ -1036,9 +1046,9 @@ function mesh_spectral_getGrid(fileUnit) enddo end select enddo - + if(.not. present(fileUnit)) close(myFileUnit) - + if (.not. gotGrid) & call IO_error(error_ID = 845_pInt, ext_msg='grid') if(any(mesh_spectral_getGrid < 1_pInt)) & @@ -1048,7 +1058,7 @@ end function mesh_spectral_getGrid !-------------------------------------------------------------------------------------------------- -!> @brief Reads size information from geometry file. If fileUnit is given, +!> @brief Reads size information from geometry file. If fileUnit is given, !! assumes an opened file, otherwise tries to open the one specified in geometryFile !-------------------------------------------------------------------------------------------------- function mesh_spectral_getSize(fileUnit) @@ -1063,7 +1073,7 @@ function mesh_spectral_getSize(fileUnit) IO_error use DAMASK_interface, only: & geometryFile - + implicit none real(pReal), dimension(3) :: mesh_spectral_getSize integer(pInt), intent(in), optional :: fileUnit @@ -1071,9 +1081,9 @@ function mesh_spectral_getSize(fileUnit) integer(pInt) :: headerLength = 0_pInt character(len=1024) :: line, & keyword - integer(pInt) :: i, j, myFileUnit + integer(pInt) :: i, j, myFileUnit logical :: gotSize = .false. - + mesh_spectral_getSize = -1.0_pReal if(.not. present(fileUnit)) then myFileUnit = 289_pInt @@ -1081,7 +1091,7 @@ function mesh_spectral_getSize(fileUnit) else myFileUnit = fileUnit endif - + call IO_checkAndRewind(myFileUnit) read(myFileUnit,'(a1024)') line @@ -1095,7 +1105,7 @@ function mesh_spectral_getSize(fileUnit) rewind(myFileUnit) do i = 1_pInt, headerLength read(myFileUnit,'(a1024)') line - chunkPos = IO_stringPos(line) + chunkPos = IO_stringPos(line) select case ( IO_lc(IO_StringValue(line,chunkPos,1,.true.)) ) case ('size') gotSize = .true. @@ -1111,7 +1121,7 @@ function mesh_spectral_getSize(fileUnit) enddo end select enddo - + if(.not. present(fileUnit)) close(myFileUnit) if (.not. gotSize) & @@ -1123,7 +1133,7 @@ end function mesh_spectral_getSize !-------------------------------------------------------------------------------------------------- -!> @brief Reads homogenization information from geometry file. If fileUnit is given, +!> @brief Reads homogenization information from geometry file. If fileUnit is given, !! assumes an opened file, otherwise tries to open the one specified in geometryFile !-------------------------------------------------------------------------------------------------- integer(pInt) function mesh_spectral_getHomogenization(fileUnit) @@ -1137,7 +1147,7 @@ integer(pInt) function mesh_spectral_getHomogenization(fileUnit) IO_error use DAMASK_interface, only: & geometryFile - + implicit none integer(pInt), intent(in), optional :: fileUnit integer(pInt), allocatable, dimension(:) :: chunkPos @@ -1146,7 +1156,7 @@ integer(pInt) function mesh_spectral_getHomogenization(fileUnit) keyword integer(pInt) :: i, myFileUnit logical :: gotHomogenization = .false. - + mesh_spectral_getHomogenization = -1_pInt if(.not. present(fileUnit)) then myFileUnit = 289_pInt @@ -1154,7 +1164,7 @@ integer(pInt) function mesh_spectral_getHomogenization(fileUnit) else myFileUnit = fileUnit endif - + call IO_checkAndRewind(myFileUnit) read(myFileUnit,'(a1024)') line @@ -1168,21 +1178,21 @@ integer(pInt) function mesh_spectral_getHomogenization(fileUnit) rewind(myFileUnit) do i = 1_pInt, headerLength read(myFileUnit,'(a1024)') line - chunkPos = IO_stringPos(line) + chunkPos = IO_stringPos(line) select case ( IO_lc(IO_StringValue(line,chunkPos,1,.true.)) ) case ('homogenization') gotHomogenization = .true. mesh_spectral_getHomogenization = IO_intValue(line,chunkPos,2_pInt) end select enddo - + if(.not. present(fileUnit)) close(myFileUnit) - + if (.not. gotHomogenization ) & call IO_error(error_ID = 845_pInt, ext_msg='homogenization') if (mesh_spectral_getHomogenization<1_pInt) & call IO_error(error_ID = 842_pInt, ext_msg='mesh_spectral_getHomogenization') - + end function mesh_spectral_getHomogenization @@ -1197,7 +1207,7 @@ subroutine mesh_spectral_count() mesh_Nelems = product(grid(1:2))*grid3 mesh_NcpElems= mesh_Nelems mesh_Nnodes = product(grid(1:2) + 1_pInt)*(grid3 + 1_pInt) - + mesh_NcpElemsGlobal = product(grid) end subroutine mesh_spectral_count @@ -1223,14 +1233,14 @@ end subroutine mesh_spectral_mapNodesAndElems !-------------------------------------------------------------------------------------------------- !> @brief Gets maximum count of nodes, IPs, IP neighbors, and subNodes among cpElements. -!! Sets global values 'mesh_maxNnodes', 'mesh_maxNips', 'mesh_maxNipNeighbors', +!! Sets global values 'mesh_maxNnodes', 'mesh_maxNips', 'mesh_maxNipNeighbors', !! and 'mesh_maxNcellnodes' !-------------------------------------------------------------------------------------------------- subroutine mesh_spectral_count_cpSizes - + implicit none integer(pInt) :: t,g,c - + t = FE_mapElemtype('C3D8R') ! fake 3D hexahedral 8 node 1 IP element g = FE_geomtype(t) c = FE_celltype(g) @@ -1254,7 +1264,7 @@ subroutine mesh_spectral_build_nodes() allocate (mesh_node0 (3,mesh_Nnodes), source = 0.0_pReal) allocate (mesh_node (3,mesh_Nnodes), source = 0.0_pReal) - + forall (n = 0_pInt:mesh_Nnodes-1_pInt) mesh_node0(1,n+1_pInt) = mesh_unitlength * & geomSize(1)*real(mod(n,(grid(1)+1_pInt) ),pReal) & @@ -1265,8 +1275,8 @@ subroutine mesh_spectral_build_nodes() mesh_node0(3,n+1_pInt) = mesh_unitlength * & size3*real(mod(n/(grid(1)+1_pInt)/(grid(2)+1_pInt),(grid3+1_pInt)),pReal) & / real(grid3,pReal) + & - size3offset - end forall + size3offset + end forall mesh_node = mesh_node0 @@ -1324,7 +1334,7 @@ subroutine mesh_spectral_build_elements(fileUnit) else call IO_error(error_ID=841_pInt, ext_msg='mesh_spectral_build_elements') endif - + !-------------------------------------------------------------------------------------------------- ! get maximum microstructure index call IO_checkAndRewind(fileUnit) @@ -1349,7 +1359,7 @@ subroutine mesh_spectral_build_elements(fileUnit) do i=1_pInt,headerLength read(fileUnit,'(a65536)') line enddo - + e = 0_pInt do while (e < mesh_NcpElemsGlobal .and. microstructures(1) > 0_pInt) ! fill expected number of elements, stop at end of data (or blank line!) microstructures = IO_continuousIntValues(fileUnit,maxIntCount,dummyName,dummySet,0_pInt) ! get affected elements @@ -1359,7 +1369,7 @@ subroutine mesh_spectral_build_elements(fileUnit) enddo enddo - elemType = FE_mapElemtype('C3D8R') + elemType = FE_mapElemtype('C3D8R') elemOffset = product(grid(1:2))*grid3Offset e = 0_pInt do while (e < mesh_NcpElems) ! fill expected number of elements, stop at end of data (or blank line!) @@ -1378,7 +1388,7 @@ subroutine mesh_spectral_build_elements(fileUnit) mesh_element(11,e) = mesh_element(9,e) + grid(1) + 2_pInt mesh_element(12,e) = mesh_element(9,e) + grid(1) + 1_pInt mesh_maxValStateVar(1) = max(mesh_maxValStateVar(1),mesh_element(3,e)) ! needed for statistics - mesh_maxValStateVar(2) = max(mesh_maxValStateVar(2),mesh_element(4,e)) + mesh_maxValStateVar(2) = max(mesh_maxValStateVar(2),mesh_element(4,e)) enddo deallocate(microstructures) @@ -1399,7 +1409,7 @@ subroutine mesh_spectral_build_ipNeighborhood x,y,z, & e allocate(mesh_ipNeighborhood(3,mesh_maxNipNeighbors,mesh_maxNips,mesh_NcpElems),source=0_pInt) - + e = 0_pInt do z = 0_pInt,grid3-1_pInt do y = 0_pInt,grid(2)-1_pInt @@ -1453,7 +1463,7 @@ function mesh_nodesAroundCentres(gDim,Favg,centres) result(nodes) debug_levelBasic use math, only: & math_mul33x3 - + implicit none real(pReal), intent(in), dimension(:,:,:,:) :: & centres @@ -1491,7 +1501,7 @@ function mesh_nodesAroundCentres(gDim,Favg,centres) result(nodes) iRes = [size(centres,2),size(centres,3),size(centres,4)] nodes = 0.0_pReal wrappedCentres = 0.0_pReal - + !-------------------------------------------------------------------------------------------------- ! report if (iand(debug_level(debug_mesh),debug_levelBasic) /= 0_pInt) then @@ -1517,7 +1527,7 @@ function mesh_nodesAroundCentres(gDim,Favg,centres) result(nodes) - math_mul33x3(Favg, real(shift,pReal)*gDim) endif enddo; enddo; enddo - + !-------------------------------------------------------------------------------------------------- ! averaging do k = 0_pInt,iRes(3); do j = 0_pInt,iRes(2); do i = 0_pInt,iRes(1) @@ -1532,10 +1542,41 @@ function mesh_nodesAroundCentres(gDim,Favg,centres) result(nodes) end function mesh_nodesAroundCentres #endif - + #ifdef Marc4DAMASK !-------------------------------------------------------------------------------------------------- -!> @brief Figures out table styles (Marc only) and stores to 'initialcondTableStyle' and +!> @brief Figures out version of Marc input file format and stores ist as MarcVersion +!-------------------------------------------------------------------------------------------------- +subroutine mesh_marc_get_fileFormat(fileUnit) + use IO, only: & + IO_lc, & + IO_intValue, & + IO_stringValue, & + IO_stringPos + + implicit none + integer(pInt), intent(in) :: fileUnit + + integer(pInt), allocatable, dimension(:) :: chunkPos + character(len=300) line + +610 FORMAT(A300) + + rewind(fileUnit) + do + read (fileUnit,610,END=620) line + chunkPos = IO_stringPos(line) + if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'version') then + MarcVersion = IO_intValue(line,chunkPos,2_pInt) + exit + endif + enddo + +620 end subroutine mesh_marc_get_fileFormat + + +!-------------------------------------------------------------------------------------------------- +!> @brief Figures out table styles (Marc only) and stores to 'initialcondTableStyle' and !! 'hypoelasticTableStyle' !-------------------------------------------------------------------------------------------------- subroutine mesh_marc_get_tableStyles(fileUnit) @@ -1544,20 +1585,20 @@ subroutine mesh_marc_get_tableStyles(fileUnit) IO_intValue, & IO_stringValue, & IO_stringPos - + implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) line initialcondTableStyle = 0_pInt hypoelasticTableStyle = 0_pInt - + 610 FORMAT(A300) rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) @@ -1570,6 +1611,41 @@ subroutine mesh_marc_get_tableStyles(fileUnit) 620 end subroutine mesh_marc_get_tableStyles +!-------------------------------------------------------------------------------------------------- +!> @brief Figures out material number of hypoelastic material and sores it as Marc_matNumber +!-------------------------------------------------------------------------------------------------- +subroutine mesh_marc_get_matNumber(fileUnit) + use IO, only: & + IO_lc, & + IO_intValue, & + IO_stringValue, & + IO_stringPos + + implicit none + integer(pInt), intent(in) :: fileUnit + + integer(pInt), allocatable, dimension(:) :: chunkPos + integer(pInt) :: i + character(len=300) line + +610 FORMAT(A300) + + rewind(fileUnit) + + do + read (fileUnit,610,END=620) line + chunkPos = IO_stringPos(line) + if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'hypoelastic') then + do i=1_pInt,1_pInt+hypoelasticTableStyle ! Skip 1 or 2 lines + read (fileUnit,610,END=620) line + enddo + Marc_matNumber = IO_intValue(line,chunkPos,1_pInt) + exit + endif + enddo + +620 end subroutine mesh_marc_get_matNumber + !-------------------------------------------------------------------------------------------------- !> @brief Count overall number of nodes and elements in mesh and stores the numbers in @@ -1581,10 +1657,10 @@ subroutine mesh_marc_count_nodesAndElements(fileUnit) IO_stringValue, & IO_stringPos, & IO_IntValue - + implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) line @@ -1594,7 +1670,7 @@ subroutine mesh_marc_count_nodesAndElements(fileUnit) 610 FORMAT(A300) rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) @@ -1621,7 +1697,7 @@ subroutine mesh_marc_count_nodesAndElements(fileUnit) IO_stringValue, & IO_stringPos, & IO_countContinuousIntValues - + implicit none integer(pInt), intent(in) :: fileUnit @@ -1634,7 +1710,7 @@ subroutine mesh_marc_count_nodesAndElements(fileUnit) 610 FORMAT(A300) rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) @@ -1663,7 +1739,7 @@ subroutine mesh_marc_map_elementSets(fileUnit) implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) :: line integer(pInt) :: elemSet = 0_pInt @@ -1685,7 +1761,7 @@ subroutine mesh_marc_map_elementSets(fileUnit) IO_continuousIntValues(fileUnit,mesh_maxNelemInSet,mesh_nameElemSet,mesh_mapElemSet,mesh_NelemSets) endif enddo - + 640 end subroutine mesh_marc_map_elementSets @@ -1699,13 +1775,14 @@ subroutine mesh_marc_count_cpElements(fileUnit) IO_stringPos, & IO_countContinuousIntValues, & IO_error, & - IO_intValue - + IO_intValue, & + IO_countNumericalDataLines + implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos - integer(pInt) :: i, version + integer(pInt) :: i character(len=300):: line mesh_NcpElems = 0_pInt @@ -1713,29 +1790,33 @@ subroutine mesh_marc_count_cpElements(fileUnit) 610 FORMAT(A300) rewind(fileUnit) - do - read (fileUnit,610,END=620) line - chunkPos = IO_stringPos(line) - if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'version') then - version = IO_intValue(line,chunkPos,2_pInt) - if (version < 13) then ! Marc 2016 or earlier - rewind(fileUnit) - do + if (MarcVersion < 13) then ! Marc 2016 or earlier + do + read (fileUnit,610,END=620) line + chunkPos = IO_stringPos(line) + if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'hypoelastic') then + do i=1_pInt,3_pInt+hypoelasticTableStyle ! Skip 3 or 4 lines read (fileUnit,610,END=620) line - chunkPos = IO_stringPos(line) - if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'hypoelastic') then - do i=1_pInt,3_pInt+hypoelasticTableStyle ! Skip 3 or 4 lines - read (fileUnit,610,END=620) line - enddo - mesh_NcpElems = mesh_NcpElems + IO_countContinuousIntValues(fileUnit) ! why not simply mesh_NcpElems = IO_countContinuousIntValues(fileUnit)? keyword hypoelastic might appear several times - exit - endif enddo - else ! Marc2017 and later - call IO_error(error_ID=701_pInt) - end if - end if - enddo + mesh_NcpElems = mesh_NcpElems + IO_countContinuousIntValues(fileUnit) ! why not simply mesh_NcpElems = IO_countContinuousIntValues(fileUnit)? keyword hypoelastic might appear several times so the next line probably should not be there + exit + endif + enddo + else ! Marc2017 and later + call IO_error(error_ID=701_pInt) + do + read (fileUnit,610,END=620) line + chunkPos = IO_stringPos(line) + if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'connectivity') then + read (fileUnit,610,END=620) line + chunkPos = IO_stringPos(line) + if (IO_intValue(line,chunkPos,6_pInt)==Marc_matNumber) then + mesh_NcpElems = mesh_NcpElems + IO_countNumericalDataLines(fileUnit) + exit + endif + endif + enddo + end if 620 end subroutine mesh_marc_count_cpElements @@ -1799,7 +1880,7 @@ subroutine mesh_marc_map_nodes(fileUnit) IO_stringValue, & IO_stringPos, & IO_fixedIntValue - + implicit none integer(pInt), intent(in) :: fileUnit @@ -1831,7 +1912,7 @@ subroutine mesh_marc_map_nodes(fileUnit) enddo 650 call math_qsort(mesh_mapFEtoCPnode,1_pInt,int(size(mesh_mapFEtoCPnode,2_pInt),pInt)) - + end subroutine mesh_marc_map_nodes @@ -1885,11 +1966,11 @@ end subroutine mesh_marc_build_nodes !-------------------------------------------------------------------------------------------------- !> @brief Gets maximum count of nodes, IPs, IP neighbors, and cellnodes among cpElements. -!! Sets global values 'mesh_maxNnodes', 'mesh_maxNips', 'mesh_maxNipNeighbors', +!! Sets global values 'mesh_maxNnodes', 'mesh_maxNips', 'mesh_maxNipNeighbors', !! and 'mesh_maxNcellnodes' !-------------------------------------------------------------------------------------------------- subroutine mesh_marc_count_cpSizes(fileUnit) - + use IO, only: IO_lc, & IO_stringValue, & IO_stringPos, & @@ -1898,7 +1979,7 @@ subroutine mesh_marc_count_cpSizes(fileUnit) implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) :: line integer(pInt) :: i,t,g,e,c @@ -1907,7 +1988,7 @@ subroutine mesh_marc_count_cpSizes(fileUnit) mesh_maxNips = 0_pInt mesh_maxNipNeighbors = 0_pInt mesh_maxNcellnodes = 0_pInt - + 610 FORMAT(A300) rewind(fileUnit) do @@ -1917,7 +1998,7 @@ subroutine mesh_marc_count_cpSizes(fileUnit) read (fileUnit,610,END=630) line ! Garbage line do i=1_pInt,mesh_Nelems ! read all elements read (fileUnit,610,END=630) line - chunkPos = IO_stringPos(line) ! limit to id and type + chunkPos = IO_stringPos(line) ! limit to id and type e = mesh_FEasCP('elem',IO_intValue(line,chunkPos,1_pInt)) if (e /= 0_pInt) then t = FE_mapElemtype(IO_stringValue(line,chunkPos,2_pInt)) @@ -1927,13 +2008,13 @@ subroutine mesh_marc_count_cpSizes(fileUnit) mesh_maxNips = max(mesh_maxNips,FE_Nips(g)) mesh_maxNipNeighbors = max(mesh_maxNipNeighbors,FE_NipNeighbors(c)) mesh_maxNcellnodes = max(mesh_maxNcellnodes,FE_Ncellnodes(g)) - call IO_skipChunks(fileUnit,FE_Nnodes(t)-(chunkPos(1_pInt)-2_pInt)) ! read on if FE_Nnodes exceeds node count present on current line + call IO_skipChunks(fileUnit,FE_Nnodes(t)-(chunkPos(1_pInt)-2_pInt)) ! read on if FE_Nnodes exceeds node count present on current line endif enddo exit endif enddo - + 630 end subroutine mesh_marc_count_cpSizes @@ -1981,7 +2062,7 @@ subroutine mesh_marc_build_elements(fileUnit) nNodesAlreadyRead = 0_pInt do j = 1_pInt,chunkPos(1)-2_pInt mesh_element(4_pInt+j,e) = mesh_FEasCP('node',IO_IntValue(line,chunkPos,j+2_pInt)) ! CP ids of nodes - enddo + enddo nNodesAlreadyRead = chunkPos(1) - 2_pInt do while(nNodesAlreadyRead < FE_Nnodes(t)) ! read on if not all nodes in one line read (fileUnit,610,END=620) line @@ -1997,7 +2078,7 @@ subroutine mesh_marc_build_elements(fileUnit) exit endif enddo - + 620 rewind(fileUnit) ! just in case "initial state" appears before "connectivity" read (fileUnit,610,END=620) line do @@ -2029,13 +2110,13 @@ subroutine mesh_marc_build_elements(fileUnit) chunkPos = IO_stringPos(line) enddo endif - else + else read (fileUnit,610,END=630) line endif enddo 630 end subroutine mesh_marc_build_elements -#endif +#endif #ifdef Abaqus !-------------------------------------------------------------------------------------------------- @@ -2049,28 +2130,28 @@ subroutine mesh_abaqus_count_nodesAndElements(fileUnit) IO_stringPos, & IO_countDataLines, & IO_error - + implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) :: line logical :: inPart mesh_Nnodes = 0_pInt mesh_Nelems = 0_pInt - + 610 FORMAT(A300) inPart = .false. rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*part' ) inPart = .true. if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*end' .and. & IO_lc(IO_stringValue(line,chunkPos,2_pInt)) == 'part' ) inPart = .false. - + if (inPart .or. noPart) then select case ( IO_lc(IO_stringValue(line,chunkPos,1_pInt))) case('*node') @@ -2092,10 +2173,10 @@ subroutine mesh_abaqus_count_nodesAndElements(fileUnit) endselect endif enddo - + 620 if (mesh_Nnodes < 2_pInt) call IO_error(error_ID=900_pInt) if (mesh_Nelems == 0_pInt) call IO_error(error_ID=901_pInt) - + end subroutine mesh_abaqus_count_nodesAndElements @@ -2116,21 +2197,21 @@ subroutine mesh_abaqus_count_elementSets(fileUnit) integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) :: line logical :: inPart - + mesh_NelemSets = 0_pInt mesh_maxNelemInSet = mesh_Nelems ! have to be conservative, since Abaqus allows for recursive definitons - + 610 FORMAT(A300) inPart = .false. rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*part' ) inPart = .true. if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*end' .and. & IO_lc(IO_stringValue(line,chunkPos,2_pInt)) == 'part' ) inPart = .false. - + if ( (inPart .or. noPart) .and. IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*elset' ) & mesh_NelemSets = mesh_NelemSets + 1_pInt enddo @@ -2155,18 +2236,18 @@ subroutine mesh_abaqus_count_materials(fileUnit) implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) :: line logical inPart - + mesh_Nmaterials = 0_pInt - + 610 FORMAT(A300) inPart = .false. rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*part' ) inPart = .true. @@ -2180,12 +2261,12 @@ subroutine mesh_abaqus_count_materials(fileUnit) enddo 620 if (mesh_Nmaterials == 0_pInt) call IO_error(error_ID=903_pInt) - + end subroutine mesh_abaqus_count_materials !-------------------------------------------------------------------------------------------------- -! Build element set mapping +! Build element set mapping ! ! allocate globals: mesh_nameElemSet, mesh_mapElemSet !-------------------------------------------------------------------------------------------------- @@ -2219,7 +2300,7 @@ subroutine mesh_abaqus_map_elementSets(fileUnit) if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*part' ) inPart = .true. if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*end' .and. & IO_lc(IO_stringValue(line,chunkPos,2_pInt)) == 'part' ) inPart = .false. - + if ( (inPart .or. noPart) .and. IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*elset' ) then elemSet = elemSet + 1_pInt mesh_nameElemSet(elemSet) = trim(IO_extractValue(IO_lc(IO_stringValue(line,chunkPos,2_pInt)),'elset')) @@ -2257,14 +2338,14 @@ subroutine mesh_abaqus_map_materials(fileUnit) integer(pInt) :: i,c = 0_pInt logical :: inPart = .false. character(len=64) :: elemSetName,materialName - + allocate (mesh_nameMaterial(mesh_Nmaterials)) ; mesh_nameMaterial = '' allocate (mesh_mapMaterial(mesh_Nmaterials)) ; mesh_mapMaterial = '' 610 FORMAT(A300) rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == '*part' ) inPart = .true. @@ -2289,7 +2370,7 @@ subroutine mesh_abaqus_map_materials(fileUnit) c = c + 1_pInt mesh_nameMaterial(c) = materialName ! name of material used for this section mesh_mapMaterial(c) = elemSetName ! mapped to respective element set - endif + endif endif enddo @@ -2299,7 +2380,7 @@ subroutine mesh_abaqus_map_materials(fileUnit) enddo end subroutine mesh_abaqus_map_materials - + !-------------------------------------------------------------------------------------------------- !> @brief Count overall number of CP elements in mesh and stores them in 'mesh_NcpElems' @@ -2311,22 +2392,22 @@ subroutine mesh_abaqus_count_cpElements(fileUnit) IO_stringPos, & IO_error, & IO_extractValue - + implicit none integer(pInt), intent(in) :: fileUnit - + integer(pInt), allocatable, dimension(:) :: chunkPos character(len=300) line integer(pInt) :: i,k logical :: materialFound = .false. character(len=64) ::materialName,elemSetName - + mesh_NcpElems = 0_pInt - + 610 FORMAT(A300) rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) select case ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) ) @@ -2348,7 +2429,7 @@ subroutine mesh_abaqus_count_cpElements(fileUnit) endif endselect enddo - + 620 if (mesh_NcpElems == 0_pInt) call IO_error(error_ID=906_pInt) end subroutine mesh_abaqus_count_cpElements @@ -2366,7 +2447,7 @@ subroutine mesh_abaqus_map_elements(fileUnit) IO_stringPos, & IO_extractValue, & IO_error - + implicit none integer(pInt), intent(in) :: fileUnit @@ -2381,7 +2462,7 @@ subroutine mesh_abaqus_map_elements(fileUnit) 610 FORMAT(A300) rewind(fileUnit) - do + do read (fileUnit,610,END=660) line chunkPos = IO_stringPos(line) select case ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) ) @@ -2500,7 +2581,7 @@ subroutine mesh_abaqus_build_nodes(fileUnit) character(len=300) :: line integer(pInt) :: i,j,m,c logical :: inPart - + allocate ( mesh_node0 (3,mesh_Nnodes) ); mesh_node0 = 0.0_pReal allocate ( mesh_node (3,mesh_Nnodes) ); mesh_node = 0.0_pReal @@ -2532,7 +2613,7 @@ subroutine mesh_abaqus_build_nodes(fileUnit) m = mesh_FEasCP('node',IO_intValue(line,chunkPos,1_pInt)) do j=1_pInt, 3_pInt mesh_node0(j,m) = mesh_unitlength * IO_floatValue(line,chunkPos,j+1_pInt) - enddo + enddo enddo endif enddo @@ -2545,7 +2626,7 @@ end subroutine mesh_abaqus_build_nodes !-------------------------------------------------------------------------------------------------- !> @brief Gets maximum count of nodes, IPs, IP neighbors, and subNodes among cpElements. -!! Sets global values 'mesh_maxNnodes', 'mesh_maxNips', 'mesh_maxNipNeighbors', +!! Sets global values 'mesh_maxNnodes', 'mesh_maxNips', 'mesh_maxNipNeighbors', !! and 'mesh_maxNcellnodes' !-------------------------------------------------------------------------------------------------- subroutine mesh_abaqus_count_cpSizes(fileUnit) @@ -2597,7 +2678,7 @@ subroutine mesh_abaqus_count_cpSizes(fileUnit) mesh_maxNcellnodes = max(mesh_maxNcellnodes,FE_Ncellnodes(g)) endif enddo - + 620 end subroutine mesh_abaqus_count_cpSizes @@ -2677,11 +2758,11 @@ subroutine mesh_abaqus_build_elements(fileUnit) endif enddo - + 620 rewind(fileUnit) ! just in case "*material" definitions apear before "*element" materialFound = .false. - do + do read (fileUnit,610,END=630) line chunkPos = IO_stringPos(line) select case ( IO_lc(IO_StringValue(line,chunkPos,1_pInt))) @@ -2737,14 +2818,14 @@ use IO, only: & integer(pInt), allocatable, dimension(:) :: chunkPos integer(pInt) chunk, Nchunks character(len=300) :: line, damaskOption, v - character(len=300) :: keyword + character(len=300) :: keyword #endif #ifdef Spectral mesh_periodicSurface = .true. #else mesh_periodicSurface = .false. -#ifdef Marc4DAMASK +#ifdef Marc4DAMASK keyword = '$damask' #endif #ifdef Abaqus @@ -2752,7 +2833,7 @@ use IO, only: & #endif rewind(fileUnit) - do + do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) Nchunks = chunkPos(1) @@ -2782,7 +2863,7 @@ use IO, only: & subroutine mesh_build_ipAreas use math, only: & math_crossproduct - + implicit none integer(pInt) :: e,t,g,c,i,f,n,m real(pReal), dimension (3,FE_maxNcellnodesPerCellface) :: nodePos, normals @@ -2824,10 +2905,10 @@ subroutine mesh_build_ipAreas enddo case (4_pInt) ! 3D 8node - ! for this cell type we get the normal of the quadrilateral face as an average of + ! for this cell type we get the normal of the quadrilateral face as an average of ! four normals of triangular subfaces; since the face consists only of two triangles, - ! the sum has to be divided by two; this whole prcedure tries to compensate for - ! probable non-planar cell surfaces + ! the sum has to be divided by two; this whole prcedure tries to compensate for + ! probable non-planar cell surfaces m = FE_NcellnodesPerCellface(c) do i = 1_pInt,FE_Nips(g) ! loop over ips=cells in this element do f = 1_pInt,FE_NipNeighbors(c) ! loop over cell faces @@ -2846,10 +2927,10 @@ subroutine mesh_build_ipAreas end select enddo !$OMP END PARALLEL DO - + end subroutine mesh_build_ipAreas - -#ifndef Spectral + +#ifndef Spectral !-------------------------------------------------------------------------------------------------- !> @brief assignment of twin nodes for each cp node, allocate globals '_nodeTwins' !-------------------------------------------------------------------------------------------------- @@ -2867,19 +2948,19 @@ subroutine mesh_build_nodeTwins tolerance ! tolerance below which positions are assumed identical real(pReal), dimension(3) :: distance ! distance between two nodes in all three coordinates logical, dimension(mesh_Nnodes) :: unpaired - + allocate(mesh_nodeTwins(3,mesh_Nnodes)) mesh_nodeTwins = 0_pInt - + tolerance = 0.001_pReal * minval(mesh_ipVolume) ** 0.333_pReal - + do dir = 1_pInt,3_pInt ! check periodicity in directions of x,y,z if (mesh_periodicSurface(dir)) then ! only if periodicity is requested - - - !*** find out which nodes sit on the surface + + + !*** find out which nodes sit on the surface !*** and have a minimum or maximum position in this dimension - + minimumNodes = 0_pInt maximumNodes = 0_pInt minCoord = minval(mesh_node0(dir,:)) @@ -2893,10 +2974,10 @@ subroutine mesh_build_nodeTwins maximumNodes(maximumNodes(1)+1_pInt) = node endif enddo - - + + !*** find the corresponding node on the other side with the same position in this dimension - + unpaired = .true. do n1 = 1_pInt,minimumNodes(1) minimumNode = minimumNodes(n1+1_pInt) @@ -2913,15 +2994,15 @@ subroutine mesh_build_nodeTwins enddo endif enddo - + endif enddo - + end subroutine mesh_build_nodeTwins !-------------------------------------------------------------------------------------------------- -!> @brief get maximum count of shared elements among cpElements and build list of elements shared +!> @brief get maximum count of shared elements among cpElements and build list of elements shared !! by each node in mesh. Allocate globals '_maxNsharedElems' and '_sharedElem' !-------------------------------------------------------------------------------------------------- subroutine mesh_build_sharedElems @@ -2930,17 +3011,16 @@ subroutine mesh_build_sharedElems integer(pint) e, & ! element index g, & ! element type node, & ! CP node index - n, & ! node index per element - myDim, & ! dimension index + n, & ! node index per element + myDim, & ! dimension index nodeTwin ! node twin in the specified dimension integer(pInt), dimension (mesh_Nnodes) :: node_count integer(pInt), dimension (:), allocatable :: node_seen - + allocate(node_seen(maxval(FE_NmatchingNodes))) - - + node_count = 0_pInt - + do e = 1_pInt,mesh_NcpElems g = FE_geomtype(mesh_element(2,e)) ! get elemGeomType node_seen = 0_pInt ! reset node duplicates @@ -2957,12 +3037,12 @@ subroutine mesh_build_sharedElems node_seen(n) = node ! remember this node to be counted already enddo enddo - + mesh_maxNsharedElems = int(maxval(node_count),pInt) ! most shared node - + allocate(mesh_sharedElem(1+mesh_maxNsharedElems,mesh_Nnodes)) mesh_sharedElem = 0_pInt - + do e = 1_pInt,mesh_NcpElems g = FE_geomtype(mesh_element(2,e)) ! get elemGeomType node_seen = 0_pInt @@ -2982,9 +3062,9 @@ subroutine mesh_build_sharedElems node_seen(n) = node enddo enddo - + deallocate(node_seen) - + end subroutine mesh_build_sharedElems @@ -2994,14 +3074,14 @@ end subroutine mesh_build_sharedElems subroutine mesh_build_ipNeighborhood use math, only: & math_mul3x3 - + implicit none integer(pInt) :: myElem, & ! my CP element index myIP, & myType, & ! my element type myFace, & neighbor, & ! neighor index - neighboringIPkey, & ! positive integer indicating the neighboring IP (for intra-element) and negative integer indicating the face towards neighbor (for neighboring element) + neighboringIPkey, & ! positive integer indicating the neighboring IP (for intra-element) and negative integer indicating the face towards neighbor (for neighboring element) candidateIP, & neighboringType, & ! element type of neighbor NlinkedNodes, & ! number of linked nodes @@ -3011,52 +3091,52 @@ subroutine mesh_build_ipNeighborhood matchingElem, & ! CP elem number of matching element matchingFace, & ! face ID of matching element a, anchor, & - neighboringIP, & + neighboringIP, & neighboringElem, & pointingToMe integer(pInt), dimension(FE_maxmaxNnodesAtIP) :: & linkedNodes = 0_pInt, & matchingNodes logical checkTwins - + allocate(mesh_ipNeighborhood(3,mesh_maxNipNeighbors,mesh_maxNips,mesh_NcpElems)) mesh_ipNeighborhood = 0_pInt - - + + do myElem = 1_pInt,mesh_NcpElems ! loop over cpElems myType = FE_geomtype(mesh_element(2,myElem)) ! get elemGeomType do myIP = 1_pInt,FE_Nips(myType) ! loop over IPs of elem - + do neighbor = 1_pInt,FE_NipNeighbors(FE_celltype(myType)) ! loop over neighbors of IP neighboringIPkey = FE_ipNeighbor(neighbor,myIP,myType) - + !*** if the key is positive, the neighbor is inside the element !*** that means, we have already found our neighboring IP - + if (neighboringIPkey > 0_pInt) then mesh_ipNeighborhood(1,neighbor,myIP,myElem) = myElem mesh_ipNeighborhood(2,neighbor,myIP,myElem) = neighboringIPkey - - + + !*** if the key is negative, the neighbor resides in a neighboring element !*** that means, we have to look through the face indicated by the key and see which element is behind that face - + elseif (neighboringIPkey < 0_pInt) then ! neighboring element's IP myFace = -neighboringIPkey call mesh_faceMatch(myElem, myFace, matchingElem, matchingFace) ! get face and CP elem id of face match if (matchingElem > 0_pInt) then ! found match? neighboringType = FE_geomtype(mesh_element(2,matchingElem)) - + !*** trivial solution if neighbor has only one IP - - if (FE_Nips(neighboringType) == 1_pInt) then + + if (FE_Nips(neighboringType) == 1_pInt) then mesh_ipNeighborhood(1,neighbor,myIP,myElem) = matchingElem mesh_ipNeighborhood(2,neighbor,myIP,myElem) = 1_pInt cycle endif - + !*** find those nodes which build the link to the neighbor - + NlinkedNodes = 0_pInt linkedNodes = 0_pInt do a = 1_pInt,FE_maxNnodesAtIP(myType) ! figure my anchor nodes on connecting face @@ -3072,11 +3152,11 @@ subroutine mesh_build_ipNeighborhood endif endif enddo - + !*** loop through the ips of my neighbor !*** and try to find an ip with matching nodes !*** also try to match with node twins - + checkCandidateIP: do candidateIP = 1_pInt,FE_Nips(neighboringType) NmatchingNodes = 0_pInt matchingNodes = 0_pInt @@ -3093,12 +3173,12 @@ subroutine mesh_build_ipNeighborhood endif endif enddo - + if (NmatchingNodes /= NlinkedNodes) & ! this ip has wrong count of anchors on face cycle checkCandidateIP - + !*** check "normal" nodes whether they match or not - + checkTwins = .false. do a = 1_pInt,NlinkedNodes if (all(matchingNodes /= linkedNodes(a))) then ! this linkedNode does not match any matchingNode @@ -3106,9 +3186,9 @@ subroutine mesh_build_ipNeighborhood exit ! no need to search further endif enddo - + !*** if no match found, then also check node twins - + if(checkTwins) then dir = int(maxloc(abs(mesh_ipAreaNormal(1:3,neighbor,myIP,myElem)),1),pInt) ! check for twins only in direction of the surface normal do a = 1_pInt,NlinkedNodes @@ -3119,12 +3199,12 @@ subroutine mesh_build_ipNeighborhood endif enddo endif - + !*** we found a match !!! - + mesh_ipNeighborhood(1,neighbor,myIP,myElem) = matchingElem mesh_ipNeighborhood(2,neighbor,myIP,myElem) = candidateIP - exit checkCandidateIP + exit checkCandidateIP enddo checkCandidateIP endif ! end of valid external matching endif ! end of internal/external matching @@ -3153,7 +3233,7 @@ subroutine mesh_build_ipNeighborhood enddo enddo enddo - + end subroutine mesh_build_ipNeighborhood #endif @@ -3179,12 +3259,12 @@ subroutine mesh_tell_statistics integer(pInt), dimension (:,:), allocatable :: mesh_HomogMicro character(len=64) :: myFmt integer(pInt) :: i,e,n,f,t,g,c, myDebug - + myDebug = debug_level(debug_mesh) if (mesh_maxValStateVar(1) < 1_pInt) call IO_error(error_ID=170_pInt) ! no homogenization specified if (mesh_maxValStateVar(2) < 1_pInt) call IO_error(error_ID=180_pInt) ! no microstructure specified - + allocate (mesh_HomogMicro(mesh_maxValStateVar(1),mesh_maxValStateVar(2))); mesh_HomogMicro = 0_pInt do e = 1_pInt,mesh_NcpElems if (mesh_element(3,e) < 1_pInt) call IO_error(error_ID=170_pInt,el=e) ! no homogenization specified @@ -3268,7 +3348,7 @@ enddo if (iand(myDebug,debug_levelSelective) /= 0_pInt .and. debug_i /= i) cycle write(6,'(i8,1x,i5,3(1x,f12.8))') e, i, mesh_ipCoordinates(:,i,e) enddo - enddo + enddo #ifndef Spectral write(6,'(/,a,/)') 'Input Parser: NODE TWINS' write(6,'(a6,3(3x,a6))') ' node','twin_x','twin_y','twin_z' @@ -3295,7 +3375,7 @@ enddo !$OMP END CRITICAL (write2out) deallocate(mesh_HomogMicro) - + end subroutine mesh_tell_statistics @@ -3307,7 +3387,7 @@ integer(pInt) function FE_mapElemtype(what) implicit none character(len=*), intent(in) :: what - + select case (IO_lc(what)) case ( '6') FE_mapElemtype = 1_pInt ! Two-dimensional Plane Strain Triangle @@ -3354,7 +3434,7 @@ integer(pInt) function FE_mapElemtype(what) 'c3d20', & 'c3d20t') FE_mapElemtype = 13_pInt ! Three-dimensional Arbitrarily Distorted quadratic hexahedral - case default + case default call IO_error(error_ID=190_pInt,ext_msg=IO_lc(what)) end select @@ -3368,7 +3448,7 @@ subroutine mesh_faceMatch(elem, face ,matchingElem, matchingFace) implicit none integer(pInt), intent(out) :: matchingElem, & ! matching CP element ID - matchingFace ! matching face ID + matchingFace ! matching face ID integer(pInt), intent(in) :: face, & ! face ID elem ! CP elem ID integer(pInt), dimension(FE_NmatchingNodesPerFace(face,FE_geomtype(mesh_element(2,elem)))) :: & @@ -3583,7 +3663,7 @@ subroutine mesh_build_FEdata 7,0, 0,0 & ],pInt),[FE_maxNnodesAtIP(me),FE_Nips(me)]) - + ! *** FE_ipNeighbor *** ! is a list of the neighborhood of each IP. ! It is sorted in (local) +x,-x, +y,-y, +z,-z direction. @@ -3596,7 +3676,7 @@ subroutine mesh_build_FEdata reshape(int([& -2,-3,-1 & ],pInt),[FE_NipNeighbors(FE_celltype(me)),FE_Nips(me)]) - + me = me + 1_pInt FE_ipNeighbor(1:FE_NipNeighbors(FE_celltype(me)),1:FE_Nips(me),me) = & ! element 125 (2D 6node 3ip) reshape(int([& @@ -3604,7 +3684,7 @@ subroutine mesh_build_FEdata -2, 1, 3,-1, & 2,-3,-2, 1 & ],pInt),[FE_NipNeighbors(FE_celltype(me)),FE_Nips(me)]) - + me = me + 1_pInt FE_ipNeighbor(1:FE_NipNeighbors(FE_celltype(me)),1:FE_Nips(me),me) = & ! element 11 (2D 4node 4ip) reshape(int([& @@ -3833,32 +3913,32 @@ subroutine mesh_build_FEdata me = 0_pInt me = me + 1_pInt - FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 6 (2D 3node 1ip) + FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 6 (2D 3node 1ip) reshape(real([& - 1, 0, 0, & - 0, 1, 0, & + 1, 0, 0, & + 0, 1, 0, & 0, 0, 1 & ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) me = me + 1_pInt FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 125 (2D 6node 3ip) reshape(real([& - 1, 0, 0, 0, 0, 0, & - 0, 1, 0, 0, 0, 0, & + 1, 0, 0, 0, 0, 0, & + 0, 1, 0, 0, 0, 0, & 0, 0, 1, 0, 0, 0, & 0, 0, 0, 1, 0, 0, & 0, 0, 0, 0, 1, 0, & 0, 0, 0, 0, 0, 1, & 1, 1, 1, 2, 2, 2 & ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) - + me = me + 1_pInt FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 11 (2D 4node 4ip) reshape(real([& - 1, 0, 0, 0, & - 0, 1, 0, 0, & + 1, 0, 0, 0, & + 0, 1, 0, 0, & 0, 0, 1, 0, & - 0, 0, 0, 1, & + 0, 0, 0, 1, & 1, 1, 0, 0, & 0, 1, 1, 0, & 0, 0, 1, 1, & @@ -3902,16 +3982,16 @@ subroutine mesh_build_FEdata ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) me = me + 1_pInt - FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 134 (3D 4node 1ip) + FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 134 (3D 4node 1ip) reshape(real([& - 1, 0, 0, 0, & - 0, 1, 0, 0, & + 1, 0, 0, 0, & + 0, 1, 0, 0, & 0, 0, 1, 0, & - 0, 0, 0, 1 & + 0, 0, 0, 1 & ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) me = me + 1_pInt - FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 157 (3D 5node 4ip) + FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 157 (3D 5node 4ip) reshape(real([& 1, 0, 0, 0, 0, & 0, 1, 0, 0, 0, & @@ -3977,7 +4057,7 @@ subroutine mesh_build_FEdata ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) me = me + 1_pInt - FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 117 (3D 8node 1ip) + FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 117 (3D 8node 1ip) reshape(real([& 1, 0, 0, 0, 0, 0, 0, 0, & 0, 1, 0, 0, 0, 0, 0, 0, & @@ -3992,134 +4072,134 @@ subroutine mesh_build_FEdata me = me + 1_pInt FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 7 (3D 8node 8ip) reshape(real([& - 1, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 1, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 1, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 1, 0, 0, 0, 0, & ! + 1, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 1, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 1, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 1, 0, 0, 0, 0, & ! 0, 0, 0, 0, 1, 0, 0, 0, & ! 5 - 0, 0, 0, 0, 0, 1, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 1, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 1, & ! - 1, 1, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 1, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 1, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 1, & ! + 1, 1, 0, 0, 0, 0, 0, 0, & ! 0, 1, 1, 0, 0, 0, 0, 0, & ! 10 - 0, 0, 1, 1, 0, 0, 0, 0, & ! - 1, 0, 0, 1, 0, 0, 0, 0, & ! - 1, 0, 0, 0, 1, 0, 0, 0, & ! - 0, 1, 0, 0, 0, 1, 0, 0, & ! + 0, 0, 1, 1, 0, 0, 0, 0, & ! + 1, 0, 0, 1, 0, 0, 0, 0, & ! + 1, 0, 0, 0, 1, 0, 0, 0, & ! + 0, 1, 0, 0, 0, 1, 0, 0, & ! 0, 0, 1, 0, 0, 0, 1, 0, & ! 15 - 0, 0, 0, 1, 0, 0, 0, 1, & ! - 0, 0, 0, 0, 1, 1, 0, 0, & ! - 0, 0, 0, 0, 0, 1, 1, 0, & ! - 0, 0, 0, 0, 0, 0, 1, 1, & ! + 0, 0, 0, 1, 0, 0, 0, 1, & ! + 0, 0, 0, 0, 1, 1, 0, 0, & ! + 0, 0, 0, 0, 0, 1, 1, 0, & ! + 0, 0, 0, 0, 0, 0, 1, 1, & ! 0, 0, 0, 0, 1, 0, 0, 1, & ! 20 - 1, 1, 1, 1, 0, 0, 0, 0, & ! - 1, 1, 0, 0, 1, 1, 0, 0, & ! - 0, 1, 1, 0, 0, 1, 1, 0, & ! - 0, 0, 1, 1, 0, 0, 1, 1, & ! + 1, 1, 1, 1, 0, 0, 0, 0, & ! + 1, 1, 0, 0, 1, 1, 0, 0, & ! + 0, 1, 1, 0, 0, 1, 1, 0, & ! + 0, 0, 1, 1, 0, 0, 1, 1, & ! 1, 0, 0, 1, 1, 0, 0, 1, & ! 25 - 0, 0, 0, 0, 1, 1, 1, 1, & ! - 1, 1, 1, 1, 1, 1, 1, 1 & ! + 0, 0, 0, 0, 1, 1, 1, 1, & ! + 1, 1, 1, 1, 1, 1, 1, 1 & ! ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) me = me + 1_pInt FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 57 (3D 20node 8ip) reshape(real([& - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 5 - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 10 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, & ! 15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, & ! 20 - 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 1, 1, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, & ! - 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 0, & ! - 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, & ! - 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 2, & ! 25 - 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, & ! - 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 & ! + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 5 + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 10 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, & ! 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, & ! 20 + 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 1, 1, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, & ! + 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 0, & ! + 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, & ! + 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 2, & ! 25 + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, & ! + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 & ! ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) me = me + 1_pInt FE_cellnodeParentnodeWeights(1:FE_Nnodes(me),1:FE_Ncellnodes(FE_geomtype(me)),me) = & ! element 21 (3D 20node 27ip) reshape(real([& - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 5 - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 10 - 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! 15 - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, & ! - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, & ! - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, & ! - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, & ! 20 - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, & ! - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, & ! 25 - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, & ! 30 - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, & ! - 4, 1, 1, 1, 0, 0, 0, 0, 8, 2, 2, 8, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 1, 4, 1, 1, 0, 0, 0, 0, 8, 8, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 1, 1, 4, 1, 0, 0, 0, 0, 2, 8, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! 35 - 1, 1, 1, 4, 0, 0, 0, 0, 2, 2, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, & ! - 4, 1, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 8, 2, 0, 0, & ! - 1, 4, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 2, 8, 0, 0, & ! - 0, 4, 1, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 8, 2, 0, & ! - 0, 1, 4, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 2, 8, 0, & ! 40 - 0, 0, 4, 1, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 8, 2, & ! - 0, 0, 1, 4, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 2, 8, & ! - 1, 0, 0, 4, 1, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 2, 2, 0, 0, 8, & ! - 4, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 2, 8, 0, 0, 2, & ! - 1, 1, 0, 0, 4, 1, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 8, 2, 0, 0, & ! 45 - 1, 1, 0, 0, 1, 4, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 8, 0, 0, & ! - 0, 1, 1, 0, 0, 4, 1, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 8, 2, 0, & ! - 0, 1, 1, 0, 0, 1, 4, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 8, 0, & ! - 0, 0, 1, 1, 0, 0, 4, 1, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 8, 2, & ! - 0, 0, 1, 1, 0, 0, 1, 4, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 8, & ! 50 - 1, 0, 0, 1, 1, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 8, 2, 0, 0, 8, & ! - 1, 0, 0, 1, 4, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 8, 8, 0, 0, 2, & ! - 0, 0, 0, 0, 4, 1, 1, 1, 0, 0, 0, 0, 8, 2, 2, 8, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 1, 4, 1, 1, 0, 0, 0, 0, 8, 8, 2, 2, 0, 0, 0, 0, & ! - 0, 0, 0, 0, 1, 1, 4, 1, 0, 0, 0, 0, 2, 8, 8, 2, 0, 0, 0, 0, & ! 55 - 0, 0, 0, 0, 1, 1, 1, 4, 0, 0, 0, 0, 2, 2, 8, 8, 0, 0, 0, 0, & ! - 24, 8, 4, 8, 8, 4, 3, 4, 32,12,12,32, 12, 4, 4,12, 32,12, 4,12, & ! - 8,24, 8, 4, 4, 8, 4, 3, 32,32,12,12, 12,12, 4, 4, 12,32,12, 4, & ! - 4, 8,24, 8, 3, 4, 8, 4, 12,32,32,12, 4,12,12, 4, 4,12,32,12, & ! - 8, 4, 8,24, 4, 3, 4, 8, 12,12,32,32, 4, 4,12,12, 12, 4,12,32, & ! 60 - 8, 4, 3, 4, 24, 8, 4, 8, 12, 4, 4,12, 32,12,12,32, 32,12, 4,12, & ! - 4, 8, 4, 3, 8,24, 8, 4, 12,12, 4, 4, 32,32,12,12, 12,32,12, 4, & ! - 3, 4, 8, 4, 4, 8,24, 8, 4,12,12, 4, 12,32,32,12, 4,12,32,12, & ! - 4, 3, 4, 8, 8, 4, 8,24, 4, 4,12,12, 12,12,32,32, 12, 4,12,32 & ! + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 5 + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! 10 + 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! 15 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, & ! + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, & ! + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, & ! + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, & ! 20 + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, & ! + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, & ! 25 + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, & ! 30 + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, & ! + 4, 1, 1, 1, 0, 0, 0, 0, 8, 2, 2, 8, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 1, 4, 1, 1, 0, 0, 0, 0, 8, 8, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 1, 1, 4, 1, 0, 0, 0, 0, 2, 8, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0, & ! 35 + 1, 1, 1, 4, 0, 0, 0, 0, 2, 2, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, & ! + 4, 1, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 8, 2, 0, 0, & ! + 1, 4, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 2, 8, 0, 0, & ! + 0, 4, 1, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 8, 2, 0, & ! + 0, 1, 4, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 2, 8, 0, & ! 40 + 0, 0, 4, 1, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 8, 2, & ! + 0, 0, 1, 4, 0, 0, 1, 1, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 2, 8, & ! + 1, 0, 0, 4, 1, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 2, 2, 0, 0, 8, & ! + 4, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 8, 0, 0, 0, 2, 8, 0, 0, 2, & ! + 1, 1, 0, 0, 4, 1, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 8, 2, 0, 0, & ! 45 + 1, 1, 0, 0, 1, 4, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 8, 0, 0, & ! + 0, 1, 1, 0, 0, 4, 1, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 8, 2, 0, & ! + 0, 1, 1, 0, 0, 1, 4, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 8, 0, & ! + 0, 0, 1, 1, 0, 0, 4, 1, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 8, 2, & ! + 0, 0, 1, 1, 0, 0, 1, 4, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 8, & ! 50 + 1, 0, 0, 1, 1, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 8, 2, 0, 0, 8, & ! + 1, 0, 0, 1, 4, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 8, 8, 0, 0, 2, & ! + 0, 0, 0, 0, 4, 1, 1, 1, 0, 0, 0, 0, 8, 2, 2, 8, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 1, 4, 1, 1, 0, 0, 0, 0, 8, 8, 2, 2, 0, 0, 0, 0, & ! + 0, 0, 0, 0, 1, 1, 4, 1, 0, 0, 0, 0, 2, 8, 8, 2, 0, 0, 0, 0, & ! 55 + 0, 0, 0, 0, 1, 1, 1, 4, 0, 0, 0, 0, 2, 2, 8, 8, 0, 0, 0, 0, & ! + 24, 8, 4, 8, 8, 4, 3, 4, 32,12,12,32, 12, 4, 4,12, 32,12, 4,12, & ! + 8,24, 8, 4, 4, 8, 4, 3, 32,32,12,12, 12,12, 4, 4, 12,32,12, 4, & ! + 4, 8,24, 8, 3, 4, 8, 4, 12,32,32,12, 4,12,12, 4, 4,12,32,12, & ! + 8, 4, 8,24, 4, 3, 4, 8, 12,12,32,32, 4, 4,12,12, 12, 4,12,32, & ! 60 + 8, 4, 3, 4, 24, 8, 4, 8, 12, 4, 4,12, 32,12,12,32, 32,12, 4,12, & ! + 4, 8, 4, 3, 8,24, 8, 4, 12,12, 4, 4, 32,32,12,12, 12,32,12, 4, & ! + 3, 4, 8, 4, 4, 8,24, 8, 4,12,12, 4, 12,32,32,12, 4,12,32,12, & ! + 4, 3, 4, 8, 8, 4, 8,24, 4, 4,12,12, 12,12,32,32, 12, 4,12,32 & ! ],pReal),[FE_Nnodes(me),FE_Ncellnodes(FE_geomtype(me))]) @@ -4174,7 +4254,7 @@ end subroutine mesh_build_FEdata integer(pInt) function mesh_get_Ncellnodes() implicit none - + mesh_get_Ncellnodes = mesh_Ncellnodes end function mesh_get_Ncellnodes @@ -4186,7 +4266,7 @@ end function mesh_get_Ncellnodes real(pReal) function mesh_get_unitlength() implicit none - + mesh_get_unitlength = mesh_unitlength end function mesh_get_unitlength From 3a448ccb7dc8791105ff733fc8ddc4887403fe40 Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 10 Jan 2018 21:12:00 +0100 Subject: [PATCH 22/26] [skip ci] updated version information after successful test of v2.0.1-1002-g7149f95 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 95d7cbcda..ca56c41b4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-1000-ge02a0d3 +v2.0.1-1002-g7149f95 From 2c4df2f07008aff184eae1887f82f4bfee6745d4 Mon Sep 17 00:00:00 2001 From: Franz Roters Date: Thu, 11 Jan 2018 17:11:03 +0100 Subject: [PATCH 23/26] working further on Marc2017 compatibility --- src/mesh.f90 | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/mesh.f90 b/src/mesh.f90 index d20b61bb4..827875d2f 100644 --- a/src/mesh.f90 +++ b/src/mesh.f90 @@ -75,8 +75,9 @@ module mesh integer(pInt), private :: & MarcVersion, & !< Version of input file format (Marc only) hypoelasticTableStyle, & !< Table style (Marc only) - initialcondTableStyle, & !< Table style (Marc only) - Marc_matNumber !< Material number of hypoelastic material (Marc only) + initialcondTableStyle !< Table style (Marc only) + integer(pInt), dimension(:), allocatable, private :: & + Marc_matNumber !< array of material numbers for hypoelastic material (Marc only) #endif integer(pInt), dimension(2), private :: & @@ -590,7 +591,7 @@ subroutine mesh_init(ip,el) if (MarcVersion > 12) then call mesh_marc_get_matNumber(FILEUNIT) if (myDebug) write(6,'(a)') ' Got hypoleastic material number'; flush(6) - endif + endif call mesh_marc_count_nodesAndElements(FILEUNIT) if (myDebug) write(6,'(a)') ' Counted nodes/elements'; flush(6) call mesh_marc_count_elementSets(FILEUNIT) @@ -1574,7 +1575,7 @@ subroutine mesh_marc_get_fileFormat(fileUnit) 620 end subroutine mesh_marc_get_fileFormat - + !-------------------------------------------------------------------------------------------------- !> @brief Figures out table styles (Marc only) and stores to 'initialcondTableStyle' and !! 'hypoelasticTableStyle' @@ -1612,7 +1613,7 @@ subroutine mesh_marc_get_tableStyles(fileUnit) 620 end subroutine mesh_marc_get_tableStyles !-------------------------------------------------------------------------------------------------- -!> @brief Figures out material number of hypoelastic material and sores it as Marc_matNumber +!> @brief Figures out material number of hypoelastic material and stores it in Marc_matNumber array !-------------------------------------------------------------------------------------------------- subroutine mesh_marc_get_matNumber(fileUnit) use IO, only: & @@ -1625,21 +1626,31 @@ subroutine mesh_marc_get_matNumber(fileUnit) integer(pInt), intent(in) :: fileUnit integer(pInt), allocatable, dimension(:) :: chunkPos - integer(pInt) :: i + integer(pInt) :: i, j, data_blocks character(len=300) line 610 FORMAT(A300) rewind(fileUnit) + data_blocks = 1_pInt do read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'hypoelastic') then - do i=1_pInt,1_pInt+hypoelasticTableStyle ! Skip 1 or 2 lines + read (fileUnit,610,END=620) line + if (len(trim(line))/=0_pInt) then + chunkPos = IO_stringPos(line) + data_blocks = IO_intValue(line,chunkPos,1_pInt) + endif + do i=1_pInt,data_blocks ! read all data blocks read (fileUnit,610,END=620) line + chunkPos = IO_stringPos(line) + Marc_matNumber = (/Marc_matNumber, IO_intValue(line,chunkPos,1_pInt)/) + do j=1_pint,2_pInt + hypoelasticTableStyle ! read 2 or 3 remaining lines of data block + read (fileUnit,610,END=620) line + enddo enddo - Marc_matNumber = IO_intValue(line,chunkPos,1_pInt) exit endif enddo @@ -1810,9 +1821,8 @@ subroutine mesh_marc_count_cpElements(fileUnit) if ( IO_lc(IO_stringValue(line,chunkPos,1_pInt)) == 'connectivity') then read (fileUnit,610,END=620) line chunkPos = IO_stringPos(line) - if (IO_intValue(line,chunkPos,6_pInt)==Marc_matNumber) then + if (any(Marc_matNumber==IO_intValue(line,chunkPos,6_pInt))) then mesh_NcpElems = mesh_NcpElems + IO_countNumericalDataLines(fileUnit) - exit endif endif enddo From f0511e1ecb9f325f196670a28da8283b4ca21d88 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 11 Jan 2018 21:12:43 +0100 Subject: [PATCH 24/26] [skip ci] updated version information after successful test of v2.0.1-1004-g2c4df2f --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ca56c41b4..bf6ea2a02 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.1-1002-g7149f95 +v2.0.1-1004-g2c4df2f From 93073ed6616f230069b8499aaf7aa5d4b049fb43 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 18 Jan 2018 10:47:52 -0500 Subject: [PATCH 25/26] summarized multiple logicals into one --- src/homogenization.f90 | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 5a30a72c8..2f4124c2b 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -16,7 +16,7 @@ module homogenization ! General variables for the homogenization at a material point implicit none private - real(pReal), dimension(:,:,:,:), allocatable, public :: & + real(pReal), dimension(:,:,:,:), allocatable, public :: & materialpoint_F0, & !< def grad of IP at start of FE increment materialpoint_F, & !< def grad of IP to be reached at end of FE increment materialpoint_P !< first P--K stress of IP @@ -128,7 +128,7 @@ subroutine homogenization_init integer(pInt), dimension(:) , pointer :: thisNoutput character(len=64), dimension(:,:), pointer :: thisOutput character(len=32) :: outputName !< name of output, intermediate fix until HDF5 output is ready - logical :: knownHomogenization, knownThermal, knownDamage, knownVacancyflux, knownPorosity, knownHydrogenflux + logical :: valid !-------------------------------------------------------------------------------------------------- @@ -199,7 +199,7 @@ subroutine homogenization_init do p = 1,material_Nhomogenization if (any(material_homog == p)) then i = homogenization_typeInstance(p) ! which instance of this homogenization type - knownHomogenization = .true. ! assume valid + valid = .true. ! assume valid select case(homogenization_type(p)) ! split per homogenization type case (HOMOGENIZATION_NONE_ID) outputName = HOMOGENIZATION_NONE_label @@ -217,10 +217,10 @@ subroutine homogenization_init thisOutput => homogenization_RGC_output thisSize => homogenization_RGC_sizePostResult case default - knownHomogenization = .false. + valid = .false. end select write(FILEUNIT,'(/,a,/)') '['//trim(homogenization_name(p))//']' - if (knownHomogenization) then + if (valid) then write(FILEUNIT,'(a)') '(type)'//char(9)//trim(outputName) write(FILEUNIT,'(a,i4)') '(ngrains)'//char(9),homogenization_Ngrains(p) if (homogenization_type(p) /= HOMOGENIZATION_NONE_ID) then @@ -230,7 +230,7 @@ subroutine homogenization_init endif endif i = thermal_typeInstance(p) ! which instance of this thermal type - knownThermal = .true. ! assume valid + valid = .true. ! assume valid select case(thermal_type(p)) ! split per thermal type case (THERMAL_isothermal_ID) outputName = THERMAL_isothermal_label @@ -248,9 +248,9 @@ subroutine homogenization_init thisOutput => thermal_conduction_output thisSize => thermal_conduction_sizePostResult case default - knownThermal = .false. + valid = .false. end select - if (knownThermal) then + if (valid) then write(FILEUNIT,'(a)') '(thermal)'//char(9)//trim(outputName) if (thermal_type(p) /= THERMAL_isothermal_ID) then do e = 1,thisNoutput(i) @@ -259,7 +259,7 @@ subroutine homogenization_init endif endif i = damage_typeInstance(p) ! which instance of this damage type - knownDamage = .true. ! assume valid + valid = .true. ! assume valid select case(damage_type(p)) ! split per damage type case (DAMAGE_none_ID) outputName = DAMAGE_none_label @@ -277,9 +277,9 @@ subroutine homogenization_init thisOutput => damage_nonlocal_output thisSize => damage_nonlocal_sizePostResult case default - knownDamage = .false. + valid = .false. end select - if (knownDamage) then + if (valid) then write(FILEUNIT,'(a)') '(damage)'//char(9)//trim(outputName) if (damage_type(p) /= DAMAGE_none_ID) then do e = 1,thisNoutput(i) @@ -288,7 +288,7 @@ subroutine homogenization_init endif endif i = vacancyflux_typeInstance(p) ! which instance of this vacancy flux type - knownVacancyflux = .true. ! assume valid + valid = .true. ! assume valid select case(vacancyflux_type(p)) ! split per vacancy flux type case (VACANCYFLUX_isoconc_ID) outputName = VACANCYFLUX_isoconc_label @@ -306,9 +306,9 @@ subroutine homogenization_init thisOutput => vacancyflux_cahnhilliard_output thisSize => vacancyflux_cahnhilliard_sizePostResult case default - knownVacancyflux = .false. + valid = .false. end select - if (knownVacancyflux) then + if (valid) then write(FILEUNIT,'(a)') '(vacancyflux)'//char(9)//trim(outputName) if (vacancyflux_type(p) /= VACANCYFLUX_isoconc_ID) then do e = 1,thisNoutput(i) @@ -317,7 +317,7 @@ subroutine homogenization_init endif endif i = porosity_typeInstance(p) ! which instance of this porosity type - knownPorosity = .true. ! assume valid + valid = .true. ! assume valid select case(porosity_type(p)) ! split per porosity type case (POROSITY_none_ID) outputName = POROSITY_none_label @@ -330,9 +330,9 @@ subroutine homogenization_init thisOutput => porosity_phasefield_output thisSize => porosity_phasefield_sizePostResult case default - knownPorosity = .false. + valid = .false. end select - if (knownPorosity) then + if (valid) then write(FILEUNIT,'(a)') '(porosity)'//char(9)//trim(outputName) if (porosity_type(p) /= POROSITY_none_ID) then do e = 1,thisNoutput(i) @@ -341,7 +341,7 @@ subroutine homogenization_init endif endif i = hydrogenflux_typeInstance(p) ! which instance of this hydrogen flux type - knownHydrogenflux = .true. ! assume valid + valid = .true. ! assume valid select case(hydrogenflux_type(p)) ! split per hydrogen flux type case (HYDROGENFLUX_isoconc_ID) outputName = HYDROGENFLUX_isoconc_label @@ -354,9 +354,9 @@ subroutine homogenization_init thisOutput => hydrogenflux_cahnhilliard_output thisSize => hydrogenflux_cahnhilliard_sizePostResult case default - knownHydrogenflux = .false. + valid = .false. end select - if (knownHydrogenflux) then + if (valid) then write(FILEUNIT,'(a)') '(hydrogenflux)'//char(9)//trim(outputName) if (hydrogenflux_type(p) /= HYDROGENFLUX_isoconc_ID) then do e = 1,thisNoutput(i) From b36151cc32f1c7f0b5e7225e65ef825a15fc8791 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Thu, 18 Jan 2018 11:14:06 -0500 Subject: [PATCH 26/26] fixing spectral cutback hiccup and multiple cleanups flush(6) at better places, added dedicated CPFEM_age subroutine, cleaned up cutback logic, fixed broken assignment of old timeinc, continueCalculation is now a logical, rearrnaged interfaces for utilities_constitutiveResponse and utilities_calculateRate, handling of stressBC more understandable, added more comments and explanations --- src/CPFEM.f90 | 3 +- src/CPFEM2.f90 | 158 +++++++------- src/DAMASK_spectral.f90 | 122 +++++------ src/numerics.f90 | 14 +- src/spectral_mech_AL.f90 | 30 +-- src/spectral_mech_Basic.f90 | 321 +++++++++++++++-------------- src/spectral_mech_Polarisation.f90 | 32 ++- src/spectral_utilities.f90 | 111 +++++----- 8 files changed, 381 insertions(+), 410 deletions(-) diff --git a/src/CPFEM.f90 b/src/CPFEM.f90 index b3848a9eb..e34b5baa8 100644 --- a/src/CPFEM.f90 +++ b/src/CPFEM.f90 @@ -162,6 +162,7 @@ subroutine CPFEM_init write(6,'(/,a)') ' <<<+- CPFEM init -+>>>' write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" + flush(6) endif mainProcess ! initialize stress and jacobian to zero @@ -242,8 +243,8 @@ subroutine CPFEM_init write(6,'(a32,1x,6(i8,1x))') 'CPFEM_dcsdE: ', shape(CPFEM_dcsdE) write(6,'(a32,1x,6(i8,1x),/)') 'CPFEM_dcsdE_knownGood: ', shape(CPFEM_dcsdE_knownGood) write(6,'(a32,l1)') 'symmetricSolver: ', symmetricSolver + flush(6) endif - flush(6) end subroutine CPFEM_init diff --git a/src/CPFEM2.f90 b/src/CPFEM2.f90 index 0ac916046..a16aee54f 100644 --- a/src/CPFEM2.f90 +++ b/src/CPFEM2.f90 @@ -9,7 +9,7 @@ module CPFEM2 private public :: & - CPFEM_general, & + CPFEM_age, & CPFEM_initAll contains @@ -127,6 +127,7 @@ subroutine CPFEM_init write(6,'(/,a)') ' <<<+- CPFEM init -+>>>' write(6,'(a15,a)') ' Current time: ',IO_timeStamp() #include "compilation_info.f90" + flush(6) endif mainProcess ! *** restore the last converged values of each essential variable from the binary file @@ -194,7 +195,6 @@ subroutine CPFEM_init restartRead = .false. endif - flush(6) end subroutine CPFEM_init @@ -202,7 +202,7 @@ end subroutine CPFEM_init !-------------------------------------------------------------------------------------------------- !> @brief perform initialization at first call, update variables and call the actual material model !-------------------------------------------------------------------------------------------------- -subroutine CPFEM_general(age, dt) +subroutine CPFEM_age() use prec, only: & pReal, & pInt @@ -215,7 +215,6 @@ subroutine CPFEM_general(age, dt) debug_levelExtensive, & debug_levelSelective use FEsolving, only: & - terminallyIll, & restartWrite use math, only: & math_identity2nd, & @@ -254,114 +253,99 @@ subroutine CPFEM_general(age, dt) crystallite_dPdF, & crystallite_Tstar0_v, & crystallite_Tstar_v - use homogenization, only: & - materialpoint_stressAndItsTangent, & - materialpoint_postResults use IO, only: & IO_write_jobRealFile, & IO_warning use DAMASK_interface implicit none - real(pReal), intent(in) :: dt !< time increment - logical, intent(in) :: age !< age results integer(pInt) :: i, k, l, m, ph, homog, mySource - character(len=1024) :: rankStr + character(len=32) :: rankStr - !*** age results and write restart data if requested - if (age) then - crystallite_F0 = crystallite_partionedF ! crystallite deformation (_subF is perturbed...) - crystallite_Fp0 = crystallite_Fp ! crystallite plastic deformation - crystallite_Lp0 = crystallite_Lp ! crystallite plastic velocity - crystallite_Fi0 = crystallite_Fi ! crystallite intermediate deformation - crystallite_Li0 = crystallite_Li ! crystallite intermediate velocity - crystallite_dPdF0 = crystallite_dPdF ! crystallite stiffness - crystallite_Tstar0_v = crystallite_Tstar_v ! crystallite 2nd Piola Kirchhoff stress +if (iand(debug_level(debug_CPFEM), debug_levelBasic) /= 0_pInt) & + write(6,'(a)') '<< CPFEM >> aging states' - forall ( i = 1:size(plasticState )) plasticState(i)%state0 = plasticState(i)%state ! copy state in this lenghty way because: A component cannot be an array if the encompassing structure is an array - do i = 1, size(sourceState) - do mySource = 1,phase_Nsources(i) - sourceState(i)%p(mySource)%state0 = sourceState(i)%p(mySource)%state ! copy state in this lenghty way because: A component cannot be an array if the encompassing structure is an array - enddo; enddo - if (iand(debug_level(debug_CPFEM), debug_levelBasic) /= 0_pInt) & - write(6,'(a)') '<< CPFEM >> aging states' +crystallite_F0 = crystallite_partionedF ! crystallite deformation (_subF is perturbed...) +crystallite_Fp0 = crystallite_Fp ! crystallite plastic deformation +crystallite_Lp0 = crystallite_Lp ! crystallite plastic velocity +crystallite_Fi0 = crystallite_Fi ! crystallite intermediate deformation +crystallite_Li0 = crystallite_Li ! crystallite intermediate velocity +crystallite_dPdF0 = crystallite_dPdF ! crystallite stiffness +crystallite_Tstar0_v = crystallite_Tstar_v ! crystallite 2nd Piola Kirchhoff stress - do homog = 1_pInt, material_Nhomogenization - homogState (homog)%state0 = homogState (homog)%state - thermalState (homog)%state0 = thermalState (homog)%state - damageState (homog)%state0 = damageState (homog)%state - vacancyfluxState (homog)%state0 = vacancyfluxState (homog)%state - hydrogenfluxState(homog)%state0 = hydrogenfluxState(homog)%state - enddo +forall (i = 1:size(plasticState)) plasticState(i)%state0 = plasticState(i)%state ! copy state in this lengthy way because: A component cannot be an array if the encompassing structure is an array +do i = 1, size(sourceState) + do mySource = 1,phase_Nsources(i) + sourceState(i)%p(mySource)%state0 = sourceState(i)%p(mySource)%state ! copy state in this lengthy way because: A component cannot be an array if the encompassing structure is an array +enddo; enddo - if (restartWrite) then - if (iand(debug_level(debug_CPFEM), debug_levelBasic) /= 0_pInt) & - write(6,'(a)') '<< CPFEM >> writing state variables of last converged step to binary files' - - write(rankStr,'(a1,i0)')'_',worldrank +do homog = 1_pInt, material_Nhomogenization + homogState (homog)%state0 = homogState (homog)%state + thermalState (homog)%state0 = thermalState (homog)%state + damageState (homog)%state0 = damageState (homog)%state + vacancyfluxState (homog)%state0 = vacancyfluxState (homog)%state + hydrogenfluxState(homog)%state0 = hydrogenfluxState(homog)%state +enddo - call IO_write_jobRealFile(777,'recordedPhase'//trim(rankStr),size(material_phase)) - write (777,rec=1) material_phase - close (777) +if (restartWrite) then + if (iand(debug_level(debug_CPFEM), debug_levelBasic) /= 0_pInt) & + write(6,'(a)') '<< CPFEM >> writing state variables of last converged step to binary files' - call IO_write_jobRealFile(777,'convergedF'//trim(rankStr),size(crystallite_F0)) - write (777,rec=1) crystallite_F0 - close (777) + write(rankStr,'(a1,i0)')'_',worldrank - call IO_write_jobRealFile(777,'convergedFp'//trim(rankStr),size(crystallite_Fp0)) - write (777,rec=1) crystallite_Fp0 - close (777) + call IO_write_jobRealFile(777,'recordedPhase'//trim(rankStr),size(material_phase)) + write (777,rec=1) material_phase; close (777) - call IO_write_jobRealFile(777,'convergedFi'//trim(rankStr),size(crystallite_Fi0)) - write (777,rec=1) crystallite_Fi0 - close (777) + call IO_write_jobRealFile(777,'convergedF'//trim(rankStr),size(crystallite_F0)) + write (777,rec=1) crystallite_F0; close (777) - call IO_write_jobRealFile(777,'convergedLp'//trim(rankStr),size(crystallite_Lp0)) - write (777,rec=1) crystallite_Lp0 - close (777) + call IO_write_jobRealFile(777,'convergedFp'//trim(rankStr),size(crystallite_Fp0)) + write (777,rec=1) crystallite_Fp0; close (777) - call IO_write_jobRealFile(777,'convergedLi'//trim(rankStr),size(crystallite_Li0)) - write (777,rec=1) crystallite_Li0 - close (777) + call IO_write_jobRealFile(777,'convergedFi'//trim(rankStr),size(crystallite_Fi0)) + write (777,rec=1) crystallite_Fi0; close (777) - call IO_write_jobRealFile(777,'convergeddPdF'//trim(rankStr),size(crystallite_dPdF0)) - write (777,rec=1) crystallite_dPdF0 - close (777) + call IO_write_jobRealFile(777,'convergedLp'//trim(rankStr),size(crystallite_Lp0)) + write (777,rec=1) crystallite_Lp0; close (777) - call IO_write_jobRealFile(777,'convergedTstar'//trim(rankStr),size(crystallite_Tstar0_v)) - write (777,rec=1) crystallite_Tstar0_v - close (777) + call IO_write_jobRealFile(777,'convergedLi'//trim(rankStr),size(crystallite_Li0)) + write (777,rec=1) crystallite_Li0; close (777) - call IO_write_jobRealFile(777,'convergedStateConst'//trim(rankStr)) - m = 0_pInt - writePlasticityInstances: do ph = 1_pInt, size(phase_plasticity) - do k = 1_pInt, plasticState(ph)%sizeState - do l = 1, size(plasticState(ph)%state0(1,:)) - m = m+1_pInt - write(777,rec=m) plasticState(ph)%state0(k,l) - enddo; enddo - enddo writePlasticityInstances - close (777) + call IO_write_jobRealFile(777,'convergeddPdF'//trim(rankStr),size(crystallite_dPdF0)) + write (777,rec=1) crystallite_dPdF0; close (777) - call IO_write_jobRealFile(777,'convergedStateHomog'//trim(rankStr)) - m = 0_pInt - writeHomogInstances: do homog = 1_pInt, material_Nhomogenization - do k = 1_pInt, homogState(homog)%sizeState - do l = 1, size(homogState(homog)%state0(1,:)) - m = m+1_pInt - write(777,rec=m) homogState(homog)%state0(k,l) - enddo; enddo - enddo writeHomogInstances - close (777) + call IO_write_jobRealFile(777,'convergedTstar'//trim(rankStr),size(crystallite_Tstar0_v)) + write (777,rec=1) crystallite_Tstar0_v; close (777) - endif - endif + call IO_write_jobRealFile(777,'convergedStateConst'//trim(rankStr)) + m = 0_pInt + writePlasticityInstances: do ph = 1_pInt, size(phase_plasticity) + do k = 1_pInt, plasticState(ph)%sizeState + do l = 1, size(plasticState(ph)%state0(1,:)) + m = m+1_pInt + write(777,rec=m) plasticState(ph)%state0(k,l) + enddo; enddo + enddo writePlasticityInstances + close (777) - if (.not. terminallyIll) & - call materialpoint_stressAndItsTangent(.True., dt) + call IO_write_jobRealFile(777,'convergedStateHomog'//trim(rankStr)) + m = 0_pInt + writeHomogInstances: do homog = 1_pInt, material_Nhomogenization + do k = 1_pInt, homogState(homog)%sizeState + do l = 1, size(homogState(homog)%state0(1,:)) + m = m+1_pInt + write(777,rec=m) homogState(homog)%state0(k,l) + enddo; enddo + enddo writeHomogInstances + close (777) -end subroutine CPFEM_general +endif + +if (iand(debug_level(debug_CPFEM), debug_levelBasic) /= 0_pInt) & + write(6,'(a)') '<< CPFEM >> done aging states' + +end subroutine CPFEM_age end module CPFEM2 diff --git a/src/DAMASK_spectral.f90 b/src/DAMASK_spectral.f90 index f32bfb7b3..ac3fbf5a2 100644 --- a/src/DAMASK_spectral.f90 +++ b/src/DAMASK_spectral.f90 @@ -442,8 +442,9 @@ program DAMASK_spectral if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_file_seek') if (.not. appendToOutFile) then ! if not restarting, write 0th increment + write(6,'(1/,a)') ' ... writing initial configuration to file ........................' do i = 1, size(materialpoint_results,3)/(maxByteOut/(materialpoint_sizeResults*pReal))+1 ! slice the output of my process in chunks not exceeding the limit for one output - outputIndex = int([(i-1_pInt)*((maxRealOut)/materialpoint_sizeResults)+1_pInt, & + outputIndex = int([(i-1_pInt)*((maxRealOut)/materialpoint_sizeResults)+1_pInt, & ! QUESTION: why not starting i at 0 instead of murky 1? min(i*((maxRealOut)/materialpoint_sizeResults),size(materialpoint_results,3))],pLongInt) call MPI_file_write(resUnit, & reshape(materialpoint_results(:,:,outputIndex(1):outputIndex(2)), & @@ -453,24 +454,23 @@ program DAMASK_spectral if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_file_write') enddo fileOffset = fileOffset + sum(outputSize) ! forward to current file position - write(6,'(1/,a)') ' ... writing initial configuration to file ........................' endif !-------------------------------------------------------------------------------------------------- -! loopping over loadcases +! looping over loadcases loadCaseLooping: do currentLoadCase = 1_pInt, size(loadCases) time0 = time ! currentLoadCase start time guess = loadCases(currentLoadCase)%followFormerTrajectory ! change of load case? homogeneous guess for the first inc !-------------------------------------------------------------------------------------------------- -! loop oper incs defined in input file for current currentLoadCase +! loop over incs defined in input file for current currentLoadCase incLooping: do inc = 1_pInt, loadCases(currentLoadCase)%incs totalIncsCounter = totalIncsCounter + 1_pInt !-------------------------------------------------------------------------------------------------- ! forwarding time - timeIncOld = timeinc + timeIncOld = timeinc ! last timeinc that brought former inc to an end if (loadCases(currentLoadCase)%logscale == 0_pInt) then ! linear scale - timeinc = loadCases(currentLoadCase)%time/real(loadCases(currentLoadCase)%incs,pReal) ! only valid for given linear time scale. will be overwritten later in case loglinear scale is used + timeinc = loadCases(currentLoadCase)%time/real(loadCases(currentLoadCase)%incs,pReal) else if (currentLoadCase == 1_pInt) then ! 1st currentLoadCase of logarithmic scale if (inc == 1_pInt) then ! 1st inc of 1st currentLoadCase of logarithmic scale @@ -486,20 +486,23 @@ program DAMASK_spectral real(loadCases(currentLoadCase)%incs ,pReal))) endif endif - timeinc = timeinc / 2.0_pReal**real(cutBackLevel,pReal) ! depending on cut back level, decrease time step + timeinc = timeinc / real(subStepFactor,pReal)**real(cutBackLevel,pReal) ! depending on cut back level, decrease time step - forwarding: if (totalIncsCounter >= restartInc) then - stepFraction = 0_pInt + skipping: if (totalIncsCounter < restartInc) then ! not yet at restart inc? + time = time + timeinc ! just advance time, skip already performed calculation + guess = .true. ! QUESTION:why forced guessing instead of inheriting loadcase preference + else skipping + stepFraction = 0_pInt ! fraction scaled by stepFactor**cutLevel !-------------------------------------------------------------------------------------------------- -! loop over sub incs - subIncLooping: do while (stepFraction/subStepFactor**cutBackLevel <1_pInt) - time = time + timeinc ! forward time - stepFraction = stepFraction + 1_pInt - remainingLoadCaseTime = time0 - time + loadCases(currentLoadCase)%time + timeInc +! loop over sub step + subStepLooping: do while (stepFraction < subStepFactor**cutBackLevel) + remainingLoadCaseTime = loadCases(currentLoadCase)%time+time0 - time + time = time + timeinc ! forward target time + stepFraction = stepFraction + 1_pInt ! count step !-------------------------------------------------------------------------------------------------- -! report begin of new increment +! report begin of new step write(6,'(/,a)') ' ###########################################################################' write(6,'(1x,a,es12.5'//& ',a,'//IO_intOut(inc)//',a,'//IO_intOut(loadCases(currentLoadCase)%incs)//& @@ -509,11 +512,11 @@ program DAMASK_spectral 's: Increment ', inc, '/', loadCases(currentLoadCase)%incs,& '-', stepFraction, '/', subStepFactor**cutBackLevel,& ' of load case ', currentLoadCase,'/',size(loadCases) - flush(6) write(incInfo,'(a,'//IO_intOut(totalIncsCounter)//',a,'//IO_intOut(sum(loadCases%incs))//& ',a,'//IO_intOut(stepFraction)//',a,'//IO_intOut(subStepFactor**cutBackLevel)//')') & 'Increment ',totalIncsCounter,'/',sum(loadCases%incs),& '-',stepFraction, '/', subStepFactor**cutBackLevel + flush(6) !-------------------------------------------------------------------------------------------------- ! forward fields @@ -542,7 +545,7 @@ program DAMASK_spectral end select case(FIELD_THERMAL_ID); call spectral_thermal_forward() - case(FIELD_DAMAGE_ID); call spectral_damage_forward() + case(FIELD_DAMAGE_ID); call spectral_damage_forward() end select enddo @@ -582,65 +585,64 @@ program DAMASK_spectral solres(field) = spectral_damage_solution(timeinc,timeIncOld,remainingLoadCaseTime) end select + if (.not. solres(field)%converged) exit ! no solution found + enddo stagIter = stagIter + 1_pInt - stagIterate = stagIter < stagItMax .and. & - all(solres(:)%converged) .and. & - .not. all(solres(:)%stagConverged) + stagIterate = stagIter < stagItMax & + .and. all(solres(:)%converged) & + .and. .not. all(solres(:)%stagConverged) ! stationary with respect to staggered iteration enddo !-------------------------------------------------------------------------------------------------- -! check solution - cutBack = .False. - if(solres(1)%termIll .or. .not. all(solres(:)%converged .and. solres(:)%stagConverged)) then ! no solution found - if (cutBackLevel < maxCutBack) then ! do cut back - write(6,'(/,a)') ' cut back detected' - cutBack = .True. - stepFraction = (stepFraction - 1_pInt) * subStepFactor ! adjust to new denominator - cutBackLevel = cutBackLevel + 1_pInt - time = time - timeinc ! rewind time - timeinc = timeinc/2.0_pReal - elseif (solres(1)%termIll) then ! material point model cannot find a solution, exit in any casy - call IO_warning(850_pInt) - call MPI_file_close(resUnit,ierr) - close(statUnit) - call quit(-1_pInt*(lastRestartWritten+1_pInt)) ! quit and provide information about last restart inc written - elseif (continueCalculation == 1_pInt) then - guess = .true. ! accept non converged BVP solution - else ! default behavior, exit if spectral solver does not converge - call IO_warning(850_pInt) - call MPI_file_close(resUnit,ierr) - close(statUnit) - call quit(-1_pInt*(lastRestartWritten+1_pInt)) ! quit and provide information about last restart inc written - endif - else +! check solution for either advance or retry + + if ( (continueCalculation .or. all(solres(:)%converged .and. solres(:)%stagConverged)) & ! don't care or did converge + .and. .not. solres(1)%termIll) then ! and acceptable solution found + timeIncOld = timeinc + cutBack = .false. guess = .true. ! start guessing after first converged (sub)inc - endif - if (.not. cutBack) then if (worldrank == 0) then write(statUnit,*) totalIncsCounter, time, cutBackLevel, & - solres%converged, solres%iterationsNeeded ! write statistics about accepted solution + solres%converged, solres%iterationsNeeded flush(statUnit) endif + elseif (cutBackLevel < maxCutBack) then ! further cutbacking tolerated? + cutBack = .true. + stepFraction = (stepFraction - 1_pInt) * subStepFactor ! adjust to new denominator + cutBackLevel = cutBackLevel + 1_pInt + time = time - timeinc ! rewind time + timeinc = timeinc/real(subStepFactor,pReal) ! cut timestep + write(6,'(/,a)') ' cutting back ' + else ! no more options to continue + call IO_warning(850_pInt) + call MPI_file_close(resUnit,ierr) + close(statUnit) + call quit(-1_pInt*(lastRestartWritten+1_pInt)) ! quit and provide information about last restart inc written endif - enddo subIncLooping + + enddo subStepLooping + cutBackLevel = max(0_pInt, cutBackLevel - 1_pInt) ! try half number of subincs next inc - if(all(solres(:)%converged)) then ! report converged inc + + if (all(solres(:)%converged)) then convergedCounter = convergedCounter + 1_pInt - write(6,'(/,a,'//IO_intOut(totalIncsCounter)//',a)') & + write(6,'(/,a,'//IO_intOut(totalIncsCounter)//',a)') & ! report converged inc ' increment ', totalIncsCounter, ' converged' else - write(6,'(/,a,'//IO_intOut(totalIncsCounter)//',a)') & ! report non-converged inc - ' increment ', totalIncsCounter, ' NOT converged' notConvergedCounter = notConvergedCounter + 1_pInt + write(6,'(/,a,'//IO_intOut(totalIncsCounter)//',a)') & ! report non-converged inc + ' increment ', totalIncsCounter, ' NOT converged' endif; flush(6) + if (mod(inc,loadCases(currentLoadCase)%outputFrequency) == 0_pInt) then ! at output frequency if (worldrank == 0) & write(6,'(1/,a)') ' ... writing results to file ......................................' + flush(6) call materialpoint_postResults() call MPI_file_seek (resUnit,fileOffset,MPI_SEEK_SET,ierr) - if(ierr /=0_pInt) call IO_error(894_pInt, ext_msg='MPI_file_seek') + if (ierr /= 0_pInt) call IO_error(894_pInt, ext_msg='MPI_file_seek') do i=1, size(materialpoint_results,3)/(maxByteOut/(materialpoint_sizeResults*pReal))+1 ! slice the output of my process in chunks not exceeding the limit for one output outputIndex=int([(i-1_pInt)*((maxRealOut)/materialpoint_sizeResults)+1_pInt, & min(i*((maxRealOut)/materialpoint_sizeResults),size(materialpoint_results,3))],pLongInt) @@ -652,15 +654,12 @@ program DAMASK_spectral enddo fileOffset = fileOffset + sum(outputSize) ! forward to current file position endif - if( loadCases(currentLoadCase)%restartFrequency > 0_pInt .and. & ! at frequency of writing restart information set restart parameter for FEsolving - mod(inc,loadCases(currentLoadCase)%restartFrequency) == 0_pInt) then ! first call to CPFEM_general will write? - restartWrite = .true. - lastRestartWritten = inc + if ( loadCases(currentLoadCase)%restartFrequency > 0_pInt & ! writing of restart info requested ... + .and. mod(inc,loadCases(currentLoadCase)%restartFrequency) == 0_pInt) then ! ... and at frequency of writing restart information + restartWrite = .true. ! set restart parameter for FEsolving + lastRestartWritten = inc ! QUESTION: first call to CPFEM_general will write? endif - else forwarding - time = time + timeinc - guess = .true. - endif forwarding + endif skipping enddo incLooping enddo loadCaseLooping @@ -673,6 +672,7 @@ program DAMASK_spectral real(convergedCounter, pReal)/& real(notConvergedCounter + convergedCounter,pReal)*100.0_pReal, & ' %) increments converged!' + flush(6) call MPI_file_close(resUnit,ierr) close(statUnit) diff --git a/src/numerics.f90 b/src/numerics.f90 index 70c7f3c30..8392ac61c 100644 --- a/src/numerics.f90 +++ b/src/numerics.f90 @@ -120,9 +120,9 @@ module numerics petsc_options = '' integer(pInt), protected, public :: & fftw_planner_flag = 32_pInt, & !< conversion of fftw_plan_mode to integer, basically what is usually done in the include file of fftw - continueCalculation = 0_pInt, & !< 0: exit if BVP solver does not converge, 1: continue calculation if BVP solver does not converge divergence_correction = 2_pInt !< correct divergence calculation in fourier space 0: no correction, 1: size scaled to 1, 2: size scaled to Npoints logical, protected, public :: & + continueCalculation = .false., & !< false:exit if BVP solver does not converge, true: continue calculation despite BVP solver not converging memory_efficient = .true., & !< for fast execution (pre calculation of gamma_hat), Default .true.: do not precalculate update_gamma = .false. !< update gamma operator with current stiffness, Default .false.: use initial stiffness #endif @@ -424,9 +424,9 @@ subroutine numerics_init case ('err_stress_tolabs') err_stress_tolabs = IO_floatValue(line,chunkPos,2_pInt) case ('continuecalculation') - continueCalculation = IO_intValue(line,chunkPos,2_pInt) + continueCalculation = IO_intValue(line,chunkPos,2_pInt) > 0_pInt case ('memory_efficient') - memory_efficient = IO_intValue(line,chunkPos,2_pInt) > 0_pInt + memory_efficient = IO_intValue(line,chunkPos,2_pInt) > 0_pInt case ('fftw_timelimit') fftw_timelimit = IO_floatValue(line,chunkPos,2_pInt) case ('fftw_plan_mode') @@ -436,7 +436,7 @@ subroutine numerics_init case ('divergence_correction') divergence_correction = IO_intValue(line,chunkPos,2_pInt) case ('update_gamma') - update_gamma = IO_intValue(line,chunkPos,2_pInt) > 0_pInt + update_gamma = IO_intValue(line,chunkPos,2_pInt) > 0_pInt case ('petsc_options') petsc_options = trim(line(chunkPos(4):)) case ('spectralsolver','myspectralsolver') @@ -599,7 +599,7 @@ subroutine numerics_init !-------------------------------------------------------------------------------------------------- ! spectral parameters #ifdef Spectral - write(6,'(a24,1x,i8)') ' continueCalculation: ',continueCalculation + write(6,'(a24,1x,L8)') ' continueCalculation: ',continueCalculation write(6,'(a24,1x,L8)') ' memory_efficient: ',memory_efficient write(6,'(a24,1x,i8)') ' divergence_correction: ',divergence_correction write(6,'(a24,1x,a)') ' spectral_derivative: ',trim(spectral_derivative) @@ -698,8 +698,6 @@ subroutine numerics_init if (err_hydrogenflux_tolabs <= 0.0_pReal) call IO_error(301_pInt,ext_msg='err_hydrogenflux_tolabs') if (err_hydrogenflux_tolrel <= 0.0_pReal) call IO_error(301_pInt,ext_msg='err_hydrogenflux_tolrel') #ifdef Spectral - if (continueCalculation /= 0_pInt .and. & - continueCalculation /= 1_pInt) call IO_error(301_pInt,ext_msg='continueCalculation') if (divergence_correction < 0_pInt .or. & divergence_correction > 2_pInt) call IO_error(301_pInt,ext_msg='divergence_correction') if (update_gamma .and. & @@ -713,7 +711,7 @@ subroutine numerics_init if (polarAlpha <= 0.0_pReal .or. & polarAlpha > 2.0_pReal) call IO_error(301_pInt,ext_msg='polarAlpha') if (polarBeta < 0.0_pReal .or. & - polarBeta > 2.0_pReal) call IO_error(301_pInt,ext_msg='polarBeta') + polarBeta > 2.0_pReal) call IO_error(301_pInt,ext_msg='polarBeta') #endif end subroutine numerics_init diff --git a/src/spectral_mech_AL.f90 b/src/spectral_mech_AL.f90 index 6d0fff286..4695d4faa 100644 --- a/src/spectral_mech_AL.f90 +++ b/src/spectral_mech_AL.f90 @@ -213,8 +213,9 @@ subroutine AL_init endif restart call Utilities_updateIPcoords(reshape(F,shape(F_lastInc))) - call Utilities_constitutiveResponse(F_lastInc, reshape(F,shape(F_lastInc)), & - 0.0_pReal,P,C_volAvg,C_minMaxAvg,temp33_Real,.false.,math_I3) + call Utilities_constitutiveResponse(P,temp33_Real,C_volAvg,C_minMaxAvg, & + reshape(F,shape(F_lastInc)), 0.0_pReal, math_I3) + nullify(F) nullify(F_lambda) call DMDAVecRestoreArrayF90(da,solution_vec,xx_psc,ierr); CHKERRQ(ierr) ! write data back to PETSc @@ -364,12 +365,10 @@ subroutine AL_formResidual(in,x_scal,f_scal,dummy,ierr) DMDALocalInfo, dimension(& DMDA_LOCAL_INFO_SIZE) :: & in - PetscScalar, target, dimension(3,3,2, & - XG_RANGE,YG_RANGE,ZG_RANGE), intent(in) :: & - x_scal - PetscScalar, target, dimension(3,3,2, & - X_RANGE,Y_RANGE,Z_RANGE), intent(out) :: & - f_scal + PetscScalar, & + target, dimension(3,3,2, XG_RANGE,YG_RANGE,ZG_RANGE), intent(in) :: x_scal + PetscScalar, & + target, dimension(3,3,2, X_RANGE, Y_RANGE, Z_RANGE), intent(out) :: f_scal PetscScalar, pointer, dimension(:,:,:,:,:) :: & F, & F_lambda, & @@ -441,8 +440,9 @@ subroutine AL_formResidual(in,x_scal,f_scal,dummy,ierr) !-------------------------------------------------------------------------------------------------- ! evaluate constitutive response P_avLastEval = P_av - call Utilities_constitutiveResponse(F_lastInc,F - residual_F_lambda/polarBeta,params%timeinc, & - residual_F,C_volAvg,C_minMaxAvg,P_av,ForwardData,params%rotation_BC) + + call Utilities_constitutiveResponse(residual_F,P_av,C_volAvg,C_minMaxAvg, & + F - residual_F_lambda/polarBeta,params%timeinc, params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,PETSC_COMM_WORLD,ierr) ForwardData = .False. @@ -655,10 +655,12 @@ subroutine AL_forward(guess,timeinc,timeinc_old,loadCaseTime,deformation_BC,stre !-------------------------------------------------------------------------------------------------- ! update coordinates and rate and forward last inc call utilities_updateIPcoords(F) - Fdot = Utilities_calculateRate(math_rotate_backward33(f_aimDot,rotation_BC), & - timeinc_old,guess,F_lastInc,reshape(F,[3,3,grid(1),grid(2),grid3])) - F_lambdaDot = Utilities_calculateRate(math_rotate_backward33(f_aimDot,rotation_BC), & - timeinc_old,guess,F_lambda_lastInc,reshape(F_lambda,[3,3,grid(1),grid(2),grid3])) + Fdot = Utilities_calculateRate(guess, & + F_lastInc, reshape(F, [3,3,grid(1),grid(2),grid3]), timeinc_old, & + math_rotate_backward33(f_aimDot,rotation_BC)) + F_lambdaDot = Utilities_calculateRate(guess, & + F_lambda_lastInc,reshape(F_lambda,[3,3,grid(1),grid(2),grid3]), timeinc_old, & + math_rotate_backward33(f_aimDot,rotation_BC)) F_lastInc = reshape(F, [3,3,grid(1),grid(2),grid3]) F_lambda_lastInc = reshape(F_lambda,[3,3,grid(1),grid(2),grid3]) endif diff --git a/src/spectral_mech_Basic.f90 b/src/spectral_mech_Basic.f90 index 55403ee7c..acdcbee3a 100644 --- a/src/spectral_mech_Basic.f90 +++ b/src/spectral_mech_Basic.f90 @@ -39,16 +39,16 @@ module spectral_mech_basic ! stress, stiffness and compliance average etc. real(pReal), private, dimension(3,3) :: & F_aim = math_I3, & - F_aim_lastIter = math_I3, & F_aim_lastInc = math_I3, & P_av = 0.0_pReal, & - F_aimDot=0.0_pReal + F_aimDot = 0.0_pReal character(len=1024), private :: incInfo real(pReal), private, dimension(3,3,3,3) :: & C_volAvg = 0.0_pReal, & !< current volume average stiffness C_volAvgLastInc = 0.0_pReal, & !< previous volume average stiffness C_minMaxAvg = 0.0_pReal, & !< current (min+max)/2 stiffness - S = 0.0_pReal !< current compliance (filled up with zeros) + C_minMaxAvgLastInc = 0.0_pReal, & !< previous (min+max)/2 stiffness + S = 0.0_pReal !< current compliance (filled up with zeros) real(pReal), private :: err_stress, err_div logical, private :: ForwardData integer(pInt), private :: & @@ -69,7 +69,7 @@ module spectral_mech_basic contains !-------------------------------------------------------------------------------------------------- -!> @brief allocates all neccessary fields and fills them with data, potentially from restart info +!> @brief allocates all necessary fields and fills them with data, potentially from restart info !-------------------------------------------------------------------------------------------------- subroutine basicPETSc_init #ifdef __GFORTRAN__ @@ -90,6 +90,8 @@ subroutine basicPETSc_init use numerics, only: & worldrank, & worldsize + use homogenization, only: & + materialpoint_F0 use DAMASK_interface, only: & getSolverJobName use spectral_utilities, only: & @@ -172,14 +174,11 @@ subroutine basicPETSc_init flush(6) write(rankStr,'(a1,i0)')'_',worldrank call IO_read_realFile(777,'F'//trim(rankStr),trim(getSolverJobName()),size(F)) - read (777,rec=1) F - close (777) + read (777,rec=1) F; close (777) call IO_read_realFile(777,'F_lastInc'//trim(rankStr),trim(getSolverJobName()),size(F_lastInc)) - read (777,rec=1) F_lastInc - close (777) + read (777,rec=1) F_lastInc; close (777) call IO_read_realFile(777,'F_aimDot',trim(getSolverJobName()),size(f_aimDot)) - read (777,rec=1) f_aimDot - close (777) + read (777,rec=1) f_aimDot; close (777) F_aim = reshape(sum(sum(sum(F,dim=4),dim=3),dim=2) * wgt, [3,3]) ! average of F F_aim_lastInc = sum(sum(sum(F_lastInc,dim=5),dim=4),dim=3) * wgt ! average of F_lastInc elseif (restartInc == 1_pInt) then restart @@ -187,41 +186,36 @@ subroutine basicPETSc_init F = reshape(F_lastInc,[9,grid(1),grid(2),grid3]) endif restart + materialpoint_F0 = reshape(F_lastInc, [3,3,1,product(grid(1:2))*grid3]) ! set starting condition for materialpoint_stressAndItsTangent call Utilities_updateIPcoords(reshape(F,shape(F_lastInc))) - call Utilities_constitutiveResponse(F_lastInc, reshape(F,shape(F_lastInc)), & - 0.0_pReal, & - P, & - C_volAvg,C_minMaxAvg, & ! global average of stiffness and (min+max)/2 - temp33_Real, & - .false., & - math_I3) + call Utilities_constitutiveResponse(P, temp33_Real, C_volAvg,C_minMaxAvg, & ! stress field, stress avg, global average of stiffness and (min+max)/2 + reshape(F,shape(F_lastInc)), & ! target F + 0.0_pReal, & ! time increment + math_I3) ! no rotation of boundary condition call DMDAVecRestoreArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) ! write data back to PETSc + ! QUESTION: why not writing back right after reading (l.189)? - restartRead: if (restartInc > 1_pInt) then + restartRead: if (restartInc > 1_pInt) then ! QUESTION: are those values not calc'ed by constitutiveResponse? why reading from file? if (iand(debug_level(debug_spectral),debug_spectralRestart)/= 0 .and. worldrank == 0_pInt) & write(6,'(/,a,'//IO_intOut(restartInc-1_pInt)//',a)') & - 'reading more values of increment', restartInc - 1_pInt, 'from file' + 'reading more values of increment', restartInc-1_pInt, 'from file' flush(6) call IO_read_realFile(777,'C_volAvg',trim(getSolverJobName()),size(C_volAvg)) - read (777,rec=1) C_volAvg - close (777) + read (777,rec=1) C_volAvg; close (777) call IO_read_realFile(777,'C_volAvgLastInc',trim(getSolverJobName()),size(C_volAvgLastInc)) - read (777,rec=1) C_volAvgLastInc - close (777) + read (777,rec=1) C_volAvgLastInc; close (777) call IO_read_realFile(777,'C_ref',trim(getSolverJobName()),size(C_minMaxAvg)) - read (777,rec=1) C_minMaxAvg - close (777) + read (777,rec=1) C_minMaxAvg; close (777) endif restartRead - call Utilities_updateGamma(C_minmaxAvg,.True.) + call Utilities_updateGamma(C_minmaxAvg,.true.) end subroutine basicPETSc_init !-------------------------------------------------------------------------------------------------- !> @brief solution for the Basic PETSC scheme with internal iterations !-------------------------------------------------------------------------------------------------- -type(tSolutionState) function & - basicPETSc_solution(incInfoIn,timeinc,timeinc_old,stress_BC,rotation_BC) +type(tSolutionState) function basicPETSc_solution(incInfoIn,timeinc,timeinc_old,stress_BC,rotation_BC) use IO, only: & IO_error use numerics, only: & @@ -238,13 +232,13 @@ type(tSolutionState) function & !-------------------------------------------------------------------------------------------------- ! input data for solution - real(pReal), intent(in) :: & - timeinc, & !< increment in time for current solution - timeinc_old !< increment in time of last increment - type(tBoundaryCondition), intent(in) :: & - stress_BC character(len=*), intent(in) :: & incInfoIn + real(pReal), intent(in) :: & + timeinc, & !< increment time for current solution + timeinc_old !< increment time of last successful increment + type(tBoundaryCondition), intent(in) :: & + stress_BC real(pReal), dimension(3,3), intent(in) :: rotation_BC !-------------------------------------------------------------------------------------------------- @@ -279,14 +273,13 @@ type(tSolutionState) function & !-------------------------------------------------------------------------------------------------- ! check convergence - call SNESGetConvergedReason(snes,reason,ierr) - CHKERRQ(ierr) + call SNESGetConvergedReason(snes,reason,ierr); CHKERRQ(ierr) + + BasicPETSc_solution%converged = reason > 0 + basicPETSC_solution%iterationsNeeded = totalIter basicPETSc_solution%termIll = terminallyIll terminallyIll = .false. - BasicPETSc_solution%converged =.true. - if (reason == -4) call IO_error(893_pInt) - if (reason < 1) basicPETSC_solution%converged = .false. - basicPETSC_solution%iterationsNeeded = totalIter + if (reason == -4) call IO_error(893_pInt) ! MPI error end function BasicPETSc_solution @@ -322,19 +315,18 @@ subroutine BasicPETSC_formResidual(in,x_scal,f_scal,dummy,ierr) terminallyIll implicit none - DMDALocalInfo, dimension(DMDA_LOCAL_INFO_SIZE) :: & - in - PetscScalar, dimension(3,3, & - XG_RANGE,YG_RANGE,ZG_RANGE), intent(in) :: & - x_scal - PetscScalar, dimension(3,3, & - X_RANGE,Y_RANGE,Z_RANGE), intent(out) :: & - f_scal + DMDALocalInfo, dimension(DMDA_LOCAL_INFO_SIZE) :: in + PetscScalar, & + dimension(3,3, XG_RANGE,YG_RANGE,ZG_RANGE), intent(in) :: x_scal !< what is this? + PetscScalar, & + dimension(3,3, X_RANGE,Y_RANGE,Z_RANGE), intent(out) :: f_scal !< what is this? PetscInt :: & PETScIter, & nfuncs PetscObject :: dummy PetscErrorCode :: ierr + real(pReal), dimension(3,3) :: & + deltaF_aim external :: & SNESGetNumberFunctionEvals, & @@ -343,46 +335,45 @@ subroutine BasicPETSC_formResidual(in,x_scal,f_scal,dummy,ierr) call SNESGetNumberFunctionEvals(snes,nfuncs,ierr); CHKERRQ(ierr) call SNESGetIterationNumber(snes,PETScIter,ierr); CHKERRQ(ierr) - if(nfuncs== 0 .and. PETScIter == 0) totalIter = -1_pInt ! new increment - newIteration: if(totalIter <= PETScIter) then + if (nfuncs == 0 .and. PETScIter == 0) totalIter = -1_pInt ! new increment !-------------------------------------------------------------------------------------------------- -! report begin of new iteration +! begin of new iteration + newIteration: if (totalIter <= PETScIter) then totalIter = totalIter + 1_pInt - write(6,'(1x,a,3(a,'//IO_intOut(itmax)//'))') trim(incInfo), & - ' @ Iteration ', itmin, '≤',totalIter, '≤', itmax + write(6,'(1x,a,3(a,'//IO_intOut(itmax)//'))') & + trim(incInfo), ' @ Iteration ', itmin, '≤',totalIter, '≤', itmax if (iand(debug_level(debug_spectral),debug_spectralRotation) /= 0) & - write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') ' deformation gradient aim (lab) =', & - math_transpose33(math_rotate_backward33(F_aim,params%rotation_BC)) - write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') ' deformation gradient aim =', & - math_transpose33(F_aim) + write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & + ' deformation gradient aim (lab) =', math_transpose33(math_rotate_backward33(F_aim,params%rotation_BC)) + write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & + ' deformation gradient aim =', math_transpose33(F_aim) flush(6) endif newIteration !-------------------------------------------------------------------------------------------------- ! evaluate constitutive response - call Utilities_constitutiveResponse(F_lastInc,x_scal,params%timeinc, & - f_scal,C_volAvg,C_minmaxAvg,P_av,ForwardData,params%rotation_BC) + call Utilities_constitutiveResponse(f_scal,P_av,C_volAvg,C_minmaxAvg, & + x_scal,params%timeinc, params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,PETSC_COMM_WORLD,ierr) - ForwardData = .false. !-------------------------------------------------------------------------------------------------- ! stress BC handling - F_aim_lastIter = F_aim - F_aim = F_aim - math_mul3333xx33(S, ((P_av - params%stress_BC))) ! S = 0.0 for no bc - err_stress = maxval(abs(mask_stress * (P_av - params%stress_BC))) ! mask = 0.0 for no bc + deltaF_aim = math_mul3333xx33(S, P_av - params%stress_BC) + F_aim = F_aim - deltaF_aim + err_stress = maxval(abs(mask_stress * (P_av - params%stress_BC))) ! mask = 0.0 when no stress bc !-------------------------------------------------------------------------------------------------- ! updated deformation gradient using fix point algorithm of basic scheme tensorField_real = 0.0_pReal tensorField_real(1:3,1:3,1:grid(1),1:grid(2),1:grid3) = f_scal - call utilities_FFTtensorForward() - err_div = Utilities_divergenceRMS() - call utilities_fourierGammaConvolution(math_rotate_backward33(F_aim_lastIter-F_aim,params%rotation_BC)) - call utilities_FFTtensorBackward() + call utilities_FFTtensorForward() ! FFT forward of global "tensorField_real" + err_div = Utilities_divergenceRMS() ! divRMS of tensorField_fourier + call utilities_fourierGammaConvolution(math_rotate_backward33(deltaF_aim,params%rotation_BC)) ! convolution of Gamma and tensorField_fourier, with arg + call utilities_FFTtensorBackward() ! FFT backward of global tensorField_fourier !-------------------------------------------------------------------------------------------------- ! constructing residual - f_scal = tensorField_real(1:3,1:3,1:grid(1),1:grid(2),1:grid3) + f_scal = tensorField_real(1:3,1:3,1:grid(1),1:grid(2),1:grid3) ! Gamma*P gives correction towards div(P) = 0, so needs to be zero, too end subroutine BasicPETSc_formResidual @@ -443,106 +434,120 @@ end subroutine BasicPETSc_converged !-------------------------------------------------------------------------------------------------- !> @brief forwarding routine +!> @details find new boundary conditions and best F estimate for end of current timestep +!> possibly writing restart information, triggering of state increment in DAMASK, and updating of IPcoordinates !-------------------------------------------------------------------------------------------------- subroutine BasicPETSc_forward(guess,timeinc,timeinc_old,loadCaseTime,deformation_BC,stress_BC,rotation_BC) - use math, only: & - math_mul33x33 ,& - math_rotate_backward33 - use numerics, only: & - worldrank - use mesh, only: & - grid, & - grid3 - use spectral_utilities, only: & - Utilities_calculateRate, & - Utilities_forwardField, & - Utilities_updateIPcoords, & - tBoundaryCondition, & - cutBack - use IO, only: & - IO_write_JobRealFile - use FEsolving, only: & - restartWrite + use math, only: & + math_mul33x33 ,& + math_rotate_backward33 + use numerics, only: & + worldrank + use homogenization, only: & + materialpoint_F0 + use mesh, only: & + grid, & + grid3 + use CPFEM2, only: & + CPFEM_age + use spectral_utilities, only: & + Utilities_calculateRate, & + Utilities_forwardField, & + Utilities_updateIPcoords, & + tBoundaryCondition, & + cutBack + use IO, only: & + IO_write_JobRealFile + use FEsolving, only: & + restartWrite - implicit none - real(pReal), intent(in) :: & - timeinc_old, & - timeinc, & - loadCaseTime !< remaining time of current load case - type(tBoundaryCondition), intent(in) :: & - stress_BC, & - deformation_BC - real(pReal), dimension(3,3), intent(in) :: rotation_BC - logical, intent(in) :: & - guess - PetscErrorCode :: ierr - PetscScalar, pointer :: F(:,:,:,:) + implicit none + logical, intent(in) :: & + guess + real(pReal), intent(in) :: & + timeinc_old, & + timeinc, & + loadCaseTime !< remaining time of current load case + type(tBoundaryCondition), intent(in) :: & + stress_BC, & + deformation_BC + real(pReal), dimension(3,3), intent(in) ::& + rotation_BC + PetscErrorCode :: ierr + PetscScalar, pointer :: F(:,:,:,:) - character(len=1024) :: rankStr + character(len=32) :: rankStr - call DMDAVecGetArrayF90(da,solution_vec,F,ierr) -!-------------------------------------------------------------------------------------------------- -! restart information for spectral solver - if (restartWrite) then - write(6,'(/,a)') ' writing converged results for restart' - flush(6) - write(rankStr,'(a1,i0)')'_',worldrank - call IO_write_jobRealFile(777,'F'//trim(rankStr),size(F)) ! writing deformation gradient field to file - write (777,rec=1) F - close (777) - call IO_write_jobRealFile(777,'F_lastInc'//trim(rankStr),size(F_lastInc)) ! writing F_lastInc field to file - write (777,rec=1) F_lastInc - close (777) - if (worldrank == 0_pInt) then - call IO_write_jobRealFile(777,'F_aimDot',size(F_aimDot)) - write (777,rec=1) F_aimDot - close(777) - call IO_write_jobRealFile(777,'C_volAvg',size(C_volAvg)) - write (777,rec=1) C_volAvg - close(777) - call IO_write_jobRealFile(777,'C_volAvgLastInc',size(C_volAvgLastInc)) - write (777,rec=1) C_volAvgLastInc - close(777) - endif - endif + call DMDAVecGetArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) + + if (cutBack) then + C_volAvg = C_volAvgLastInc ! QUESTION: where is this required? + C_minMaxAvg = C_minMaxAvgLastInc ! QUESTION: where is this required? + else + !-------------------------------------------------------------------------------------------------- + ! restart information for spectral solver + if (restartWrite) then ! QUESTION: where is this logical properly set? + write(6,'(/,a)') ' writing converged results for restart' + flush(6) - call utilities_updateIPcoords(F) + if (worldrank == 0_pInt) then + call IO_write_jobRealFile(777,'C_volAvg',size(C_volAvg)) + write (777,rec=1) C_volAvg; close(777) + call IO_write_jobRealFile(777,'C_volAvgLastInc',size(C_volAvgLastInc)) + write (777,rec=1) C_volAvgLastInc; close(777) + call IO_write_jobRealFile(777,'C_minMaxAvg',size(C_volAvg)) + write (777,rec=1) C_minMaxAvg; close(777) + call IO_write_jobRealFile(777,'C_minMaxAvgLastInc',size(C_volAvgLastInc)) + write (777,rec=1) C_minMaxAvgLastInc; close(777) + endif - if (cutBack) then - F_aim = F_aim_lastInc - F = reshape(F_lastInc, [9,grid(1),grid(2),grid3]) - C_volAvg = C_volAvgLastInc - else - ForwardData = .True. - C_volAvgLastInc = C_volAvg -!-------------------------------------------------------------------------------------------------- -! calculate rate for aim - if (deformation_BC%myType=='l') then ! calculate f_aimDot from given L and current F - f_aimDot = deformation_BC%maskFloat * math_mul33x33(deformation_BC%values, F_aim) - elseif(deformation_BC%myType=='fdot') then ! f_aimDot is prescribed - f_aimDot = deformation_BC%maskFloat * deformation_BC%values - elseif(deformation_BC%myType=='f') then ! aim at end of load case is prescribed - f_aimDot = deformation_BC%maskFloat * (deformation_BC%values -F_aim)/loadCaseTime - endif - if (guess) f_aimDot = f_aimDot + stress_BC%maskFloat * (F_aim - F_aim_lastInc)/timeinc_old - F_aim_lastInc = F_aim + write(rankStr,'(a1,i0)')'_',worldrank + call IO_write_jobRealFile(777,'F'//trim(rankStr),size(F)) ! writing deformation gradient field to file + write (777,rec=1) F; close (777) + call IO_write_jobRealFile(777,'F_lastInc'//trim(rankStr),size(F_lastInc)) ! writing F_lastInc field to file + write (777,rec=1) F_lastInc; close (777) + endif + + call CPFEM_age() ! age state and kinematics + call utilities_updateIPcoords(F) + + C_volAvgLastInc = C_volAvg + C_minMaxAvgLastInc = C_minMaxAvg + + if (guess) then ! QUESTION: better with a = L ? x:y + F_aimDot = stress_BC%maskFloat * (F_aim - F_aim_lastInc)/timeinc_old ! initialize with correction based on last inc + else + F_aimDot = 0.0_pReal + endif + F_aim_lastInc = F_aim + !-------------------------------------------------------------------------------------------------- + ! calculate rate for aim + if (deformation_BC%myType=='l') then ! calculate f_aimDot from given L and current F + F_aimDot = & + F_aimDot + deformation_BC%maskFloat * math_mul33x33(deformation_BC%values, F_aim_lastInc) + elseif(deformation_BC%myType=='fdot') then ! f_aimDot is prescribed + F_aimDot = & + F_aimDot + deformation_BC%maskFloat * deformation_BC%values + elseif (deformation_BC%myType=='f') then ! aim at end of load case is prescribed + F_aimDot = & + F_aimDot + deformation_BC%maskFloat * (deformation_BC%values - F_aim_lastInc)/loadCaseTime + endif + + + Fdot = Utilities_calculateRate(guess, & + F_lastInc,reshape(F,[3,3,grid(1),grid(2),grid3]),timeinc_old, & + math_rotate_backward33(f_aimDot,rotation_BC)) + F_lastInc = reshape(F, [3,3,grid(1),grid(2),grid3]) ! winding F forward + materialpoint_F0 = reshape(F_lastInc, [3,3,1,product(grid(1:2))*grid3]) ! set starting condition for materialpoint_stressAndItsTangent + endif !-------------------------------------------------------------------------------------------------- -! update coordinates and rate and forward last inc - call utilities_updateIPcoords(F) - Fdot = Utilities_calculateRate(math_rotate_backward33(f_aimDot,rotation_BC), & - timeinc_old,guess,F_lastInc,reshape(F,[3,3,grid(1),grid(2),grid3])) - F_lastInc = reshape(F, [3,3,grid(1),grid(2),grid3]) - endif - - F_aim = F_aim + f_aimDot * timeinc - -!-------------------------------------------------------------------------------------------------- -! update local deformation gradient - F = reshape(Utilities_forwardField(timeinc,F_lastInc,Fdot, & ! ensure that it matches rotated F_aim - math_rotate_backward33(F_aim,rotation_BC)),[9,grid(1),grid(2),grid3]) - call DMDAVecRestoreArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) - +! update average and local deformation gradients + F_aim = F_aim_lastInc + f_aimDot * timeinc + F = reshape(Utilities_forwardField(timeinc,F_lastInc,Fdot, & ! estimate of F at end of time+timeinc that matches rotated F_aim on average + math_rotate_backward33(F_aim,rotation_BC)),[9,grid(1),grid(2),grid3]) + call DMDAVecRestoreArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) + end subroutine BasicPETSc_forward !-------------------------------------------------------------------------------------------------- diff --git a/src/spectral_mech_Polarisation.f90 b/src/spectral_mech_Polarisation.f90 index ecf707d46..fc65f14cf 100644 --- a/src/spectral_mech_Polarisation.f90 +++ b/src/spectral_mech_Polarisation.f90 @@ -213,8 +213,8 @@ subroutine Polarisation_init endif restart call Utilities_updateIPcoords(reshape(F,shape(F_lastInc))) - call Utilities_constitutiveResponse(F_lastInc, reshape(F,shape(F_lastInc)), & - 0.0_pReal,P,C_volAvg,C_minMaxAvg,temp33_Real,.false.,math_I3) + call Utilities_constitutiveResponse(P,temp33_Real,C_volAvg,C_minMaxAvg, & + reshape(F,shape(F_lastInc)),0.0_pReal,math_I3) nullify(F) nullify(F_tau) call DMDAVecRestoreArrayF90(da,solution_vec,xx_psc,ierr); CHKERRQ(ierr) ! write data back to PETSc @@ -364,12 +364,10 @@ subroutine Polarisation_formResidual(in,x_scal,f_scal,dummy,ierr) DMDALocalInfo, dimension(& DMDA_LOCAL_INFO_SIZE) :: & in - PetscScalar, target, dimension(3,3,2, & - XG_RANGE,YG_RANGE,ZG_RANGE), intent(in) :: & - x_scal - PetscScalar, target, dimension(3,3,2, & - X_RANGE,Y_RANGE,Z_RANGE), intent(out) :: & - f_scal + PetscScalar, & + target, dimension(3,3,2, XG_RANGE,YG_RANGE,ZG_RANGE), intent(in) :: x_scal + PetscScalar, & + target, dimension(3,3,2, X_RANGE, Y_RANGE, Z_RANGE), intent(out) :: f_scal PetscScalar, pointer, dimension(:,:,:,:,:) :: & F, & F_tau, & @@ -440,8 +438,8 @@ subroutine Polarisation_formResidual(in,x_scal,f_scal,dummy,ierr) !-------------------------------------------------------------------------------------------------- ! evaluate constitutive response P_avLastEval = P_av - call Utilities_constitutiveResponse(F_lastInc,F - residual_F_tau/polarBeta,params%timeinc, & - residual_F,C_volAvg,C_minMaxAvg,P_av,ForwardData,params%rotation_BC) + call Utilities_constitutiveResponse(residual_F,P_av,C_volAvg,C_minMaxAvg, & + F - residual_F_tau/polarBeta,params%timeinc,params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,PETSC_COMM_WORLD,ierr) ForwardData = .False. @@ -654,13 +652,13 @@ subroutine Polarisation_forward(guess,timeinc,timeinc_old,loadCaseTime,deformati !-------------------------------------------------------------------------------------------------- ! update coordinates and rate and forward last inc call utilities_updateIPcoords(F) - Fdot = Utilities_calculateRate(math_rotate_backward33(f_aimDot,rotation_BC), & - timeinc_old,guess,F_lastInc, & - reshape(F,[3,3,grid(1),grid(2),grid3])) - F_tauDot = Utilities_calculateRate(math_rotate_backward33(2.0_pReal*f_aimDot,rotation_BC), & - timeinc_old,guess,F_tau_lastInc, & - reshape(F_tau,[3,3,grid(1),grid(2),grid3])) - F_lastInc = reshape(F, [3,3,grid(1),grid(2),grid3]) + Fdot = Utilities_calculateRate(guess, & + F_lastInc, reshape(F, [3,3,grid(1),grid(2),grid3]), timeinc_old, & + math_rotate_backward33( f_aimDot,rotation_BC)) + F_tauDot = Utilities_calculateRate(guess, & + F_tau_lastInc, reshape(F_tau,[3,3,grid(1),grid(2),grid3]), timeinc_old, & + math_rotate_backward33(2.0_pReal*f_aimDot,rotation_BC)) + F_lastInc = reshape(F, [3,3,grid(1),grid(2),grid3]) F_tau_lastInc = reshape(F_tau,[3,3,grid(1),grid(2),grid3]) endif diff --git a/src/spectral_utilities.f90 b/src/spectral_utilities.f90 index 1bbf2e608..3295aa2bd 100644 --- a/src/spectral_utilities.f90 +++ b/src/spectral_utilities.f90 @@ -16,7 +16,7 @@ module spectral_utilities #include include 'fftw3-mpi.f03' - logical, public :: cutBack =.false. !< cut back of BVP solver in case convergence is not achieved or a material point is terminally ill + logical, public :: cutBack = .false. !< cut back of BVP solver in case convergence is not achieved or a material point is terminally ill integer(pInt), public, parameter :: maxPhaseFields = 2_pInt integer(pInt), public :: nActiveFields = 0_pInt @@ -799,7 +799,7 @@ function utilities_maskedCompliance(rot_BC,mask_stress,C) call math_invert(size_reduced, c_reduced, s_reduced, errmatinv) ! invert reduced stiffness if (any(IEEE_is_NaN(s_reduced))) errmatinv = .true. - if(errmatinv) call IO_error(error_ID=400_pInt,ext_msg='utilities_maskedCompliance') + if (errmatinv) call IO_error(error_ID=400_pInt,ext_msg='utilities_maskedCompliance') temp99_Real = 0.0_pReal ! fill up compliance with zeros k = 0_pInt do n = 1_pInt,9_pInt @@ -817,28 +817,30 @@ function utilities_maskedCompliance(rot_BC,mask_stress,C) sTimesC = matmul(c_reduced,s_reduced) do m=1_pInt, size_reduced do n=1_pInt, size_reduced - if(m==n .and. abs(sTimesC(m,n)) > (1.0_pReal + 10.0e-12_pReal)) errmatinv = .true. ! diagonal elements of S*C should be 1 - if(m/=n .and. abs(sTimesC(m,n)) > (0.0_pReal + 10.0e-12_pReal)) errmatinv = .true. ! off diagonal elements of S*C should be 0 + errmatinv = errmatinv & + .or. (m==n .and. abs(sTimesC(m,n)-1.0_pReal) > 1.0e-12_pReal) & ! diagonal elements of S*C should be 1 + .or. (m/=n .and. abs(sTimesC(m,n)) > 1.0e-12_pReal) ! off-diagonal elements of S*C should be 0 enddo enddo - if(debugGeneral .or. errmatinv) then - write(formatString, '(I16.16)') size_reduced + if (debugGeneral .or. errmatinv) then + write(formatString, '(i2)') size_reduced formatString = '(/,a,/,'//trim(formatString)//'('//trim(formatString)//'(2x,es9.2,1x)/))' write(6,trim(formatString),advance='no') ' C * S (load) ', & transpose(matmul(c_reduced,s_reduced)) write(6,trim(formatString),advance='no') ' S (load) ', transpose(s_reduced) + if(errmatinv) call IO_error(error_ID=400_pInt,ext_msg='utilities_maskedCompliance') endif - if(errmatinv) call IO_error(error_ID=400_pInt,ext_msg='utilities_maskedCompliance') deallocate(c_reduced) deallocate(s_reduced) deallocate(sTimesC) else temp99_real = 0.0_pReal endif - if(debugGeneral) & - write(6,'(/,a,/,9(9(2x,f12.7,1x)/),/)',advance='no') ' Masked Compliance (load) * GPa =', & - transpose(temp99_Real*1.e9_pReal) - flush(6) + if(debugGeneral) then + write(6,'(/,a,/,9(9(2x,f10.5,1x)/),/)',advance='no') & + ' Masked Compliance (load) / GPa =', transpose(temp99_Real*1.e-9_pReal) + flush(6) + endif utilities_maskedCompliance = math_Plain99to3333(temp99_Real) end function utilities_maskedCompliance @@ -924,10 +926,10 @@ end subroutine utilities_fourierTensorDivergence !-------------------------------------------------------------------------------------------------- -!> @brief calculates constitutive response +!> @brief calculate constitutive response from materialpoint_F0 to F during timeinc !-------------------------------------------------------------------------------------------------- -subroutine utilities_constitutiveResponse(F_lastInc,F,timeinc, & - P,C_volAvg,C_minmaxAvg,P_av,forwardData,rotation_BC) +subroutine utilities_constitutiveResponse(P,P_av,C_volAvg,C_minmaxAvg,& + F,timeinc,rotation_BC) use IO, only: & IO_error use debug, only: & @@ -940,31 +942,22 @@ subroutine utilities_constitutiveResponse(F_lastInc,F,timeinc, & use mesh, only: & grid,& grid3 - use FEsolving, only: & - restartWrite - use CPFEM2, only: & - CPFEM_general use homogenization, only: & - materialpoint_F0, & materialpoint_F, & materialpoint_P, & - materialpoint_dPdF + materialpoint_dPdF, & + materialpoint_stressAndItsTangent implicit none - real(pReal), intent(in), dimension(3,3,grid(1),grid(2),grid3) :: & - F_lastInc, & !< target deformation gradient - F !< previous deformation gradient - real(pReal), intent(in) :: timeinc !< loading time - logical, intent(in) :: forwardData !< age results - real(pReal), intent(in), dimension(3,3) :: rotation_BC !< rotation of load frame - real(pReal),intent(out), dimension(3,3,3,3) :: C_volAvg, C_minmaxAvg !< average stiffness real(pReal),intent(out), dimension(3,3) :: P_av !< average PK stress real(pReal),intent(out), dimension(3,3,grid(1),grid(2),grid3) :: P !< PK stress - logical :: & - age + real(pReal), intent(in), dimension(3,3,grid(1),grid(2),grid3) :: F !< deformation gradient target !< previous deformation gradient + real(pReal), intent(in) :: timeinc !< loading time + real(pReal), intent(in), dimension(3,3) :: rotation_BC !< rotation of load frame + integer(pInt) :: & j,k,ierr real(pReal), dimension(3,3,3,3) :: max_dPdF, min_dPdF @@ -975,17 +968,9 @@ subroutine utilities_constitutiveResponse(F_lastInc,F,timeinc, & write(6,'(/,a)') ' ... evaluating constitutive response ......................................' flush(6) - age = .False. - - if (forwardData) then ! aging results - age = .True. - materialpoint_F0 = reshape(F_lastInc, [3,3,1,product(grid(1:2))*grid3]) - endif - if (cutBack) age = .False. ! restore saved variables - - materialpoint_F = reshape(F,[3,3,1,product(grid(1:2))*grid3]) - call debug_reset() ! this has no effect on rank >0 - + + materialpoint_F = reshape(F,[3,3,1,product(grid(1:2))*grid3]) ! set materialpoint target F to estimated field + !-------------------------------------------------------------------------------------------------- ! calculate bounds of det(F) and report if(debugGeneral) then @@ -1002,7 +987,19 @@ subroutine utilities_constitutiveResponse(F_lastInc,F,timeinc, & flush(6) endif - call CPFEM_general(age,timeinc) + call debug_reset() ! this has no effect on rank >0 + call materialpoint_stressAndItsTangent(.true.,timeinc) ! calculate P field + + P = reshape(materialpoint_P, [3,3,grid(1),grid(2),grid3]) + P_av = sum(sum(sum(P,dim=5),dim=4),dim=3) * wgt ! average of P + call MPI_Allreduce(MPI_IN_PLACE,P_av,9,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD,ierr) + if (debugRotation) & + write(6,'(/,a,/,3(3(2x,f12.4,1x)/))',advance='no') ' Piola--Kirchhoff stress (lab) / MPa =',& + math_transpose33(P_av)*1.e-6_pReal + P_av = math_rotate_forward33(P_av,rotation_BC) + write(6,'(/,a,/,3(3(2x,f12.4,1x)/))',advance='no') ' Piola--Kirchhoff stress / MPa =',& + math_transpose33(P_av)*1.e-6_pReal + flush(6) max_dPdF = 0.0_pReal max_dPdF_norm = 0.0_pReal @@ -1020,38 +1017,24 @@ subroutine utilities_constitutiveResponse(F_lastInc,F,timeinc, & end do call MPI_Allreduce(MPI_IN_PLACE,max_dPdF,81,MPI_DOUBLE,MPI_MAX,PETSC_COMM_WORLD,ierr) - if(ierr /=0_pInt) call IO_error(894_pInt, ext_msg='MPI_Allreduce max') + if (ierr /= 0_pInt) call IO_error(894_pInt, ext_msg='MPI_Allreduce max') call MPI_Allreduce(MPI_IN_PLACE,min_dPdF,81,MPI_DOUBLE,MPI_MIN,PETSC_COMM_WORLD,ierr) - if(ierr /=0_pInt) call IO_error(894_pInt, ext_msg='MPI_Allreduce min') + if (ierr /= 0_pInt) call IO_error(894_pInt, ext_msg='MPI_Allreduce min') C_minmaxAvg = 0.5_pReal*(max_dPdF + min_dPdF) - C_volAvg = sum(sum(materialpoint_dPdF,dim=6),dim=5) * wgt + C_volAvg = sum(sum(materialpoint_dPdF,dim=6),dim=5) * wgt call MPI_Allreduce(MPI_IN_PLACE,C_volAvg,81,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD,ierr) call debug_info() ! this has no effect on rank >0 - restartWrite = .false. ! reset restartWrite status - cutBack = .false. ! reset cutBack status - - P = reshape(materialpoint_P, [3,3,grid(1),grid(2),grid3]) - P_av = sum(sum(sum(P,dim=5),dim=4),dim=3) * wgt ! average of P - call MPI_Allreduce(MPI_IN_PLACE,P_av,9,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD,ierr) - if (debugRotation) & - write(6,'(/,a,/,3(3(2x,f12.4,1x)/))',advance='no') ' Piola--Kirchhoff stress (lab) / MPa =',& - math_transpose33(P_av)*1.e-6_pReal - P_av = math_rotate_forward33(P_av,rotation_BC) - write(6,'(/,a,/,3(3(2x,f12.4,1x)/))',advance='no') ' Piola--Kirchhoff stress / MPa =',& - math_transpose33(P_av)*1.e-6_pReal - flush(6) - end subroutine utilities_constitutiveResponse !-------------------------------------------------------------------------------------------------- !> @brief calculates forward rate, either guessing or just add delta/timeinc !-------------------------------------------------------------------------------------------------- -pure function utilities_calculateRate(avRate,timeinc_old,guess,field_lastInc,field) +pure function utilities_calculateRate(heterogeneous,field0,field,dt,avRate) use mesh, only: & grid3, & grid @@ -1059,17 +1042,17 @@ pure function utilities_calculateRate(avRate,timeinc_old,guess,field_lastInc,fie implicit none real(pReal), intent(in), dimension(3,3) :: avRate !< homogeneous addon real(pReal), intent(in) :: & - timeinc_old !< timeinc of last step + dt !< timeinc between field0 and field logical, intent(in) :: & - guess !< guess along former trajectory + heterogeneous !< calculate field of rates real(pReal), intent(in), dimension(3,3,grid(1),grid(2),grid3) :: & - field_lastInc, & !< data of previous step + field0, & !< data of previous step field !< data of current step real(pReal), dimension(3,3,grid(1),grid(2),grid3) :: & utilities_calculateRate - if (guess) then - utilities_calculateRate = (field-field_lastInc) / timeinc_old + if (heterogeneous) then + utilities_calculateRate = (field-field0) / dt else utilities_calculateRate = spread(spread(spread(avRate,3,grid(1)),4,grid(2)),5,grid3) endif