Example #1
0
void PolygonLine :: giveGlobalCoordinates(FloatArray &oGlobalCoord, const double &iArcPos) const
{
    double L = computeLength();
    double xSegStart = 0.0, xSegEnd = 0.0;
    double xiSegStart = 0.0, xiSegEnd = 0.0;
    size_t numSeg = mVertices.size() - 1;
    const double xiTol = 1.0e-9;
    if ( iArcPos < xiTol ) {
        oGlobalCoord = mVertices [ 0 ];
        return;
    }

    for ( size_t i = 0; i < numSeg; i++ ) {
        xSegEnd += mVertices [ i ].distance(mVertices [ i + 1 ]);

        xiSegStart = xSegStart / L;
        xiSegEnd        = xSegEnd / L;

        if ( iArcPos > xiSegStart-xiTol && iArcPos < xiSegEnd+xiTol ) {
            // Point is within the segment
            FloatArray p;
            double elXi = ( iArcPos - xiSegStart ) / ( xiSegEnd - xiSegStart );
            p.beScaled( ( 1.0 - elXi ), mVertices [ i ] );
            p.add(elXi, mVertices [ i + 1 ]);
            oGlobalCoord = p;
            return;
        }

    }
}
Example #2
0
void
RankineMatNl :: giveRealStressVector(FloatArray &answer, MatResponseForm form, GaussPoint *gp,
                                     const FloatArray &totalStrain, TimeStep *atTime)
{
    RankineMatNlStatus *nlStatus = ( RankineMatNlStatus * ) this->giveStatus(gp);
    //mj this->initGpForNewStep(gp);

    double tempDam;
    FloatArray tempEffStress, totalStress;
    MaterialMode mode = gp->giveMaterialMode();
    //mj performPlasticityReturn(gp, totalStrain, mode);
    // nonlocal method "computeDamage" performs the plastic return
    // for all Gauss points when it is called for the first time
    // in the iteration
    tempDam = this->computeDamage(gp, atTime);
    nlStatus->giveTempEffectiveStress(tempEffStress);
    answer.beScaled( 1.0 - tempDam, tempEffStress);
    nlStatus->setTempDamage(tempDam);
    nlStatus->letTempStrainVectorBe(totalStrain);
    nlStatus->letTempStressVectorBe(answer);
#ifdef keep_track_of_dissipated_energy
    double gf = sig0 * sig0 / E; // only estimated, but OK for this purpose
    nlStatus->computeWork(gp, mode, gf);
#endif
}
Example #3
0
void PolygonLine :: giveSubPolygon(std :: vector< FloatArray > &oPoints, const double &iXiStart, const double &iXiEnd) const
{
    double L = computeLength();
    double xSegStart = 0.0, xSegEnd = 0.0;
    double xiSegStart = 0.0, xiSegEnd = 0.0;
    size_t numSeg = mVertices.size() - 1;
    const double xiTol = 1.0e-9;
    if ( iXiStart < xiTol ) {
        // Add first point
        oPoints.push_back(mVertices [ 0 ]);
    }

    for ( size_t i = 0; i < numSeg; i++ ) {
        xSegEnd += mVertices [ i ].distance(mVertices [ i + 1 ]);

        xiSegStart = xSegStart / L;
        xiSegEnd        = xSegEnd / L;

        if ( iXiStart > xiSegStart-xiTol && iXiStart < xiSegEnd+xiTol ) {
            // Start point is within the segment
            FloatArray p;
            double elXi = ( iXiStart - xiSegStart ) / ( xiSegEnd - xiSegStart );
            p.beScaled( ( 1.0 - elXi ), mVertices [ i ] );
            p.add(elXi, mVertices [ i + 1 ]);
            oPoints.push_back(p);
        }


        if ( iXiEnd > xiSegStart && iXiEnd < xiSegEnd ) {
            // End point is within the segment
            FloatArray p;
            double elXi = ( iXiEnd - xiSegStart ) / ( xiSegEnd - xiSegStart );
            p.beScaled( ( 1.0 - elXi ), mVertices [ i ] );
            p.add(elXi, mVertices [ i + 1 ]);
            oPoints.push_back(p);
        }

        if ( xiSegEnd > iXiStart && xiSegEnd < iXiEnd + xiTol ) {
            // End point of the segment is within range
            oPoints.push_back(mVertices [ i + 1 ]);
        }

        xSegStart = xSegEnd;
    }
}
void
FEI3dLineLin :: local2global(FloatArray &answer, const FloatArray &lcoords, const FEICellGeometry &cellgeo)
{
    double ksi;
    FloatArray n(2);
    ksi = lcoords.at(1);

    answer.beScaled( ( 1. - ksi ) * 0.5, *cellgeo.giveVertexCoordinates(1) );
    answer.add( ( 1. + ksi ) * 0.5, *cellgeo.giveVertexCoordinates(2) );
}
Example #5
0
void
IsotropicMoistureTransferMaterial :: giveFluxVector(FloatArray &answer, GaussPoint *gp, const FloatArray &grad, const FloatArray &field, TimeStep *tStep)
{
    TransportMaterialStatus *ms = static_cast< TransportMaterialStatus * >( this->giveStatus(gp) );

    ///@todo Shouldn't the permeability typically depend on the primary field and/or its gradient?
    answer.beScaled(-this->givePermeability(gp, tStep), grad);

    ms->setTempField(field);
    ms->setTempGradient(grad);
    ms->setTempFlux(answer);
}
void
NonlinearMassTransferMaterial :: giveFluxVector(FloatArray &answer, GaussPoint *gp, const FloatArray &grad, const FloatArray &field, TimeStep *tStep)
{
    TransportMaterialStatus *ms = static_cast< TransportMaterialStatus * >( this->giveStatus(gp) );

    double gradPNorm = grad.computeNorm();
    answer.beScaled( -(1. + C * pow(gradPNorm, alpha)), grad);

    ms->setTempGradient(grad);
    ms->setTempField(field);
    ms->setTempFlux(answer);
}
Example #7
0
void
ConstantEdgeLoad :: computeValueAt(FloatArray &answer, TimeStep *tStep, const FloatArray &coords, ValueModeType mode)
{
    if ( ( mode != VM_Total ) && ( mode != VM_Incremental ) ) {
        OOFEM_ERROR("mode not supported");
    }

    if ( !isImposed(tStep) ) {
        answer.zero();
    } else {
        double factor = this->giveTimeFunction()->evaluate(tStep, mode);
        answer.beScaled(factor, componentArray);
    }
}
Example #8
0
void
MisesMatNl :: giveRealStressVector(FloatArray &answer, GaussPoint *gp,
                                   const FloatArray &totalStrain, TimeStep *tStep)
{
    MisesMatNlStatus *nlStatus = static_cast< MisesMatNlStatus * >( this->giveStatus(gp) );
    this->initGpForNewStep(gp);

    double tempDam;
    performPlasticityReturn(gp, totalStrain);
    tempDam = this->computeDamage(gp, tStep);
    answer.beScaled(1.0 - tempDam, nlStatus->giveTempEffectiveStress());
    nlStatus->setTempDamage(tempDam);
    nlStatus->letTempStrainVectorBe(totalStrain);
    nlStatus->letTempStressVectorBe(answer);
}
Example #9
0
void PrescribedGradientBCWeak :: giveTractionElArcPos(size_t iElInd, double &oXiStart, double &oXiEnd) const
{
	FloatArray xS, xE;
	giveTractionElCoord(iElInd, xS, xE);

    FloatArray xC;
    xC.beScaled(0.5, xS);
    xC.add(0.5, xE);
    int sideIndex = giveSideIndex(xC);

    const double nodeDistTol = 1.0e-15;
    ArcPosSortFunction3< bool >sortFunc(mLC, mUC, nodeDistTol, sideIndex);

    oXiStart = sortFunc.calcArcPos(xS);
    oXiEnd = sortFunc.calcArcPos(xE);
}
Example #10
0
void
MisesMatNl :: giveRealStressVector(FloatArray &answer, MatResponseForm form, GaussPoint *gp,
                                   const FloatArray &totalStrain, TimeStep *atTime)
{
    MisesMatNlStatus *nlStatus = ( MisesMatNlStatus * ) this->giveStatus(gp);
    this->initGpForNewStep(gp);

    double tempDam;
    FloatArray tempEffStress, totalStress;
    MaterialMode mode = gp->giveMaterialMode();
    performPlasticityReturn(gp, totalStrain, mode);
    tempDam = this->computeDamage(gp, atTime);
    nlStatus->giveTempEffectiveStress(tempEffStress);
    answer.beScaled( 1.0 - tempDam, tempEffStress);
    nlStatus->setTempDamage(tempDam);
    nlStatus->letTempStrainVectorBe(totalStrain);
    nlStatus->letTempStressVectorBe(answer);
}
Example #11
0
void
MisesMatGrad :: giveRealStressVectorGrad(FloatArray &answer1, double &answer2, GaussPoint *gp, const FloatArray &totalStrain, double nonlocalCumulatedStrain, TimeStep *tStep)
{
    MisesMatGradStatus *status = static_cast< MisesMatGradStatus * >( this->giveStatus(gp) );
    //this->initGpForNewStep(gp);
    this->initTempStatus(gp);

    double tempDamage;

    MisesMat :: performPlasticityReturn(gp, totalStrain);
    status->letTempStrainVectorBe(totalStrain);
    tempDamage = computeDamage(gp, tStep);
    const FloatArray &tempEffStress = status->giveTempEffectiveStress();
    answer1.beScaled(1.0 - tempDamage, tempEffStress);
    answer2 = status->giveTempCumulativePlasticStrain();

    status->setNonlocalCumulatedStrain(nonlocalCumulatedStrain);
    status->setTempDamage(tempDamage);
    status->letTempEffectiveStressBe(tempEffStress);
    status->letTempStressVectorBe(answer1);
}
void
SimpleInterfaceMaterial :: giveRealStressVector(FloatArray &answer, GaussPoint *gp,
                                                //const FloatArray &totalStrain,// @todo temporary -should not be here /JB
                                                const FloatArray &strainVector,
                                                TimeStep *tStep)
//
// returns real stress vector in 3d stress space of receiver according to
// previous level of stress and current
// strain increment, the only way, how to correctly update gp records
//
{
    SimpleInterfaceMaterialStatus *status = static_cast< SimpleInterfaceMaterialStatus * >( this->giveStatus(gp) );
    //this->initGpForNewStep(gp);
    this->initTempStatus(gp);
    FloatArray shearStrain(2), shearStress; //, strainVector;
    StructuralElement *el = static_cast< StructuralElement * >( gp->giveElement() );
    //el->computeStrainVector(strainVector, gp, tStep);

    FloatArray tempShearStressShift = status->giveTempShearStressShift();
    const double normalStrain = strainVector.at(1);
    double normalStress, maxShearStress, dp;
    double shift = -this->kn * this->stiffCoeff * normalClearance;

    MaterialMode mMode = el->giveMaterialMode();
    //answer.resize(giveSizeOfReducedStressStrainVector(mMode));
    answer.zero();
    if ( normalStrain + normalClearance <= 0. ) {
        normalStress = this->kn * ( normalStrain + normalClearance ) + shift; //in compression and after the clearance gap closed
        maxShearStress = fabs(normalStress) * this->frictCoeff;
    } else {
        normalStress = this->kn * this->stiffCoeff * ( normalStrain + normalClearance ) + shift;
        maxShearStress = 0.;
    }

    switch ( mMode ) {
    case _1dInterface:
        answer.resize(1);
        break;
    case _2dInterface:
        answer.resize(2);
        shearStrain.at(1) = strainVector.at(2);
        shearStress.beScaled(this->kn, shearStrain);
        shearStress.subtract(tempShearStressShift);
        dp = shearStress.dotProduct(shearStress, 1);
        if ( dp > maxShearStress * maxShearStress ) {
            shearStress.times( maxShearStress / sqrt(dp) );
        }

        tempShearStressShift.beScaled(this->kn, shearStrain);
        tempShearStressShift.subtract(shearStress);
        answer.at(2) = shearStress.at(1);
        break;
    case _3dInterface:
    case _3dMat: //JB
        answer.resize(3);
        shearStrain.at(1) = strainVector.at(2);
        shearStrain.at(2) = strainVector.at(3);
        shearStress.beScaled(this->kn, shearStrain);
        shearStress.subtract(tempShearStressShift);
        dp = shearStress.dotProduct(shearStress, 2);
        if ( dp > maxShearStress * maxShearStress ) {
            shearStress.times( maxShearStress / sqrt(dp) );
        }

        tempShearStressShift.beScaled(this->kn, shearStrain);
        tempShearStressShift.subtract(shearStress);
        answer.at(2) = shearStress.at(1);
        answer.at(3) = shearStress.at(2);
        break;
    default:
        OOFEM_ERROR("Unsupported interface mode");
    }

    double lim = 1.e+50;
    answer.at(1) = min(normalStress, lim);  //threshold on maximum
    answer.at(1) = max(answer.at(1), -lim);  //threshold on minimum
    //answer.at(1) = normalStress > lim ? lim : normalStress < -lim ? -lim : normalStress;
    // update gp
    status->setTempShearStressShift(tempShearStressShift);
    status->letTempStrainVectorBe(strainVector);
    status->letTempStressVectorBe(answer);
}
Example #13
0
void PLHoopStressCirc :: propagateInterfaces(Domain &iDomain, EnrichmentDomain &ioEnrDom)
{
    // Fetch crack tip data
    TipInfo tipInfoStart, tipInfoEnd;
    ioEnrDom.giveTipInfos(tipInfoStart, tipInfoEnd);
    std :: vector< TipInfo >tipInfo = {tipInfoStart, tipInfoEnd};

    SpatialLocalizer *localizer = iDomain.giveSpatialLocalizer();

    for ( size_t tipIndex = 0; tipIndex < tipInfo.size(); tipIndex++ ) {
        // Construct circle points on an arc from -90 to 90 degrees
        double angle = -90.0 + mAngleInc;
        std :: vector< double >angles;
        while ( angle <= ( 90.0 - mAngleInc ) ) {
            angles.push_back(angle * M_PI / 180.0);
            angle += mAngleInc;
        }

        const FloatArray &xT    = tipInfo [ tipIndex ].mGlobalCoord;
        const FloatArray &t             = tipInfo [ tipIndex ].mTangDir;
        const FloatArray &n             = tipInfo [ tipIndex ].mNormalDir;

        // It is meaningless to propagate a tip that is not inside any element
        Element *el = localizer->giveElementContainingPoint(tipInfo [ tipIndex ].mGlobalCoord);
        if ( el != NULL ) {
            std :: vector< FloatArray >circPoints;

            for ( size_t i = 0; i < angles.size(); i++ ) {
                FloatArray tangent(2);
                tangent.zero();
                tangent.add(cos(angles [ i ]), t);
                tangent.add(sin(angles [ i ]), n);
                tangent.normalize();

                FloatArray x(xT);
                x.add(mRadius, tangent);
                circPoints.push_back(x);
            }



            std :: vector< double >sigTTArray, sigRTArray;

            // Loop over circle points
            for ( size_t pointIndex = 0; pointIndex < circPoints.size(); pointIndex++ ) {
                FloatArray stressVec;

                if ( mUseRadialBasisFunc ) {
                    // Interpolate stress with radial basis functions

                    // Choose a cut-off length l:
                    // take the distance between two nodes in the element containing the
                    // crack tip multiplied by a constant factor.
                    // ( This choice implies that we hope that the element has reasonable
                    // aspect ratio.)
                    const FloatArray &x1 = * ( el->giveDofManager(1)->giveCoordinates() );
                    const FloatArray &x2 = * ( el->giveDofManager(2)->giveCoordinates() );
                    const double l = 1.0 * x1.distance(x2);

                    // Use the octree to get all elements that have
                    // at least one Gauss point in a certain region around the tip.
                    const double searchRadius = 3.0 * l;
                    std :: set< int >elIndices;
                    localizer->giveAllElementsWithIpWithinBox(elIndices, circPoints [ pointIndex ], searchRadius);


                    // Loop over the elements and Gauss points obtained.
                    // Evaluate the interpolation.
                    FloatArray sumQiWiVi;
                    double sumWiVi = 0.0;
                    for ( int elIndex: elIndices ) {
                        Element *gpEl = iDomain.giveElement(elIndex);
                        IntegrationRule *iRule = gpEl->giveDefaultIntegrationRulePtr();

                        for ( GaussPoint *gp_i: *iRule ) {

                            ////////////////////////////////////////
                            // Compute global gp coordinates
                            FloatArray N;
                            FEInterpolation *interp = gpEl->giveInterpolation();
                            interp->evalN( N, * ( gp_i->giveCoordinates() ), FEIElementGeometryWrapper(gpEl) );


                            // Compute global coordinates of Gauss point
                            FloatArray globalCoord(2);
                            globalCoord.zero();

                            for ( int i = 1; i <= gpEl->giveNumberOfDofManagers(); i++ ) {
                                DofManager *dMan = gpEl->giveDofManager(i);
                                globalCoord.at(1) += N.at(i) * dMan->giveCoordinate(1);
                                globalCoord.at(2) += N.at(i) * dMan->giveCoordinate(2);
                            }


                            ////////////////////////////////////////
                            // Compute weight of kernel function

                            FloatArray tipToGP;
                            tipToGP.beDifferenceOf(globalCoord, xT);
                            bool inFrontOfCrack = true;
                            if ( tipToGP.dotProduct(t) < 0.0 ) {
                                inFrontOfCrack = false;
                            }

                            double r = circPoints [ pointIndex ].distance(globalCoord);

                            if ( r < l && inFrontOfCrack ) {
                                double w = ( ( l - r ) / ( pow(2.0 * M_PI, 1.5) * pow(l, 3) ) ) * exp( -0.5 * pow(r, 2) / pow(l, 2) );

                                // Compute gp volume
                                double V = gpEl->computeVolumeAround(gp_i);

                                // Get stress
                                StructuralMaterialStatus *ms = dynamic_cast< StructuralMaterialStatus * >( gp_i->giveMaterialStatus() );
                                if ( ms == NULL ) {
                                    OOFEM_ERROR("failed to fetch MaterialStatus.");
                                }

                                FloatArray stressVecGP = ms->giveStressVector();

                                if ( sumQiWiVi.giveSize() != stressVecGP.giveSize() ) {
                                    sumQiWiVi.resize( stressVecGP.giveSize() );
                                    sumQiWiVi.zero();
                                }

                                // Add to numerator
                                sumQiWiVi.add(w * V, stressVecGP);

                                // Add to denominator
                                sumWiVi += w * V;
                            }
                        }
                    }


                    if ( fabs(sumWiVi) > 1.0e-12 ) {
                        stressVec.beScaled(1.0 / sumWiVi, sumQiWiVi);
                    } else {
                        // Take stress from closest Gauss point
                        int region = 1;
                        bool useCZGP = false;
                        GaussPoint &gp = * ( localizer->giveClosestIP(circPoints [ pointIndex ], region, useCZGP) );


                        // Compute stresses
                        StructuralMaterialStatus *ms = dynamic_cast< StructuralMaterialStatus * >( gp.giveMaterialStatus() );
                        if ( ms == NULL ) {
                            OOFEM_ERROR("failed to fetch MaterialStatus.");
                        }

                        stressVec = ms->giveStressVector();
                    }
                } else {
                    // Take stress from closest Gauss point
                    int region = 1;
                    bool useCZGP = false;
                    GaussPoint &gp = * ( localizer->giveClosestIP(circPoints [ pointIndex ], region, useCZGP) );


                    // Compute stresses
                    StructuralMaterialStatus *ms = dynamic_cast< StructuralMaterialStatus * >( gp.giveMaterialStatus() );
                    if ( ms == NULL ) {
                        OOFEM_ERROR("failed to fetch MaterialStatus.");
                    }

                    stressVec = ms->giveStressVector();
                }

                FloatMatrix stress(2, 2);

                int shearPos = stressVec.giveSize();

                stress.at(1, 1) = stressVec.at(1);
                stress.at(1, 2) = stressVec.at(shearPos);
                stress.at(2, 1) = stressVec.at(shearPos);
                stress.at(2, 2) = stressVec.at(2);


                // Rotation matrix
                FloatMatrix rot(2, 2);
                rot.at(1, 1) =  cos(angles [ pointIndex ]);
                rot.at(1, 2) = -sin(angles [ pointIndex ]);
                rot.at(2, 1) =  sin(angles [ pointIndex ]);
                rot.at(2, 2) =  cos(angles [ pointIndex ]);

                FloatArray tRot, nRot;
                tRot.beProductOf(rot, t);
                nRot.beProductOf(rot, n);

                FloatMatrix rotTot(2, 2);
                rotTot.setColumn(tRot, 1);
                rotTot.setColumn(nRot, 2);


                FloatMatrix tmp, stressRot;

                tmp.beTProductOf(rotTot, stress);
                stressRot.beProductOf(tmp, rotTot);


                const double sigThetaTheta      =               stressRot.at(2, 2);
                sigTTArray.push_back(sigThetaTheta);

                const double sigRTheta          =               stressRot.at(1, 2);
                sigRTArray.push_back(sigRTheta);
            }

            //////////////////////////////
            // Compute propagation angle

            // Find angles that fulfill sigRT = 0
            const double stressTol = 1.0e-9;
            double maxSigTT = 0.0, maxAngle = 0.0;
            bool foundZeroLevel = false;
            for ( size_t segIndex = 0; segIndex < ( circPoints.size() - 1 ); segIndex++ ) {
                // If the shear stress sigRT changes sign over the segment
                if ( sigRTArray [ segIndex ] * sigRTArray [ segIndex + 1 ] < stressTol ) {
                    // Compute location of zero level
                    double xi = EnrichmentItem :: calcXiZeroLevel(sigRTArray [ segIndex ], sigRTArray [ segIndex + 1 ]);

                    double theta                    = 0.5 * ( 1.0 - xi ) * angles [ segIndex ]         + 0.5 * ( 1.0 + xi ) * angles [ segIndex + 1 ];
                    double sigThetaTheta    = 0.5 * ( 1.0 - xi ) * sigTTArray [ segIndex ] + 0.5 * ( 1.0 + xi ) * sigTTArray [ segIndex + 1 ];

                    //					printf("Found candidate: theta: %e sigThetaTheta: %e\n", theta, sigThetaTheta);

                    if ( sigThetaTheta > maxSigTT ) {
                        foundZeroLevel = true;
                        maxSigTT = sigThetaTheta;
                        maxAngle = theta;
                    }
                }
            }

            if ( !foundZeroLevel ) {
                printf("No zero level was found.\n");
            }

            if ( iDomain.giveXfemManager()->giveVtkDebug() ) {
                XFEMDebugTools :: WriteArrayToMatlab("sigTTvsAngle.m", angles, sigTTArray);
                XFEMDebugTools :: WriteArrayToMatlab("sigRTvsAngle.m", angles, sigRTArray);

                XFEMDebugTools :: WriteArrayToGnuplot("sigTTvsAngle.dat", angles, sigTTArray);
                XFEMDebugTools :: WriteArrayToGnuplot("sigRTvsAngle.dat", angles, sigRTArray);
            }

            // Compare with threshold
            if ( maxSigTT > mHoopStressThreshold && foundZeroLevel ) {
                // Rotation matrix
                FloatMatrix rot(2, 2);
                rot.at(1, 1) =  cos(maxAngle);
                rot.at(1, 2) = -sin(maxAngle);
                rot.at(2, 1) =  sin(maxAngle);
                rot.at(2, 2) =  cos(maxAngle);

                FloatArray dir;
                dir.beProductOf(rot, tipInfo [ tipIndex ].mTangDir);

                // Fill up struct
                std :: vector< TipPropagation >tipPropagations;
                TipPropagation tipProp;
                tipProp.mTipIndex = tipIndex;
                tipProp.mPropagationDir = dir;
                tipProp.mPropagationLength = mIncrementLength;
                tipPropagations.push_back(tipProp);


                // Propagate
                ioEnrDom.propagateTips(tipPropagations);
            }
        }
    }
}
void PrescribedGradientBCNeumann :: assembleVector(FloatArray &answer, TimeStep *tStep,
                                                   CharType type, ValueModeType mode,
                                                   const UnknownNumberingScheme &s, FloatArray *eNorm)
{
    Set *setPointer = this->giveDomain()->giveSet(this->set);
    const IntArray &boundaries = setPointer->giveBoundaryList();

    IntArray loc, sigma_loc;  // For the displacements and stress respectively
    IntArray masterDofIDs, sigmaMasterDofIDs;
    mpSigmaHom->giveCompleteLocationArray(sigma_loc, s);

    if ( type == ExternalForcesVector ) {
        // The external forces have two contributions. On the additional equations for sigma, the load is simply the prescribed gradient.
        double rve_size = this->domainSize();
        FloatArray stressLoad;
        FloatArray gradVoigt;
        giveGradientVoigt(gradVoigt);

        stressLoad.beScaled(-rve_size, gradVoigt);
        answer.assemble(stressLoad, sigma_loc);
    } else if ( type == InternalForcesVector ) {
        FloatMatrix Ke;
        FloatArray fe_v, fe_s;
        FloatArray sigmaHom, e_u;

        // Fetch the current values of the stress;
        mpSigmaHom->giveCompleteUnknownVector(sigmaHom, mode, tStep);
        // and the master dof ids for sigmadev used for the internal norms
        mpSigmaHom->giveCompleteMasterDofIDArray(sigmaMasterDofIDs);

        // Assemble
        for ( int pos = 1; pos <= boundaries.giveSize() / 2; ++pos ) {
            Element *e = this->giveDomain()->giveElement( boundaries.at(pos * 2 - 1) );
            int boundary = boundaries.at(pos * 2);

            // Fetch the element information;
            e->giveLocationArray(loc, s, & masterDofIDs);
            // Here, we could use only the nodes actually located on the boundary, but we don't.
            // Instead, we use all nodes belonging to the element, which is allowed because the
            // basis functions related to the interior nodes will be zero on the boundary.
            // Obviously, this is less efficient, so why do we want to do it this way?
            // Because it is easier when XFEM enrichments are present. /ES
            e->computeVectorOf(mode, tStep, e_u);
            this->integrateTangent(Ke, e, boundary);

            // We just use the tangent, less duplicated code (the addition of sigmaDev is linear).
            fe_v.beProductOf(Ke, e_u);
            fe_s.beTProductOf(Ke, sigmaHom);

            // Note: The terms appear negative in the equations:
            fe_v.negated();
            fe_s.negated();

            answer.assemble(fe_s, loc); // Contributions to delta_v equations
            answer.assemble(fe_v, sigma_loc); // Contribution to delta_s_i equations
            if ( eNorm != NULL ) {
                eNorm->assembleSquared(fe_s, masterDofIDs);
                eNorm->assembleSquared(fe_v, sigmaMasterDofIDs);
            }
        }
    }
}
void IntMatBilinearCZ :: giveFirstPKTraction_3d(FloatArray &answer, GaussPoint *gp, const FloatArray &jump,
                                                const FloatMatrix &F, TimeStep *tStep)
{
    IntMatBilinearCZStatus *status = static_cast< IntMatBilinearCZStatus * >( this->giveStatus(gp) );

    status->mJumpNew = jump;

    FloatArray jumpInc;
    jumpInc.beDifferenceOf(status->mJumpNew, status->mJumpOld);

    FloatArray tractionTrial = status->mTractionOld;
    tractionTrial.add(mPenaltyStiffness, jumpInc);

    double TTrNormal    = tractionTrial.at(3);
    double TTrTang              = sqrt( pow(tractionTrial.at(1), 2.0) + pow(tractionTrial.at(2), 2.0) );
    double phiTr = computeYieldFunction(TTrNormal, TTrTang);

    const double damageTol = 1.0e-6;
    if ( status->mDamageOld > ( 1.0 - damageTol ) ) {
        status->mDamageNew = 1.0;
        status->mPlastMultIncNew = 0.0;
        answer.resize(3);
        answer.zero();
        status->mTractionNew = answer;

        status->letTempJumpBe(jump);
        status->letTempFirstPKTractionBe(answer);
        status->letTempTractionBe(answer);

        return;
    }


    answer = tractionTrial;

    if ( phiTr < 0.0 ) {
        status->mDamageNew = status->mDamageOld;
        status->mPlastMultIncNew = 0.0;
        answer.beScaled( ( 1.0 - status->mDamageNew ), answer );

        status->mTractionNew = answer;

        status->letTempJumpBe(jump);
        status->letTempFirstPKTractionBe(answer);
        status->letTempTractionBe(answer);

        return;
    } else {
        // Iterate to find plastic strain increment.
        int maxIter = 50;
        int minIter = 1;
        double absTol = 1.0e-9; // Absolute error tolerance
        double relTol = 1.0e-9; // Relative error tolerance
        double eps = 1.0e-12; // Small value for perturbation when computing numerical Jacobian
        double plastMultInc = 0.0;
        double initialRes = 0.0;

        for ( int iter = 0; iter < maxIter; iter++ ) {
            // Evaluate residual (i.e. yield function)
            computeTraction(answer, tractionTrial, plastMultInc);

            double TNormal      = answer.at(3);
            double TTang                = sqrt( pow(answer.at(1), 2.0) + pow(answer.at(2), 2.0) );
            double phi = computeYieldFunction(TNormal, TTang);

            //          if(iter > 20) {
            //              printf("iter: %d res: %e\n", iter, fabs(phi) );
            //          }

            if ( iter == 0 ) {
                initialRes = fabs(phi);
                initialRes = max(initialRes, 1.0e-12);
            }

            if ( (iter >= minIter && fabs(phi) < absTol) || ( iter >= minIter && ( fabs(phi) / initialRes ) < relTol ) ) {
                // Add damage evolution
                double S = mGIc / mSigmaF;
                status->mPlastMultIncNew = plastMultInc;

                double damageInc = status->mPlastMultIncNew / S;
                status->mDamageNew = status->mDamageOld + damageInc;

                if ( status->mDamageNew > 1.0 ) {
                    status->mDamageNew = 1.0;
                }

                if(mSemiExplicit) {
//                    computeTraction(answer, tractionTrial, status->mPlastMultIncOld);
                    answer.beScaled( ( 1.0 - status->mDamageOld ), answer );
               }
                else {
                    answer.beScaled( ( 1.0 - status->mDamageNew ), answer );
                }

                status->mTractionNew = answer;

                // Jim
                status->letTempJumpBe(jump);
                status->letTempFirstPKTractionBe(answer);
                status->letTempTractionBe(answer);

                return;
            }

            // Numerical Jacobian
            FloatArray tractionPert(3);
            computeTraction(tractionPert, tractionTrial, plastMultInc + eps);
            double TNormalPert          = tractionPert.at(3);
            double TTangPert            = sqrt( pow(tractionPert.at(1), 2.0) + pow(tractionPert.at(2), 2.0) );
            double phiPert = computeYieldFunction(TNormalPert, TTangPert);

            double Jac = ( phiPert - phi ) / eps;
            plastMultInc -= ( 1.0 / Jac ) * phi;
        }
    }

    OOFEM_ERROR("No convergence in.");
}