Solid models update - boundary traction
This commit is contained in:
parent
47c824e967
commit
3810b29f37
20 changed files with 1491 additions and 1249 deletions
|
@ -116,10 +116,10 @@ solidCohesiveFvPatchVectorField::solidCohesiveFvPatchVectorField
|
|||
nonLinear_(nonLinearGeometry::OFF),
|
||||
orthotropic_(false)
|
||||
{
|
||||
Info << "Creating solidCohesive patch" << nl
|
||||
Info<< "Creating solidCohesive patch" << nl
|
||||
<< "\tOnly Dugdale law currently available!" << endl;
|
||||
|
||||
//- check if traction boundary is for non linear solver
|
||||
// Check if traction boundary is for non linear solver
|
||||
if (dict.found("nonLinear"))
|
||||
{
|
||||
nonLinear_ = nonLinearGeometry::nonLinearNames_.read
|
||||
|
@ -415,12 +415,13 @@ tmp<scalarField> solidCohesiveFvPatchVectorField::crackingAndDamage() const
|
|||
(
|
||||
new scalarField(size(), 1.0)
|
||||
);
|
||||
scalarField& cad = tcrackingAndDamage();
|
||||
|
||||
forAll(tcrackingAndDamage(), facei)
|
||||
forAll(cad, facei)
|
||||
{
|
||||
if (cracked_[facei])
|
||||
{
|
||||
tcrackingAndDamage()[facei] = 2.0;
|
||||
cad[facei] = 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,10 +435,11 @@ tmp<scalarField> solidCohesiveFvPatchVectorField::GI() const
|
|||
(
|
||||
new scalarField(size(), 0.0)
|
||||
);
|
||||
scalarField& GI = tGI();
|
||||
|
||||
forAll(tGI(), facei)
|
||||
forAll(GI, facei)
|
||||
{
|
||||
tGI()[facei] = currentGI_[facei];
|
||||
GI[facei] = currentGI_[facei];
|
||||
}
|
||||
|
||||
return tGI;
|
||||
|
@ -450,15 +452,17 @@ tmp<scalarField> solidCohesiveFvPatchVectorField::GII() const
|
|||
(
|
||||
new scalarField(size(), 0.0)
|
||||
);
|
||||
scalarField& GII = tGII();
|
||||
|
||||
forAll(tGII(), facei)
|
||||
forAll(GII, facei)
|
||||
{
|
||||
tGII()[facei] = currentGII_[facei];
|
||||
GII[facei] = currentGII_[facei];
|
||||
}
|
||||
|
||||
return tGII;
|
||||
}
|
||||
|
||||
|
||||
bool solidCohesiveFvPatchVectorField::cracking()
|
||||
{
|
||||
const fvMesh& mesh = patch().boundaryMesh().mesh();
|
||||
|
@ -470,6 +474,7 @@ bool solidCohesiveFvPatchVectorField::cracking()
|
|||
|
||||
label sumDamaged = 0;
|
||||
label sumCracked = 0;
|
||||
|
||||
forAll(globalCracked, facei)
|
||||
{
|
||||
if (globalCracked[facei] > 0.0)
|
||||
|
@ -481,8 +486,8 @@ bool solidCohesiveFvPatchVectorField::cracking()
|
|||
sumDamaged++;
|
||||
}
|
||||
}
|
||||
Info << "\t\tThere are " << sumDamaged << " damaged faces" << endl;
|
||||
Info << "\t\tThere are " << sumCracked << " cracked faces" << endl;
|
||||
Info<< "\t\tThere are " << sumDamaged << " damaged faces" << nl
|
||||
<< "\t\tThere are " << sumCracked << " cracked faces" << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -534,10 +539,11 @@ void solidCohesiveFvPatchVectorField::autoMap
|
|||
// the traction_ field after mapping and call updateCoeffs.
|
||||
// For here, we will just set traction to zero.
|
||||
|
||||
if ( (patch().size()==1) && (nNewFaces == 1) )
|
||||
if ( (patch().size() == 1) && (nNewFaces == 1) )
|
||||
{
|
||||
label i=0;
|
||||
this->valueFraction()[i] = symmTensor::zero;
|
||||
|
||||
traction_[i] = vector::zero;
|
||||
cracked_[i] = false;
|
||||
curTractionN_[i] = 0.0;
|
||||
|
@ -556,12 +562,13 @@ void solidCohesiveFvPatchVectorField::autoMap
|
|||
currentGII_[i] = 0.0;
|
||||
oldGII_[i] = 0.0;
|
||||
}
|
||||
else if ( (patch().size()==2) && (nNewFaces == 1) )
|
||||
else if ( (patch().size() == 2) && (nNewFaces == 1) )
|
||||
{
|
||||
label i=1;
|
||||
this->valueFraction()[i] = symmTensor::zero;
|
||||
traction_[i] = vector::zero;
|
||||
cracked_[i] = false;
|
||||
|
||||
curTractionN_[i] = 0.0;
|
||||
oldTractionN_[i] = 0.0;
|
||||
curTractionS_[i] = 0.0;
|
||||
|
@ -586,6 +593,7 @@ void solidCohesiveFvPatchVectorField::autoMap
|
|||
i=1;
|
||||
this->valueFraction()[i] = symmTensor::zero;
|
||||
traction_[i] = vector::zero;
|
||||
|
||||
cracked_[i] = false;
|
||||
curTractionN_[i] = 0.0;
|
||||
oldTractionN_[i] = 0.0;
|
||||
|
@ -611,6 +619,7 @@ void solidCohesiveFvPatchVectorField::autoMap
|
|||
{
|
||||
this->valueFraction()[i] = symmTensor::zero;
|
||||
traction_[i] = vector::zero;
|
||||
|
||||
cracked_[i] = false;
|
||||
curTractionN_[i] = 0.0;
|
||||
oldTractionN_[i] = 0.0;
|
||||
|
@ -682,6 +691,7 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
(
|
||||
"rheologyProperties"
|
||||
);
|
||||
|
||||
label patchID = patch().index();
|
||||
const scalarField sigmaMax =
|
||||
rheology.cohLaw().sigmaMax()().boundaryField()[patchID];
|
||||
|
@ -717,7 +727,10 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
max
|
||||
(
|
||||
unloadingDeltaEff_,
|
||||
Foam::sqrt(max(deltaN_, 0.0)*max(deltaN_, 0.0) + deltaS_*deltaS_)
|
||||
Foam::sqrt
|
||||
(
|
||||
max(deltaN_, 0.0)*max(deltaN_, 0.0) + deltaS_*deltaS_
|
||||
)
|
||||
);
|
||||
|
||||
curTimeIndex_ = this->db().time().timeIndex();
|
||||
|
@ -760,20 +773,20 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
const regionSplit& regions = crackerMesh.regions();
|
||||
|
||||
labelField faceCellRegion(size(), -1);
|
||||
|
||||
forAll(faceCellRegion, faceI)
|
||||
{
|
||||
faceCellRegion[faceI] = regions[faceCells[faceI]];
|
||||
}
|
||||
|
||||
labelField globalFaceCellRegion =
|
||||
crackerMesh.globalCrackField(faceCellRegion);
|
||||
|
||||
|
||||
// Patch displacement
|
||||
vectorField UPatch = *this;
|
||||
if (fieldName_ == "DU")
|
||||
{
|
||||
UPatch +=
|
||||
patch().lookupPatchField<volVectorField, vector>("U");
|
||||
UPatch += patch().lookupPatchField<volVectorField, vector>("U");
|
||||
}
|
||||
|
||||
// Global displacement
|
||||
|
@ -818,6 +831,7 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
deltaN_[i] =
|
||||
relaxationFactor_*curDeltaN_[i]
|
||||
+ (1.0 - relaxationFactor_)*deltaN_[i];
|
||||
|
||||
deltaS_[i] =
|
||||
relaxationFactor_*curDeltaS_[i]
|
||||
+ (1.0 - relaxationFactor_)*deltaS_[i];
|
||||
|
@ -829,7 +843,7 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
|
||||
// effective delta - only positive deltaN is considered
|
||||
const scalar deltaEff =
|
||||
::sqrt(max(deltaN_[i], 0.0)*max(deltaN_[i], 0.0)
|
||||
Foam::sqrt(max(deltaN_[i], 0.0)*max(deltaN_[i], 0.0)
|
||||
+ deltaS_[i]*deltaS_[i]);
|
||||
|
||||
// update energies
|
||||
|
@ -844,19 +858,19 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
// trapezoidal rule
|
||||
currentGI_[i] =
|
||||
oldGI_[i]
|
||||
+ ((0.5*(curTractionN_[i]+oldTractionN_[i]))
|
||||
*(deltaN_[i]-oldDeltaN_[i]));
|
||||
+ ((0.5*(curTractionN_[i]+oldTractionN_[i]))*
|
||||
(deltaN_[i]-oldDeltaN_[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
// no modeI energy lost if in compression
|
||||
currentGI_[i] = oldGI_[i];
|
||||
}
|
||||
|
||||
// mode II - trapezoidal rule
|
||||
currentGII_[i] =
|
||||
oldGII_[i]
|
||||
+ ((0.5*(curTractionS_[i]+oldTractionS_[i]))
|
||||
*(deltaS_[i]-oldDeltaS_[i]));
|
||||
currentGII_[i] = oldGII_[i]
|
||||
+ ((0.5*(curTractionS_[i]+oldTractionS_[i]))*
|
||||
(deltaS_[i]-oldDeltaS_[i]));
|
||||
}
|
||||
//scalar currentG = currentGI_[i] + currentGII_[i];
|
||||
|
||||
|
@ -885,7 +899,10 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
if ( ((currentGI_[i]/GIc[i]) + (currentGII_[i]/GIIc[i])) >= 1 )
|
||||
{
|
||||
//Pout << "GIc[i] is " << GIc[i] << ", curG is " << currentG << endl;
|
||||
if (!cracked_[i]) Pout << "Face " << i << " is fully cracked" << endl;
|
||||
if (!cracked_[i])
|
||||
{
|
||||
Pout << "Face " << i << " is fully cracked" << endl;
|
||||
}
|
||||
|
||||
cracked_[i] = true;
|
||||
|
||||
|
@ -923,16 +940,20 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
curNormalTraction =
|
||||
relaxationFactor_*curNormalTraction
|
||||
+ (1-relaxationFactor_)*(n[i] & traction_[i]);
|
||||
|
||||
curTangentialTraction =
|
||||
relaxationFactor_*curTangentialTraction
|
||||
+ (1-relaxationFactor_)*( (I -sqr(n[i])) & traction_[i]);
|
||||
|
||||
traction_[i] = curNormalTraction*n[i] + curTangentialTraction;
|
||||
}
|
||||
|
||||
// damging face with positive normal delta
|
||||
else if ( deltaN_[i] > 0.0 )
|
||||
{
|
||||
if (cracked_[i]) Pout << "Face " << i << " is un-cracked" << endl;
|
||||
if (cracked_[i])
|
||||
{
|
||||
Pout << "Face " << i << " is un-cracked" << endl;
|
||||
}
|
||||
|
||||
cracked_[i] = false;
|
||||
|
||||
|
@ -977,16 +998,20 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
curNormalTraction =
|
||||
relaxationFactor_*tN
|
||||
+ (1-relaxationFactor_)*(n[i] & traction_[i]);
|
||||
|
||||
curTangentialTraction =
|
||||
relaxationFactor_*(tS*sDir)
|
||||
+ (1-relaxationFactor_)*( (I -sqr(n[i])) & traction_[i]);
|
||||
|
||||
traction_[i] = curNormalTraction*n[i] + curTangentialTraction;
|
||||
}
|
||||
|
||||
// damaging faces with negative normal delta
|
||||
else
|
||||
{
|
||||
if (cracked_[i]) Pout << "Face " << i << " is un-cracked" << endl;
|
||||
if (cracked_[i])
|
||||
{
|
||||
Pout << "Face " << i << " is un-cracked" << endl;
|
||||
}
|
||||
|
||||
cracked_[i] = false;
|
||||
//Pout << "Contact and shearing face " << i << endl;
|
||||
|
@ -1011,13 +1036,14 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
curNormalTraction =
|
||||
relaxationFactor_*contactTN
|
||||
+ (1-relaxationFactor_)*(n[i] & traction_[i]);
|
||||
|
||||
curTangentialTraction =
|
||||
relaxationFactor_*(tS*sDir)
|
||||
+ (1-relaxationFactor_)*( (I -sqr(n[i])) & traction_[i]);
|
||||
|
||||
traction_[i] = curNormalTraction*n[i] + curTangentialTraction;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// unloading
|
||||
|
@ -1047,34 +1073,40 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
scalar scaleFactor = deltaEff/(unloadingDeltaEff_[i]);
|
||||
traction_[i] =
|
||||
relaxationFactor_*scaleFactor*traction_[i]
|
||||
+ (1-relaxationFactor_)*traction_[i];
|
||||
+ (1 - relaxationFactor_)*traction_[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
this->refGrad() =
|
||||
tractionBoundaryGradient()
|
||||
this->refGrad() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
traction_,
|
||||
scalarField(traction_.size(), 0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
|
||||
directionMixedFvPatchVectorField::updateCoeffs();
|
||||
}
|
||||
|
||||
void solidCohesiveFvPatchVectorField::calcPenaltyFactor()
|
||||
{
|
||||
|
||||
void solidCohesiveFvPatchVectorField::calcPenaltyFactor()
|
||||
{
|
||||
// calculate penalty factor similar to standardPenalty contact model
|
||||
// approx penaltyFactor from mechanical properties
|
||||
// this can then be scaled using the penaltyScale
|
||||
// to-do: write equivalent for orthotropic
|
||||
if (orthotropic_)
|
||||
{
|
||||
FatalError << "solidCohesiveFvPatchVectorField::calcPenaltyFactor()"
|
||||
FatalErrorIn
|
||||
(
|
||||
"void solidCohesiveFvPatchVectorField::calcPenaltyFactor()"
|
||||
) << "solidCohesiveFvPatchVectorField::calcPenaltyFactor()"
|
||||
<< " has yet to be written for orthotropic"
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
@ -1085,6 +1117,7 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
const scalarField& mu =
|
||||
mesh.objectRegistry::lookupObject<volScalarField>
|
||||
("mu").boundaryField()[patchID];
|
||||
|
||||
const scalarField& lambda =
|
||||
mesh.objectRegistry::lookupObject<volScalarField>
|
||||
("lambda").boundaryField()[patchID];
|
||||
|
@ -1097,9 +1130,12 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
|
||||
// average contact patch cell volume
|
||||
scalar cellVolume = 0.0;
|
||||
|
||||
const volScalarField::DimensionedInternalField & V = mesh.V();
|
||||
{
|
||||
const unallocLabelList& faceCells = mesh.boundary()[patchID].faceCells();
|
||||
const unallocLabelList& faceCells =
|
||||
mesh.boundary()[patchID].faceCells();
|
||||
|
||||
forAll(mesh.boundary()[patchID], facei)
|
||||
{
|
||||
cellVolume += V[faceCells[facei]];
|
||||
|
@ -1111,7 +1147,7 @@ void solidCohesiveFvPatchVectorField::updateCoeffs()
|
|||
// we approximate penalty factor for traction instead of force
|
||||
penaltyFactorPtr_ =
|
||||
new scalar(penaltyScale_*bulkModulus*faceArea/cellVolume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write
|
||||
|
|
|
@ -81,8 +81,8 @@ class constitutiveModel
|
|||
//- Plane stress
|
||||
Switch planeStress_;
|
||||
|
||||
//- Run-time selectable solidInterface method for correct discretisation
|
||||
// on bi-material interfaces
|
||||
//- Run-time selectable solidInterface method for correct
|
||||
// discretisation on bi-material interfaces
|
||||
autoPtr<solidInterface> solidInterfacePtr_;
|
||||
|
||||
// we use IOReferencer to allow lookup of solidInterface object in
|
||||
|
@ -141,6 +141,7 @@ public:
|
|||
{
|
||||
return plasticityStressReturnPtr_->plasticityActive();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,51 +36,55 @@ Author
|
|||
#include "constitutiveModel.H"
|
||||
#include "thermalModel.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
// * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * * * //
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
defineTypeNameAndDebug(tractionBoundaryGradient, 0);
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * Member functions * * * * * * * * * * * * * * //
|
||||
|
||||
tmp<vectorField> tractionBoundaryGradient::traction
|
||||
Foam::tmp<Foam::vectorField> Foam::tractionBoundaryGradient::traction
|
||||
(
|
||||
const tensorField& gradField,
|
||||
const word fieldName,
|
||||
const word& workingFieldName,
|
||||
const word& integralFieldName,
|
||||
const fvPatch& patch,
|
||||
Switch orthotropic,
|
||||
word nonLinear
|
||||
) const
|
||||
const bool orthotropic,
|
||||
const nonLinearGeometry::nonLinearType& nonLinear,
|
||||
const bool incremental
|
||||
)
|
||||
{
|
||||
// create tmp
|
||||
// Create result
|
||||
tmp<vectorField> ttraction
|
||||
(
|
||||
new vectorField(gradField.size(), vector::zero)
|
||||
);
|
||||
vectorField& traction = ttraction();
|
||||
|
||||
// Orthotropic material
|
||||
if (orthotropic)
|
||||
{
|
||||
// for now, turn off orthotropic
|
||||
FatalError
|
||||
<< "tractionBoundaryGradient::traction is not written for"
|
||||
<< " orthotropic yet" << nl
|
||||
// For now, turn off orthotropic
|
||||
FatalErrorIn
|
||||
(
|
||||
"tmp<vectorField> tractionBoundaryGradient::traction\n"
|
||||
"(\n"
|
||||
" const tensorField& gradField,\n"
|
||||
" const word& workingFieldName,\n"
|
||||
" const word& integralFieldName,\n"
|
||||
" const fvPatch& patch,\n"
|
||||
" const bool orthotropic,\n"
|
||||
" const nonLinearGeometry::nonLinearType& nonLinear\n"
|
||||
") const"
|
||||
) << "tractionBoundaryGradient::traction is not written for"
|
||||
<< " orthotropic materials yet" << nl
|
||||
<< "you are probably trying to use solidContact boundaries "
|
||||
<< "with orthotropic solver" << nl
|
||||
<< "it should be straight-forward but I have not done it yet!"
|
||||
<< "with the orthotropic solver" << nl
|
||||
<< exit(FatalError);
|
||||
}
|
||||
else
|
||||
{
|
||||
// lookup material properties from the solver
|
||||
const fvPatchField<scalar>& mu =
|
||||
// Lookup material properties from the solver
|
||||
const fvPatchScalarField& mu =
|
||||
patch.lookupPatchField<volScalarField, scalar>("mu");
|
||||
const fvPatchField<scalar>& lambda =
|
||||
|
||||
const fvPatchScalarField& lambda =
|
||||
patch.lookupPatchField<volScalarField, scalar>("lambda");
|
||||
|
||||
// only for nonlinear elastic properties
|
||||
|
@ -93,212 +97,297 @@ tmp<vectorField> tractionBoundaryGradient::traction
|
|||
// lambda = plasticity.newLambda().boundaryField()[patch.index()];
|
||||
// }
|
||||
|
||||
// required fields
|
||||
// Get patch normal
|
||||
vectorField n = patch.nf();
|
||||
|
||||
// Calculate traction
|
||||
traction = 2*mu*(n & symm(gradField)) + lambda*tr(gradField)*n;
|
||||
|
||||
// calculate traction
|
||||
traction = 2*mu*(n&symm(gradField)) + lambda*tr(gradField)*n;
|
||||
|
||||
|
||||
//- if there is plasticity
|
||||
// Plasticity effects
|
||||
const constitutiveModel& rheology =
|
||||
patch.boundaryMesh().mesh().objectRegistry::
|
||||
lookupObject<constitutiveModel>("rheologyProperties");
|
||||
|
||||
if (rheology.plasticityActive())
|
||||
{
|
||||
traction -=
|
||||
2*mu*(n & rheology.DEpsilonP().boundaryField()[patch.index()]);
|
||||
}
|
||||
|
||||
//- if there are thermal effects
|
||||
if (patch.boundaryMesh().mesh().objectRegistry::
|
||||
foundObject<thermalModel>("thermalProperties"))
|
||||
// Thermal effects
|
||||
if
|
||||
(
|
||||
patch.boundaryMesh().mesh().objectRegistry::
|
||||
foundObject<thermalModel>("thermalProperties")
|
||||
)
|
||||
{
|
||||
const thermalModel& thermo =
|
||||
patch.boundaryMesh().mesh().objectRegistry::
|
||||
lookupObject<thermalModel>("thermalProperties");
|
||||
|
||||
const fvPatchField<scalar>& threeKalpha =
|
||||
const fvPatchScalarField& threeKalpha =
|
||||
patch.lookupPatchField<volScalarField, scalar>
|
||||
("((threeK*rho)*alpha)");
|
||||
|
||||
if (fieldName == "DU")
|
||||
// Incremental thermal contribution
|
||||
if (incremental)
|
||||
{
|
||||
const fvPatchField<scalar>& DT =
|
||||
const fvPatchScalarField& DT =
|
||||
patch.lookupPatchField<volScalarField, scalar>("DT");
|
||||
|
||||
traction -= (n*threeKalpha*DT);
|
||||
}
|
||||
else
|
||||
{
|
||||
const fvPatchField<scalar>& T =
|
||||
const fvPatchScalarField& T =
|
||||
patch.lookupPatchField<volScalarField, scalar>("T");
|
||||
|
||||
const scalarField T0 = thermo.T0()().boundaryField()[patch.index()];
|
||||
const scalarField T0 =
|
||||
thermo.T0()().boundaryField()[patch.index()];
|
||||
|
||||
traction -= (n*threeKalpha*(T - T0));
|
||||
}
|
||||
}
|
||||
|
||||
// non linear terms
|
||||
if (nonLinear == "updatedLagrangian" || nonLinear == "totalLagrangian")
|
||||
// Non-linear terms
|
||||
if
|
||||
(
|
||||
nonLinear == nonLinearGeometry::UPDATED_LAGRANGIAN
|
||||
|| nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
traction +=
|
||||
(n & (mu*(gradField & gradField.T())))
|
||||
+ 0.5*n*lambda*(gradField && gradField);
|
||||
|
||||
if (fieldName == "DU" && nonLinear == "totalLagrangian")
|
||||
if
|
||||
(
|
||||
incremental
|
||||
&& nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
// incremental total Lagrangian
|
||||
const fvPatchField<tensor>& gradU =
|
||||
patch.lookupPatchField<volTensorField, tensor>("grad(U)");
|
||||
// Incremental total Lagrangian
|
||||
|
||||
const fvPatchTensorField& gradU =
|
||||
patch.lookupPatchField<volTensorField, tensor>
|
||||
(
|
||||
"grad(" + integralFieldName + ")"
|
||||
);
|
||||
|
||||
traction +=
|
||||
(n & (mu*( (gradU & gradField.T()) + (gradField & gradU.T()))))
|
||||
+ 0.5*n*lambda*tr((gradU & gradField.T()) + (gradField & gradU.T()));
|
||||
(
|
||||
n
|
||||
& (
|
||||
mu*
|
||||
(
|
||||
(gradU & gradField.T())
|
||||
+ (gradField & gradU.T())
|
||||
)
|
||||
)
|
||||
)
|
||||
+ 0.5*n*lambda*tr
|
||||
(
|
||||
(gradU & gradField.T())
|
||||
+ (gradField & gradU.T())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//- add old stress for incremental approaches
|
||||
if (fieldName == "DU")
|
||||
// Add old stress for incremental approaches
|
||||
if (incremental)
|
||||
{
|
||||
// add on old traction
|
||||
const fvPatchField<symmTensor>& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>("sigma");
|
||||
const fvPatchSymmTensorField& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>
|
||||
(
|
||||
"sigma"
|
||||
);
|
||||
|
||||
traction += (n & sigma);
|
||||
}
|
||||
|
||||
//- visco-elastic
|
||||
// Visco-elastic effects
|
||||
if (rheology.viscoActive())
|
||||
{
|
||||
const fvPatchField<symmTensor>& DSigmaCorr =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>("DSigmaCorr");
|
||||
const fvPatchSymmTensorField& DSigmaCorr =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>
|
||||
(
|
||||
"DSigmaCorr"
|
||||
);
|
||||
|
||||
traction += (n & DSigmaCorr);
|
||||
}
|
||||
|
||||
//- updated Lagrangian or total Lagrangian large strain
|
||||
if (nonLinear == "updatedLagrangian" || nonLinear == "totalLagrangian")
|
||||
// Updated Lagrangian or total Lagrangian large strain
|
||||
if
|
||||
(
|
||||
nonLinear == nonLinearGeometry::UPDATED_LAGRANGIAN
|
||||
|| nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
tensorField F = I + gradField;
|
||||
if (fieldName == "DU" && nonLinear == "totalLagrangian")
|
||||
|
||||
if
|
||||
(
|
||||
incremental
|
||||
&& nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
const fvPatchField<tensor>& gradU =
|
||||
patch.lookupPatchField<volTensorField, tensor>("grad(U)");
|
||||
// Incremental total Lagrangian
|
||||
|
||||
const fvPatchTensorField& gradU =
|
||||
patch.lookupPatchField<volTensorField, tensor>
|
||||
(
|
||||
"grad(" + integralFieldName + ")"
|
||||
);
|
||||
|
||||
F += gradU;
|
||||
}
|
||||
|
||||
tensorField Finv = inv(F);
|
||||
scalarField J = det(F);
|
||||
|
||||
//- rotate second Piola Kirchhoff traction to be Cauchy traction
|
||||
// Rotate second Piola Kirchhoff traction to be Cauchy traction
|
||||
traction = (traction & F)/(mag(J * Finv & n));
|
||||
}
|
||||
}
|
||||
|
||||
return ttraction;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Operators * * * * * * * * * * * * * * //
|
||||
tmp<vectorField> tractionBoundaryGradient::operator()
|
||||
(
|
||||
|
||||
Foam::tmp<Foam::vectorField> Foam::tractionBoundaryGradient::snGrad
|
||||
(
|
||||
const vectorField& traction,
|
||||
const scalarField& pressure,
|
||||
const word fieldName,
|
||||
const word& workingFieldName,
|
||||
const word& integralFieldName,
|
||||
const fvPatch& patch,
|
||||
Switch orthotropic,
|
||||
word nonLinear
|
||||
) const
|
||||
{
|
||||
// create tmp
|
||||
const bool orthotropic,
|
||||
const nonLinearGeometry::nonLinearType& nonLinear,
|
||||
const bool incremental
|
||||
)
|
||||
{
|
||||
// Create result
|
||||
tmp<vectorField> tgradient(new vectorField(traction.size(), vector::zero));
|
||||
vectorField& gradient = tgradient();
|
||||
|
||||
// lookup switches
|
||||
|
||||
// orthotropic solvers
|
||||
// Orthotropic material
|
||||
if (orthotropic)
|
||||
{
|
||||
// mechanical properties
|
||||
// Get mechanical properties
|
||||
const constitutiveModel& rheology =
|
||||
patch.boundaryMesh().mesh().objectRegistry::
|
||||
lookupObject<constitutiveModel>("rheologyProperties");
|
||||
const diagTensorField K = rheology.K()().boundaryField()[patch.index()];
|
||||
|
||||
const diagTensorField K =
|
||||
rheology.K()().boundaryField()[patch.index()];
|
||||
|
||||
const symmTensor4thOrderField C =
|
||||
rheology.C()().boundaryField()[patch.index()];
|
||||
|
||||
// required fields
|
||||
// Required fields
|
||||
vectorField n = patch.nf();
|
||||
const diagTensorField Kinv = inv(K);
|
||||
const fvPatchField<tensor>& gradField =
|
||||
patch.lookupPatchField<volTensorField, tensor>("grad(" + fieldName + ")");
|
||||
const fvPatchTensorField& gradField =
|
||||
patch.lookupPatchField<volTensorField, tensor>
|
||||
(
|
||||
"grad(" + workingFieldName + ")"
|
||||
);
|
||||
|
||||
|
||||
// calculate the traction to apply
|
||||
// Calculate the traction to apply
|
||||
vectorField Traction(n.size(), vector::zero);
|
||||
tensorField sigmaExp(n.size(), tensor::zero);
|
||||
|
||||
//- total Lagrangian small strain
|
||||
if (fieldName == "U" && nonLinear == "off")
|
||||
// Total Lagrangian, small strain
|
||||
if
|
||||
(
|
||||
!incremental
|
||||
&& nonLinear == nonLinearGeometry::OFF
|
||||
)
|
||||
{
|
||||
//- total traction
|
||||
// Use total traction
|
||||
Traction = (traction - n*pressure);
|
||||
|
||||
sigmaExp = (n*(n&(C && symm(gradField)))) - (K & gradField);
|
||||
}
|
||||
//- incremental total Lagrangian small strain
|
||||
else if (fieldName == "DU" && nonLinear == "off")
|
||||
// Incremental total Lagrangian small strain
|
||||
else if
|
||||
(
|
||||
incremental
|
||||
&& nonLinear == nonLinearGeometry::OFF
|
||||
)
|
||||
{
|
||||
const fvPatchField<symmTensor>& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>("sigma");
|
||||
const fvPatchSymmTensorField& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>
|
||||
(
|
||||
"sigma"
|
||||
);
|
||||
|
||||
//- increment of traction
|
||||
// Increment of traction
|
||||
Traction = (traction - n*pressure) - (n & sigma);
|
||||
|
||||
sigmaExp = (n*(n&(C && symm(gradField)))) - (K & gradField);
|
||||
}
|
||||
//- updated Lagrangian large strain
|
||||
else if (nonLinear == "updatedLagrangian")
|
||||
// Updated Lagrangian large strain
|
||||
else if
|
||||
(
|
||||
nonLinear == nonLinearGeometry::UPDATED_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
const fvPatchField<symmTensor>& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>("sigma");
|
||||
const fvPatchSymmTensorField& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>
|
||||
(
|
||||
"sigma"
|
||||
);
|
||||
|
||||
tensorField F = I + gradField;
|
||||
tensorField Finv = inv(F);
|
||||
scalarField J = det(F);
|
||||
vectorField nCurrent = Finv & n;
|
||||
nCurrent /= mag(nCurrent);
|
||||
nCurrent /= mag(nCurrent) + SMALL;
|
||||
vectorField tractionCauchy = traction - nCurrent*pressure;
|
||||
|
||||
//- increment of 2nd Piola-Kirchhoff traction
|
||||
Traction = (mag(J * Finv & n) * tractionCauchy & Finv) - (n & sigma);
|
||||
// Increment of 2nd Piola-Kirchhoff traction
|
||||
Traction = mag(J*(Finv & n))*(tractionCauchy & Finv) - (n & sigma);
|
||||
|
||||
sigmaExp = (n*(n&(C && symm(gradField)))) - (K & gradField);
|
||||
sigmaExp = n*(n &(C && symm(gradField))) - (K & gradField);
|
||||
}
|
||||
else
|
||||
{
|
||||
FatalError
|
||||
<< "solidTractionOrtho boundary condition not suitable for "
|
||||
<< " field " << fieldName << " and " << nonLinear
|
||||
FatalErrorIn
|
||||
(
|
||||
"tmp<vectorField> tractionBoundaryGradient::snGrad\n"
|
||||
"(\n"
|
||||
" const vectorField& traction,\n"
|
||||
" const scalarField& pressure,\n"
|
||||
" const word& workingFieldName,\n"
|
||||
" const word& integralFieldName,\n"
|
||||
" const fvPatch& patch,\n"
|
||||
" const bool orthotropic,\n"
|
||||
" const nonLinearGeometry::nonLinearType& nonLinear,\n"
|
||||
" const bool incremental\n"
|
||||
") const"
|
||||
) << "solidTractionOrtho boundary condition not suitable for "
|
||||
<< " field " << workingFieldName << " and "
|
||||
<< nonLinearGeometry::nonLinearNames_[nonLinear]
|
||||
<< abort(FatalError);
|
||||
}
|
||||
|
||||
gradient =
|
||||
n &
|
||||
(
|
||||
Kinv & ( n*(Traction) - sigmaExp )
|
||||
);
|
||||
gradient = n & (Kinv & ( n*(Traction) - sigmaExp ));
|
||||
}
|
||||
|
||||
// standard isotropic solvers
|
||||
else
|
||||
{
|
||||
// lookup material properties from the solver
|
||||
const fvPatchField<scalar>& mu =
|
||||
// Standard isotropic solvers
|
||||
|
||||
// Lookup material properties from the solver
|
||||
const fvPatchScalarField& mu =
|
||||
patch.lookupPatchField<volScalarField, scalar>("mu");
|
||||
const fvPatchField<scalar>& lambda =
|
||||
|
||||
const fvPatchScalarField& lambda =
|
||||
patch.lookupPatchField<volScalarField, scalar>("lambda");
|
||||
|
||||
// only for nonlinear elastic properties
|
||||
|
@ -311,153 +400,215 @@ tmp<vectorField> tractionBoundaryGradient::traction
|
|||
// lambda = plasticity.newLambda().boundaryField()[patch.index()];
|
||||
// }
|
||||
|
||||
|
||||
// required fields
|
||||
vectorField n = patch.nf();
|
||||
|
||||
// gradient of the field
|
||||
const fvPatchField<tensor>& gradField =
|
||||
patch.lookupPatchField<volTensorField, tensor>("grad(" + fieldName + ")");
|
||||
const fvPatchTensorField& gradField =
|
||||
patch.lookupPatchField<volTensorField, tensor>
|
||||
(
|
||||
"grad(" + workingFieldName + ")"
|
||||
);
|
||||
|
||||
|
||||
//---------------------------//
|
||||
//- calculate the actual traction to apply
|
||||
//---------------------------//
|
||||
vectorField Traction(n.size(),vector::zero);
|
||||
vectorField Traction(n.size(), vector::zero);
|
||||
|
||||
//- total Lagrangian small strain
|
||||
if (fieldName == "U" && nonLinear == "off")
|
||||
// Total Lagrangian, small strain
|
||||
if
|
||||
(
|
||||
!incremental
|
||||
&& nonLinear == nonLinearGeometry::OFF
|
||||
)
|
||||
{
|
||||
//- total traction
|
||||
// Use total traction
|
||||
Traction = (traction - n*pressure);
|
||||
}
|
||||
//- incremental total Lagrangian small strain
|
||||
else if (fieldName == "DU" && nonLinear == "off")
|
||||
// Incremental total Lagrangian small strain
|
||||
else if
|
||||
(
|
||||
incremental
|
||||
&& nonLinear == nonLinearGeometry::OFF
|
||||
)
|
||||
{
|
||||
const fvPatchField<symmTensor>& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>("sigma");
|
||||
const fvPatchSymmTensorField& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>
|
||||
(
|
||||
"sigma"
|
||||
);
|
||||
|
||||
//- increment of traction
|
||||
// Increment of traction
|
||||
Traction = (traction - n*pressure) - (n & sigma);
|
||||
}
|
||||
//- updated Lagrangian or total Lagrangian large strain
|
||||
else if (nonLinear == "updatedLagrangian" || nonLinear == "totalLagrangian")
|
||||
else if
|
||||
(
|
||||
nonLinear == nonLinearGeometry::UPDATED_LAGRANGIAN
|
||||
|| nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
const fvPatchField<symmTensor>& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>("sigma");
|
||||
const fvPatchSymmTensorField& sigma =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>
|
||||
(
|
||||
"sigma"
|
||||
);
|
||||
|
||||
tensorField F = I + gradField;
|
||||
if (fieldName == "DU" && nonLinear == "totalLagrangian") // for incr TL
|
||||
|
||||
if
|
||||
(
|
||||
incremental
|
||||
&& nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
const fvPatchField<tensor>& gradU =
|
||||
patch.lookupPatchField<volTensorField, tensor>("grad(U)");
|
||||
// Incremental total Lagrangian
|
||||
|
||||
const fvPatchTensorField& gradU =
|
||||
patch.lookupPatchField<volTensorField, tensor>
|
||||
(
|
||||
"grad(" + integralFieldName + ")"
|
||||
);
|
||||
|
||||
F += gradU;
|
||||
}
|
||||
|
||||
tensorField Finv = hinv(F);
|
||||
scalarField J = det(F);
|
||||
vectorField nCurrent = Finv & n;
|
||||
nCurrent /= mag(nCurrent);
|
||||
nCurrent /= mag(nCurrent) + SMALL;
|
||||
vectorField tractionCauchy = traction - nCurrent*pressure;
|
||||
|
||||
Traction = (mag(J * Finv & n) * tractionCauchy & Finv);
|
||||
Traction = mag(J*(Finv & n))*(tractionCauchy & Finv);
|
||||
|
||||
if (fieldName == "DU")
|
||||
if (incremental)
|
||||
{
|
||||
//- increment of 2nd Piola-Kirchhoff traction
|
||||
// Increment of 2nd Piola-Kirchhoff traction
|
||||
Traction -= (n & sigma);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FatalError
|
||||
<< "Field " << fieldName << " and " << nonLinear
|
||||
FatalErrorIn
|
||||
(
|
||||
"tmp<vectorField> tractionBoundaryGradient::snGrad\n"
|
||||
"(\n"
|
||||
" const vectorField& traction,\n"
|
||||
" const scalarField& pressure,\n"
|
||||
" const word& workingFieldName,\n"
|
||||
" const word& integralFieldName,\n"
|
||||
" const fvPatch& patch,\n"
|
||||
" const bool orthotropic,\n"
|
||||
" const nonLinearGeometry::nonLinearType& nonLinear,\n"
|
||||
" const bool incremental\n"
|
||||
") const"
|
||||
) << " field " << workingFieldName << " and "
|
||||
<< nonLinearGeometry::nonLinearNames_[nonLinear]
|
||||
<< " nonLinear are not compatible!"
|
||||
<< exit(FatalError);
|
||||
<< abort(FatalError);
|
||||
}
|
||||
|
||||
//- visco-elastic
|
||||
const constitutiveModel& rheology =
|
||||
patch.boundaryMesh().mesh().objectRegistry::
|
||||
lookupObject<constitutiveModel>("rheologyProperties");
|
||||
|
||||
if (rheology.viscoActive())
|
||||
{
|
||||
//Info << "visco active" << endl;
|
||||
const fvPatchField<symmTensor>& DSigmaCorr =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>("DSigmaCorr");
|
||||
const fvPatchSymmTensorField& DSigmaCorr =
|
||||
patch.lookupPatchField<volSymmTensorField, symmTensor>
|
||||
(
|
||||
"DSigmaCorr"
|
||||
);
|
||||
|
||||
Traction -= (n & DSigmaCorr);
|
||||
}
|
||||
|
||||
//---------------------------//
|
||||
//- calculate the normal gradient based on the traction
|
||||
//---------------------------//
|
||||
// Calculate the normal gradient based on the traction
|
||||
|
||||
gradient =
|
||||
Traction
|
||||
- (n & (mu*gradField.T() - (mu + lambda)*gradField))
|
||||
- n*lambda*tr(gradField);
|
||||
|
||||
//- if there is plasticity
|
||||
//- Plasticity contribution
|
||||
if (rheology.plasticityActive())
|
||||
{
|
||||
//Info << "plasticity is active" << endl;
|
||||
gradient +=
|
||||
2*mu*(n & rheology.DEpsilonP().boundaryField()[patch.index()]);
|
||||
}
|
||||
|
||||
//- if there are thermal effects
|
||||
if (patch.boundaryMesh().mesh().objectRegistry::
|
||||
foundObject<thermalModel>("thermalProperties"))
|
||||
// Thermal effects
|
||||
if
|
||||
(
|
||||
patch.boundaryMesh().mesh().objectRegistry::
|
||||
foundObject<thermalModel>("thermalProperties")
|
||||
)
|
||||
{
|
||||
const thermalModel& thermo =
|
||||
patch.boundaryMesh().mesh().objectRegistry::
|
||||
lookupObject<thermalModel>("thermalProperties");
|
||||
|
||||
const fvPatchField<scalar>& threeKalpha =
|
||||
const fvPatchScalarField& threeKalpha =
|
||||
patch.lookupPatchField<volScalarField, scalar>
|
||||
("((threeK*rho)*alpha)");
|
||||
(
|
||||
"((threeK*rho)*alpha)"
|
||||
);
|
||||
|
||||
if (fieldName == "DU")
|
||||
if (!incremental)
|
||||
{
|
||||
const fvPatchField<scalar>& DT =
|
||||
const fvPatchScalarField& DT =
|
||||
patch.lookupPatchField<volScalarField, scalar>("DT");
|
||||
|
||||
gradient += (n*threeKalpha*DT);
|
||||
gradient += n*threeKalpha*DT;
|
||||
}
|
||||
else
|
||||
{
|
||||
const fvPatchField<scalar>& T =
|
||||
const fvPatchScalarField& T =
|
||||
patch.lookupPatchField<volScalarField, scalar>("T");
|
||||
|
||||
const scalarField T0 = thermo.T0()().boundaryField()[patch.index()];
|
||||
const scalarField T0 =
|
||||
thermo.T0()().boundaryField()[patch.index()];
|
||||
|
||||
gradient += (n*threeKalpha*(T - T0));
|
||||
gradient += n*threeKalpha*(T - T0);
|
||||
}
|
||||
}
|
||||
|
||||
//- higher order non-linear terms
|
||||
if (nonLinear == "updatedLagrangian" || nonLinear == "totalLagrangian")
|
||||
// Higher order non-linear terms
|
||||
if
|
||||
(
|
||||
nonLinear == nonLinearGeometry::UPDATED_LAGRANGIAN
|
||||
|| nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
// no extra relaxation
|
||||
gradient -=
|
||||
(n & (mu*(gradField & gradField.T())))
|
||||
// + 0.5*n*lambda*(gradField && gradField);
|
||||
+ 0.5*n*lambda*tr(gradField & gradField.T());
|
||||
//- tensorial identity
|
||||
//- tr(gradField & gradField.T())*I == (gradField && gradField)*I
|
||||
|
||||
if (fieldName == "DU" && nonLinear == "totalLagrangian")
|
||||
if
|
||||
(
|
||||
incremental
|
||||
&& nonLinear == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
// gradU is const in a time step
|
||||
const fvPatchField<tensor>& gradU =
|
||||
const fvPatchTensorField& gradU =
|
||||
patch.lookupPatchField<volTensorField, tensor>("grad(U)");
|
||||
|
||||
gradient -=
|
||||
(n &
|
||||
(mu*( (gradField & gradU.T())
|
||||
+ (gradU & gradField.T()) ))
|
||||
(
|
||||
n &
|
||||
(
|
||||
mu*
|
||||
(
|
||||
(gradField & gradU.T())
|
||||
+ (gradU & gradField.T())
|
||||
)
|
||||
+ 0.5*n*lambda*tr( (gradField & gradU.T())
|
||||
+ (gradU & gradField.T()) );
|
||||
)
|
||||
)
|
||||
+ 0.5*n*lambda*tr
|
||||
(
|
||||
(gradField & gradU.T())
|
||||
+ (gradU & gradField.T())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,8 +617,6 @@ tmp<vectorField> tractionBoundaryGradient::traction
|
|||
|
||||
return tgradient;
|
||||
}
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// ************************************************************************* //
|
||||
|
|
|
@ -35,6 +35,7 @@ SourceFiles
|
|||
|
||||
Author
|
||||
Philip Cardiff UCD
|
||||
Clean-up and re-factoring Hrvoje Jasak
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -47,6 +48,7 @@ Author
|
|||
#include "volFields.H"
|
||||
#include "tmp.H"
|
||||
#include "rheologyLaw.H"
|
||||
#include "nonLinearGeometry.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
|
@ -59,49 +61,38 @@ namespace Foam
|
|||
|
||||
class tractionBoundaryGradient
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
//- Runtime type information
|
||||
TypeName("tractionBoundaryGradient");
|
||||
|
||||
// Constructors
|
||||
|
||||
tractionBoundaryGradient()
|
||||
{}
|
||||
|
||||
|
||||
// Destructor
|
||||
|
||||
virtual ~tractionBoundaryGradient()
|
||||
{}
|
||||
|
||||
|
||||
// Member functions
|
||||
// Static member functions
|
||||
|
||||
//- Return the boundary Cauchy traction corresponding to
|
||||
// the given gradient
|
||||
tmp<vectorField> traction
|
||||
static tmp<vectorField> traction
|
||||
(
|
||||
const tensorField& gradField,
|
||||
const word fieldName,
|
||||
const word& workingFieldName, // Working variable
|
||||
const word& integralFieldName, // Integrated displacement
|
||||
const fvPatch& patch,
|
||||
Switch orthotropic,
|
||||
word nonLinear
|
||||
) const;
|
||||
const bool orthotropic,
|
||||
const nonLinearGeometry::nonLinearType& nonLinear,
|
||||
const bool incremental
|
||||
);
|
||||
|
||||
|
||||
// Operators
|
||||
|
||||
tmp<vectorField> operator()
|
||||
//- Return surface-normal gradient given traction and pressure
|
||||
static tmp<vectorField> snGrad
|
||||
(
|
||||
const vectorField& traction,
|
||||
const scalarField& pressure,
|
||||
const word fieldName,
|
||||
const word& workingFieldName, // Working variable
|
||||
const word& integralFieldName, // Integrated displacement
|
||||
const fvPatch& patch,
|
||||
Switch orthotropic,
|
||||
word nonLinear
|
||||
) const;
|
||||
const bool orthotropic,
|
||||
const nonLinearGeometry::nonLinearType& nonLinear,
|
||||
const bool incremental
|
||||
);
|
||||
};
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
|
|
@ -269,17 +269,23 @@ Foam::dirichletNeumannFriction::dirichletNeumannFriction
|
|||
const fvPatch& slavePatch = mesh.boundary()[slavePatchIndex];
|
||||
const fvPatchField<tensor>& gradField =
|
||||
slavePatch.lookupPatchField<volTensorField, tensor>
|
||||
("grad("+fieldName+")");
|
||||
vectorField slaveShearTraction =
|
||||
("grad(" + fieldName + ")");
|
||||
|
||||
bool incremental(fieldName == "DU");
|
||||
|
||||
vectorField slaveShearTraction
|
||||
(
|
||||
(I - sqr(slaveFaceNormals))
|
||||
&
|
||||
tractionBoundaryGradient().traction
|
||||
& tractionBoundaryGradient::traction
|
||||
(
|
||||
gradField,
|
||||
fieldName,
|
||||
"U",
|
||||
slavePatch,
|
||||
orthotropic,
|
||||
nonLinear
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear],
|
||||
incremental
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -149,10 +149,10 @@ dirichletNeumann::dirichletNeumann
|
|||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * * //
|
||||
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
|
||||
|
||||
void dirichletNeumann::correct
|
||||
(
|
||||
void dirichletNeumann::correct
|
||||
(
|
||||
const PrimitivePatch<face, List, pointField>& masterFaceZonePatch,
|
||||
const PrimitivePatch<face, List, pointField>& slaveFaceZonePatch,
|
||||
const intersection::algorithm alg,
|
||||
|
@ -161,11 +161,13 @@ dirichletNeumann::dirichletNeumann
|
|||
const Switch orthotropic,
|
||||
const word nonLinear,
|
||||
vectorField& slaveFaceNormals,
|
||||
GGIInterpolation< PrimitivePatch< face, List, pointField >,
|
||||
GGIInterpolation
|
||||
<
|
||||
PrimitivePatch< face, List, pointField >,
|
||||
PrimitivePatch< face, List, pointField >
|
||||
>* ggiInterpolatorPtr
|
||||
)
|
||||
{
|
||||
)
|
||||
{
|
||||
if (!settleContact_ || (iCorr_ < settleIterationNumber_))
|
||||
{
|
||||
|
||||
|
@ -209,8 +211,8 @@ dirichletNeumann::dirichletNeumann
|
|||
// pointDistanceToIntersection() sometimes gives a seg fault when the
|
||||
// momentum equation diverges
|
||||
|
||||
globalSlavePointPenetration
|
||||
= masterToSlavePatchToPatchInterpolator.pointDistanceToIntersection();
|
||||
globalSlavePointPenetration =
|
||||
masterToSlavePatchToPatchInterpolator.pointDistanceToIntersection();
|
||||
|
||||
// get local point values from global values
|
||||
forAll(slavePointPenetration, pointI)
|
||||
|
@ -218,10 +220,7 @@ dirichletNeumann::dirichletNeumann
|
|||
// the local point values seem to be kept at the start
|
||||
// of the global field
|
||||
slavePointPenetration[pointI] =
|
||||
globalSlavePointPenetration
|
||||
[
|
||||
pointI
|
||||
];
|
||||
globalSlavePointPenetration[pointI];
|
||||
|
||||
//- when the master surface surrounds the slave (like the pelvis and
|
||||
// femur head) then
|
||||
|
@ -505,8 +504,9 @@ dirichletNeumann::dirichletNeumann
|
|||
{
|
||||
slaveDispPoints =
|
||||
localSlaveInterpolator.faceToPointInterpolate<vector>(slaveDisp_);
|
||||
slaveDisp_ =
|
||||
localSlaveInterpolator.pointToFaceInterpolate<vector>(slaveDispPoints);
|
||||
|
||||
slaveDisp_ = localSlaveInterpolator.pointToFaceInterpolate<vector>
|
||||
(slaveDispPoints);
|
||||
|
||||
// make sure no tangential component
|
||||
slaveDisp_ = slaveFaceNormals*(slaveFaceNormals & slaveDisp_);
|
||||
|
@ -533,14 +533,21 @@ dirichletNeumann::dirichletNeumann
|
|||
const fvPatch& slavePatch = mesh.boundary()[slavePatchIndex];
|
||||
const fvPatchField<tensor>& gradField =
|
||||
slavePatch.lookupPatchField<volTensorField, tensor>
|
||||
("grad("+fieldName+")");
|
||||
slavePressure_ = tractionBoundaryGradient().traction
|
||||
(
|
||||
"grad(" + fieldName + ")"
|
||||
);
|
||||
|
||||
bool incremental(fieldName == "DU");
|
||||
|
||||
slavePressure_ = tractionBoundaryGradient::traction
|
||||
(
|
||||
gradField,
|
||||
fieldName,
|
||||
"U",
|
||||
slavePatch,
|
||||
orthotropic,
|
||||
nonLinear
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear],
|
||||
incremental
|
||||
);
|
||||
|
||||
// set traction to zero on faces not in contact
|
||||
|
@ -555,16 +562,18 @@ dirichletNeumann::dirichletNeumann
|
|||
|
||||
forAll(touchFraction_, facei)
|
||||
{
|
||||
if (touchFraction_[facei] < SMALL
|
||||
||
|
||||
( (slaveFaceNormals[facei] & slavePressure_[facei])
|
||||
> 1e-3*maxMagSlavePressure)
|
||||
if
|
||||
(
|
||||
touchFraction_[facei] < SMALL
|
||||
|| (
|
||||
(slaveFaceNormals[facei] & slavePressure_[facei])
|
||||
> 1e-3*maxMagSlavePressure
|
||||
)
|
||||
)
|
||||
{
|
||||
slavePressure_[facei] = vector::zero;
|
||||
slaveValueFrac_[facei] = symmTensor::zero;
|
||||
slaveDisp_[facei] =
|
||||
slaveFaceNormals[facei]
|
||||
slaveDisp_[facei] = slaveFaceNormals[facei]
|
||||
*(slaveFaceNormals[facei]&oldSlaveDisp[facei]);
|
||||
}
|
||||
}
|
||||
|
@ -581,9 +590,14 @@ dirichletNeumann::dirichletNeumann
|
|||
{
|
||||
forAll(slavePressure_, facei)
|
||||
{
|
||||
if ( (slaveFaceNormals[facei]&slavePressure_[facei]) < -pressureLimit_)
|
||||
if
|
||||
(
|
||||
(slaveFaceNormals[facei] & slavePressure_[facei])
|
||||
< -pressureLimit_
|
||||
)
|
||||
{
|
||||
slavePressure_[facei] = -pressureLimit_*slaveFaceNormals[facei];
|
||||
slavePressure_[facei] =
|
||||
-pressureLimit_*slaveFaceNormals[facei];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,15 +206,16 @@ void fixedDisplacementOrSolidTractionFvPatchVectorField::updateCoeffs()
|
|||
this->valueFraction() = symmTensor::zero;
|
||||
|
||||
// set gradient to enfore specified traction
|
||||
refGrad() = tractionBoundaryGradient()
|
||||
refGrad() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
traction_,
|
||||
pressure_,
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -144,6 +144,7 @@ fixedDisplacementZeroShearFvPatchVectorField
|
|||
FatalError << "value entry not found for patch " << patch().name()
|
||||
<< exit(FatalError);
|
||||
}
|
||||
|
||||
this->refValue() = *this;
|
||||
|
||||
Field<vector> normalValue = transform(valueFraction(), refValue());
|
||||
|
@ -223,15 +224,19 @@ void fixedDisplacementZeroShearFvPatchVectorField::updateCoeffs()
|
|||
// this->valueFraction() = sqr(nCurrent);
|
||||
// }
|
||||
|
||||
refGrad() = tractionBoundaryGradient()
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
refGrad() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
vectorField(patch().size(), vector::zero),
|
||||
scalarField(patch().size(), 0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
|
||||
directionMixedFvPatchVectorField::updateCoeffs();
|
||||
}
|
||||
|
|
|
@ -201,14 +201,15 @@ void fixedDisplacementZeroShearOrSolidTractionFvPatchVectorField::rmap
|
|||
}
|
||||
|
||||
|
||||
void fixedDisplacementZeroShearOrSolidTractionFvPatchVectorField::updateCoeffs()
|
||||
void
|
||||
fixedDisplacementZeroShearOrSolidTractionFvPatchVectorField::updateCoeffs()
|
||||
{
|
||||
if (this->updated())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mag(timeSeries_(this->db().time().timeOutputValue())) < SMALL)
|
||||
if (mag(timeSeries_(this->db().time().timeOutputValue())) < SMALL)
|
||||
{
|
||||
// traction boundary
|
||||
|
||||
|
@ -216,15 +217,16 @@ void fixedDisplacementZeroShearOrSolidTractionFvPatchVectorField::updateCoeffs()
|
|||
this->valueFraction() = symmTensor::zero;
|
||||
|
||||
// set gradient to enfore specified traction
|
||||
refGrad() = tractionBoundaryGradient()
|
||||
refGrad() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
traction_,
|
||||
pressure_,
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -234,15 +236,16 @@ void fixedDisplacementZeroShearOrSolidTractionFvPatchVectorField::updateCoeffs()
|
|||
this->valueFraction() = sqr(fixedNormal_);
|
||||
|
||||
// force zero shear stresses
|
||||
refGrad() = tractionBoundaryGradient()
|
||||
refGrad() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
vectorField(traction_.size(), vector::zero),
|
||||
scalarField(traction_.size(), 0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
);
|
||||
|
||||
// set displacement
|
||||
refValue() = displacement_;
|
||||
|
@ -256,7 +259,7 @@ void fixedDisplacementZeroShearOrSolidTractionFvPatchVectorField::updateCoeffs()
|
|||
void fixedDisplacementZeroShearOrSolidTractionFvPatchVectorField::write
|
||||
(
|
||||
Ostream& os
|
||||
) const
|
||||
) const
|
||||
{
|
||||
directionMixedFvPatchVectorField::write(os);
|
||||
os.writeKeyword("nonLinear")
|
||||
|
|
|
@ -767,6 +767,8 @@ void solidContactFvPatchVectorField::updateCoeffs()
|
|||
}
|
||||
|
||||
// set boundary conditions
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
if (!master_)
|
||||
{
|
||||
// set refValue, refGrad and valueFraction
|
||||
|
@ -778,16 +780,18 @@ void solidContactFvPatchVectorField::updateCoeffs()
|
|||
|
||||
//refGrad - set traction
|
||||
refGrad() =
|
||||
tractionBoundaryGradient()
|
||||
tractionBoundaryGradient::snGrad
|
||||
(
|
||||
frictionContactModelPtr_->slaveTraction()
|
||||
+ normalContactModelPtr_->slavePressure(),
|
||||
scalarField(patch().size(),0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
|
||||
//valueFraction
|
||||
valueFraction() =
|
||||
|
@ -805,20 +809,21 @@ void solidContactFvPatchVectorField::updateCoeffs()
|
|||
{
|
||||
// set to master to traction free if it is rigid
|
||||
refGrad() =
|
||||
tractionBoundaryGradient()
|
||||
tractionBoundaryGradient::snGrad
|
||||
(
|
||||
vectorField(patch().size(),vector::zero),
|
||||
scalarField(patch().size(),0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
refGrad() =
|
||||
tractionBoundaryGradient()
|
||||
refGrad() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
interpolateSlaveToMaster
|
||||
(
|
||||
|
@ -826,11 +831,13 @@ void solidContactFvPatchVectorField::updateCoeffs()
|
|||
-normalContactModelPtr_->slavePressure()
|
||||
),
|
||||
scalarField(patch().size(),0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
}
|
||||
}
|
||||
} // if correction freqeuncy
|
||||
|
@ -840,21 +847,26 @@ void solidContactFvPatchVectorField::updateCoeffs()
|
|||
else
|
||||
{
|
||||
// set refGrad to traction free for master and slave
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
refGrad() =
|
||||
tractionBoundaryGradient()
|
||||
tractionBoundaryGradient::snGrad
|
||||
(
|
||||
vectorField(patch().size(),vector::zero),
|
||||
scalarField(patch().size(),0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
}
|
||||
|
||||
directionMixedFvPatchVectorField::updateCoeffs();
|
||||
}
|
||||
|
||||
|
||||
// Interpolate traction from slave to master
|
||||
tmp<vectorField> solidContactFvPatchVectorField::interpolateSlaveToMaster
|
||||
(
|
||||
|
@ -1326,21 +1338,32 @@ tmp<scalarField> solidContactFvPatchVectorField::Qc() const
|
|||
|
||||
// average force
|
||||
const fvPatchField<tensor>& gradField =
|
||||
patch().lookupPatchField<volTensorField, tensor>("grad("+fieldName_+")");
|
||||
patch().lookupPatchField<volTensorField, tensor>
|
||||
(
|
||||
"grad(" + fieldName_ + ")"
|
||||
);
|
||||
|
||||
// current Cauchy traction for nonlinear
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
const vectorField curTrac =
|
||||
tractionBoundaryGradient().traction
|
||||
tractionBoundaryGradient::traction
|
||||
(
|
||||
gradField,
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
word(nonLinearGeometry::nonLinearNames_[nonLinear_])
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
|
||||
vectorField Sf = patch().Sf();
|
||||
if (nonLinear_ != nonLinearGeometry::OFF
|
||||
&& nonLinear_ == nonLinearGeometry::TOTAL_LAGRANGIAN)
|
||||
if
|
||||
(
|
||||
nonLinear_ != nonLinearGeometry::OFF
|
||||
&& nonLinear_ == nonLinearGeometry::TOTAL_LAGRANGIAN
|
||||
)
|
||||
{
|
||||
// current areas
|
||||
const fvPatchField<tensor>& gradU =
|
||||
|
@ -1352,7 +1375,7 @@ tmp<scalarField> solidContactFvPatchVectorField::Qc() const
|
|||
}
|
||||
const scalarField magSf = mag(Sf);
|
||||
|
||||
const vectorField curForce = magSf * curTrac;
|
||||
const vectorField curForce = magSf*curTrac;
|
||||
|
||||
const fvPatchField<symmTensor>& oldSigma =
|
||||
patch().lookupPatchField<volSymmTensorField, symmTensor>("sigma_0");
|
||||
|
|
|
@ -210,15 +210,19 @@ void solidTractionFvPatchVectorField::updateCoeffs()
|
|||
return;
|
||||
}
|
||||
|
||||
gradient() = tractionBoundaryGradient()
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
gradient() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
traction_,
|
||||
pressure_,
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
|
||||
fixedGradientFvPatchVectorField::updateCoeffs();
|
||||
}
|
||||
|
@ -243,14 +247,14 @@ void solidTractionFvPatchVectorField::evaluate(const Pstream::commsTypes)
|
|||
//- non-orthogonal correction vectors
|
||||
vectorField k = delta - n*(n&delta);
|
||||
|
||||
Field<vector>::operator=
|
||||
vectorField::operator=
|
||||
(
|
||||
this->patchInternalField()
|
||||
+ (k&gradField.patchInternalField())
|
||||
+ gradient()/this->patch().deltaCoeffs()
|
||||
);
|
||||
|
||||
fvPatchField<vector>::evaluate();
|
||||
fvPatchVectorField::evaluate();
|
||||
}
|
||||
|
||||
// Write
|
||||
|
|
|
@ -191,15 +191,19 @@ void solidTractionFreeFvPatchVectorField::updateCoeffs()
|
|||
return;
|
||||
}
|
||||
|
||||
gradient() = tractionBoundaryGradient()
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
gradient() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
vectorField(patch().size(), vector::zero),
|
||||
scalarField(patch().size(), 0.0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
)();
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
|
||||
fixedGradientFvPatchVectorField::updateCoeffs();
|
||||
}
|
||||
|
@ -226,7 +230,7 @@ void solidTractionFreeFvPatchVectorField::evaluate(const Pstream::commsTypes)
|
|||
Field<vector>::operator=
|
||||
(
|
||||
this->patchInternalField()
|
||||
+ (k&gradField.patchInternalField())
|
||||
+ (k & gradField.patchInternalField())
|
||||
+ gradient()/this->patch().deltaCoeffs()
|
||||
);
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class solidTractionFreeFvPatchVectorField
|
|||
//- Name of the displacement field
|
||||
const word fieldName_;
|
||||
|
||||
//- if it is a non linear solver
|
||||
//- Is it a non linear solver
|
||||
nonLinearGeometry::nonLinearType nonLinear_;
|
||||
|
||||
//- Is it an orthropic solver
|
||||
|
|
|
@ -233,14 +233,18 @@ void timeVaryingFixedDisplacementZeroShearFvPatchVectorField::updateCoeffs()
|
|||
//vectorField n = patch().nf();
|
||||
//this->valueFraction() = sqr(n);
|
||||
|
||||
refGrad() = tractionBoundaryGradient()
|
||||
bool incremental(fieldName_ == "DU");
|
||||
|
||||
refGrad() = tractionBoundaryGradient::snGrad
|
||||
(
|
||||
vectorField(patch().size(), vector::zero),
|
||||
scalarField(patch().size(), 0),
|
||||
word(fieldName_),
|
||||
fieldName_,
|
||||
"U",
|
||||
patch(),
|
||||
orthotropic_,
|
||||
nonLinearGeometry::nonLinearNames_[nonLinear_]
|
||||
nonLinear_,
|
||||
incremental
|
||||
);
|
||||
|
||||
directionMixedFvPatchVectorField::updateCoeffs();
|
||||
|
|
|
@ -53,7 +53,7 @@ class nonLinearGeometry
|
|||
{
|
||||
public:
|
||||
|
||||
//- non-linear solver options
|
||||
//- Non-linear solver options
|
||||
enum nonLinearType
|
||||
{
|
||||
OFF,
|
||||
|
|
|
@ -27,19 +27,20 @@ boundaryField
|
|||
}
|
||||
right
|
||||
{
|
||||
type analyticalPlateHoleTraction;
|
||||
// type analyticalPlateHoleTraction;
|
||||
type solidTraction;
|
||||
traction uniform (0 0 0);
|
||||
pressure uniform 1e7;
|
||||
}
|
||||
|
||||
down
|
||||
{
|
||||
type symmetryPlane;
|
||||
}
|
||||
|
||||
up
|
||||
{
|
||||
type analyticalPlateHoleTraction;
|
||||
// type analyticalPlateHoleTraction;
|
||||
type solidTractionFree;
|
||||
}
|
||||
|
||||
hole
|
||||
{
|
||||
type solidTractionFree;
|
||||
|
|
Reference in a new issue