OOFEM 3.0
Loading...
Searching...
No Matches
prescribedgradientbcneumann.C
Go to the documentation of this file.
1/*
2 *
3 * ##### ##### ###### ###### ### ###
4 * ## ## ## ## ## ## ## ### ##
5 * ## ## ## ## #### #### ## # ##
6 * ## ## ## ## ## ## ## ##
7 * ## ## ## ## ## ## ## ##
8 * ##### ##### ## ###### ## ##
9 *
10 *
11 * OOFEM : Object Oriented Finite Element Code
12 *
13 * Copyright (C) 1993 - 2025 Borek Patzak
14 *
15 *
16 *
17 * Czech Technical University, Faculty of Civil Engineering,
18 * Department of Structural Mechanics, 166 29 Prague, Czech Republic
19 *
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
29 *
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 */
34
36#include "classfactory.h"
37#include "node.h"
38#include "masterdof.h"
39#include "element.h"
40#include "feinterpol.h"
41#include "feinterpol2d.h"
42#include "gausspoint.h"
43#include "sparsemtrx.h"
46#include "timestep.h"
47#include "function.h"
48#include "sparselinsystemnm.h"
50#include "engngm.h"
51#include "mathfem.h"
52
53#ifdef _OPENMP
54#include <omp.h>
55#endif
56
57namespace oofem {
59
60PrescribedGradientBCNeumann :: PrescribedGradientBCNeumann(int n, Domain *d) :
63 mpSigmaHom( new Node(0, d) )
64{
65 int nsd = d->giveNumberOfSpatialDimensions();
66 for ( int i = 0; i < nsd * nsd; i++ ) {
67 // Just putting in X_i id-items since they don't matter.
68 int dofId = d->giveNextFreeDofID();
69 mSigmaIds.followedBy(dofId);
70 mpSigmaHom->appendDof( new MasterDof( mpSigmaHom.get(), ( DofIDItem ) ( dofId ) ) );
71 }
72}
73
74PrescribedGradientBCNeumann :: ~PrescribedGradientBCNeumann()
75{
76}
77
78
79void PrescribedGradientBCNeumann :: initializeFrom(InputRecord &ir)
80{
81 ActiveBoundaryCondition :: initializeFrom(ir);
82 PrescribedGradientHomogenization :: initializeFrom(ir);
83}
84
85
86void PrescribedGradientBCNeumann :: giveInputRecord(DynamicInputRecord &input)
87{
88 ActiveBoundaryCondition :: giveInputRecord(input);
89 PrescribedGradientHomogenization :: giveInputRecord(input);
90}
91
92
93DofManager *PrescribedGradientBCNeumann :: giveInternalDofManager(int i)
94{
95 return mpSigmaHom.get();
96}
97
98void PrescribedGradientBCNeumann :: scale(double s)
99{
100 this->mGradient.times(s);
101}
102
103void PrescribedGradientBCNeumann :: assembleVector(FloatArray &answer, TimeStep *tStep,
104 CharType type, ValueModeType mode,
105 const UnknownNumberingScheme &s,
106 FloatArray *eNorm,
107 void* lock)
108{
109 IntArray sigma_loc; // For the displacements and stress respectively
110 mpSigmaHom->giveLocationArray(mSigmaIds, sigma_loc, s);
111
112 if ( type == ExternalForcesVector ) {
113 // The external forces have two contributions. On the additional equations for sigma, the load is simply the prescribed gradient.
114 double rve_size = this->domainSize(this->giveDomain(), this->giveSetNumber());
115 FloatArray stressLoad;
116 FloatArray gradVoigt;
117 giveGradientVoigt(gradVoigt);
118
119 double loadLevel = this->giveTimeFunction()->evaluateAtTime(tStep->giveTargetTime());
120 stressLoad.beScaled(-rve_size*loadLevel, gradVoigt);
121#ifdef _OPENMP
122 if (lock) omp_set_lock(static_cast<omp_lock_t*>(lock));
123#endif
124 answer.assemble(stressLoad, sigma_loc);
125#ifdef _OPENMP
126 if (lock) omp_unset_lock(static_cast<omp_lock_t*>(lock));
127#endif
128 } else if ( type == InternalForcesVector ) {
129 FloatMatrix Ke;
130 FloatArray fe_v, fe_s;
131 FloatArray sigmaHom, e_u;
132 IntArray loc, masterDofIDs, sigmaMasterDofIDs;
133
134 // Fetch the current values of the stress;
135 mpSigmaHom->giveUnknownVector(sigmaHom, mSigmaIds, mode, tStep);
136 // and the master dof ids for sigmadev used for the internal norms
137 mpSigmaHom->giveMasterDofIDArray(mSigmaIds, sigmaMasterDofIDs);
138
139 // Assemble
140 Set *setPointer = this->giveDomain()->giveSet(this->set);
141 const IntArray &boundaries = setPointer->giveBoundaryList();
142 for ( int pos = 1; pos <= boundaries.giveSize() / 2; ++pos ) {
143 Element *e = this->giveDomain()->giveElement( boundaries.at(pos * 2 - 1) );
144 int boundary = boundaries.at(pos * 2);
145
146 // Fetch the element information;
147 e->giveLocationArray(loc, s, & masterDofIDs);
148 // Here, we could use only the nodes actually located on the boundary, but we don't.
149 // Instead, we use all nodes belonging to the element, which is allowed because the
150 // basis functions related to the interior nodes will be zero on the boundary.
151 // Obviously, this is less efficient, so why do we want to do it this way?
152 // Because it is easier when XFEM enrichments are present. /ES
153 e->computeVectorOf(mode, tStep, e_u);
154 this->integrateTangent(Ke, e, boundary);
155
156 // We just use the tangent, less duplicated code (the addition of sigmaDev is linear).
157 fe_v.beProductOf(Ke, e_u);
158 fe_s.beTProductOf(Ke, sigmaHom);
159
160 // Note: The terms appear negative in the equations:
161 fe_v.negated();
162 fe_s.negated();
163#ifdef _OPENMP
164 if (lock) omp_set_lock(static_cast<omp_lock_t*>(lock));
165#endif
166 answer.assemble(fe_s, loc); // Contributions to delta_v equations
167 answer.assemble(fe_v, sigma_loc); // Contribution to delta_s_i equations
168 if ( eNorm != NULL ) {
169 eNorm->assembleSquared(fe_s, masterDofIDs);
170 eNorm->assembleSquared(fe_v, sigmaMasterDofIDs);
171 }
172#ifdef _OPENMP
173 if (lock) omp_unset_lock(static_cast<omp_lock_t*>(lock));
174#endif
175 }
176 }
177}
178
179void PrescribedGradientBCNeumann :: assemble(SparseMtrx &answer, TimeStep *tStep,
180 CharType type,
181 const UnknownNumberingScheme &r_s,
182 const UnknownNumberingScheme &c_s,
183 double scale,
184 void* lock)
185{
186 if ( type == TangentStiffnessMatrix || type == SecantStiffnessMatrix || type == ElasticStiffnessMatrix ) {
187 FloatMatrix Ke, KeT;
188 IntArray loc_r, loc_c, sigma_loc_r, sigma_loc_c;
189 Set *set = this->giveDomain()->giveSet(this->set);
190 const IntArray &boundaries = set->giveBoundaryList();
191
192 // Fetch the columns/rows for the stress contributions;
193 mpSigmaHom->giveLocationArray(mSigmaIds, sigma_loc_r, r_s);
194 mpSigmaHom->giveLocationArray(mSigmaIds, sigma_loc_c, c_s);
195
196 for ( int pos = 1; pos <= boundaries.giveSize() / 2; ++pos ) {
197 Element *e = this->giveDomain()->giveElement( boundaries.at(pos * 2 - 1) );
198 int boundary = boundaries.at(pos * 2);
199
200 // Here, we could use only the nodes actually located on the boundary, but we don't.
201 // Instead, we use all nodes belonging to the element, which is allowed because the
202 // basis functions related to the interior nodes will be zero on the boundary.
203 // Obviously, this is less efficient, so why do we want to do it this way?
204 // Because it is easier when XFEM enrichments are present. /ES
205 e->giveLocationArray(loc_r, r_s);
206 e->giveLocationArray(loc_c, c_s);
207
208 this->integrateTangent(Ke, e, boundary);
209 Ke.negated();
210 Ke.times(scale);
211 KeT.beTranspositionOf(Ke);
212#ifdef _OPENMP
213 if (lock) omp_set_lock(static_cast<omp_lock_t*>(lock));
214#endif
215 answer.assemble(sigma_loc_r, loc_c, Ke); // Contribution to delta_s_i equations
216 answer.assemble(loc_r, sigma_loc_c, KeT); // Contributions to delta_v equations
217#ifdef _OPENMP
218 if (lock) omp_unset_lock(static_cast<omp_lock_t*>(lock));
219#endif
220 }
221 } else {
222 OOFEM_LOG_DEBUG("Skipping assembly in PrescribedGradientBCNeumann::assemble().");
223 }
224}
225
226void PrescribedGradientBCNeumann :: giveLocationArrays(std :: vector< IntArray > &rows, std :: vector< IntArray > &cols, CharType type,
227 const UnknownNumberingScheme &r_s, const UnknownNumberingScheme &c_s)
228{
229 IntArray loc_r, loc_c, sigma_loc_r, sigma_loc_c;
230
231 // Fetch the columns/rows for the stress contributions;
232 mpSigmaHom->giveLocationArray(mSigmaIds, sigma_loc_r, r_s);
233 mpSigmaHom->giveLocationArray(mSigmaIds, sigma_loc_c, c_s);
234
235 Set *set = this->giveDomain()->giveSet(this->set);
236 const IntArray &boundaries = set->giveBoundaryList();
237
238 rows.resize( boundaries.giveSize() );
239 cols.resize( boundaries.giveSize() );
240 int i = 0;
241 for ( int pos = 1; pos <= boundaries.giveSize() / 2; ++pos ) {
242 Element *e = this->giveDomain()->giveElement( boundaries.at(pos * 2 - 1) );
243
244 // Here, we could use only the nodes actually located on the boundary, but we don't.
245 // Instead, we use all nodes belonging to the element, which is allowed because the
246 // basis functions related to the interior nodes will be zero on the boundary.
247 // Obviously, this is less efficient, so why do we want to do it this way?
248 // Because it is easier when XFEM enrichments are present. /ES
249 e->giveLocationArray(loc_r, r_s);
250 e->giveLocationArray(loc_c, c_s);
251
252 // For most uses, loc_r == loc_c, and sigma_loc_r == sigma_loc_c.
253 rows [ i ] = loc_r;
254 cols [ i ] = sigma_loc_c;
255 i++;
256 // and the symmetric part (usually the transpose of above)
257 rows [ i ] = sigma_loc_r;
258 cols [ i ] = loc_c;
259 i++;
260 }
261}
262
263void PrescribedGradientBCNeumann :: computeField(FloatArray &sigma, TimeStep *tStep)
264{
265 mpSigmaHom->giveUnknownVector(sigma, mSigmaIds, VM_Total, tStep);
266}
267
268
269void PrescribedGradientBCNeumann :: computeTangent(FloatMatrix &tangent, TimeStep *tStep)
270{
271 EngngModel *rve = this->giveDomain()->giveEngngModel();
273 std :: unique_ptr< SparseLinearSystemNM > solver(
274 classFactory.createSparseLinSolver( ST_Petsc, this->domain, this->domain->giveEngngModel() ) ); // = rve->giveLinearSolver();
275 SparseMtrxType stype = solver->giveRecommendedMatrix(true);
276 double rve_size = this->domainSize(this->giveDomain(), this->giveSetNumber());
277
278 // 1. Kuu*us = -Kus*s => us = -Kuu\Ku where u = us*s
279 // 2. Ks = Kus'*us
280 // 3. Ks*lambda = I
281
282 // 1.
283 // This is not very good. We have to keep Kuu and Kff in memory at the same time. Not optimal
284 // Consider changing this approach.
286 std :: unique_ptr< SparseMtrx > Kff( classFactory.createSparseMtrx(stype) );
287 if ( !Kff ) {
288 OOFEM_ERROR("Couldn't create sparse matrix of type %d\n", stype);
289 }
290 Kff->buildInternalStructure(rve, this->domain->giveNumber(), fnum);
291
292 rve->assemble(*Kff, tStep, TangentAssembler(TangentStiffness), fnum, this->domain);
293
294 IntArray loc_u, loc_s;
295 this->mpSigmaHom->giveLocationArray(this->mSigmaIds, loc_s, fnum);
296 int neq = Kff->giveNumberOfRows();
297 loc_u.resize(neq - loc_s.giveSize());
298 int k = 0;
299 for ( int i = 1; i <= neq; ++i ) {
300 if ( !loc_s.contains(i) ) {
301 loc_u.at(k++) = i;
302 }
303 }
304
305 std :: unique_ptr< SparseMtrx > Kuu = Kff->giveSubMatrix(loc_u, loc_u);
306 // NOTE: Kus is actually a dense matrix, but we have to make it a dense matrix first
307 std :: unique_ptr< SparseMtrx > Kus = Kff->giveSubMatrix(loc_u, loc_s);
308 FloatMatrix eye(Kus->giveNumberOfColumns(), Kus->giveNumberOfColumns());
309 eye.beUnitMatrix();
310 FloatMatrix KusD;
311 Kus->times(KusD, eye);
312
313 // Release a large chunk of redundant memory early.
314 Kus.reset();
315 Kff.reset();
316
317 // 1.
318 FloatMatrix us;
319 solver->solve(*Kuu, KusD, us);
320 us.negated();
321
322 // 2.
323 FloatMatrix Ks;
324 Ks.beTProductOf(KusD, us);
325 Kus->times(Ks, us);
326
327 // 3.
328 tangent.beInverseOf(Ks);
329 tangent.times(rve_size);
330}
331
332
333void PrescribedGradientBCNeumann :: giveStressLocationArray(IntArray &oCols, const UnknownNumberingScheme &r_s)
334{
335 mpSigmaHom->giveLocationArray(mSigmaIds, oCols, r_s);
336}
337
338void PrescribedGradientBCNeumann :: integrateTangent(FloatMatrix &oTangent, Element *e, int iBndIndex)
339{
340 FloatArray normal, n;
341 FloatMatrix nMatrix, E_n;
342 FloatMatrix contrib;
343
344 Domain *domain = e->giveDomain();
345
346 FEInterpolation *interp = e->giveInterpolation(); // Geometry interpolation
347
349
350 // Interpolation order
351 int order = interp->giveInterpolationOrder();
352 std :: unique_ptr< IntegrationRule > ir;
353
354 XfemElementInterface *xfemElInt = dynamic_cast< XfemElementInterface * >( e );
355 if ( xfemElInt && domain->hasXfemManager() ) {
356 FEInterpolation2d *interp2d = dynamic_cast< FEInterpolation2d * >( interp );
357 if ( !interp2d ) {
358 OOFEM_ERROR("failed to cast to FEInterpolation2d.")
359 }
360 // const auto &edgeNodes = interp2d->computeLocalEdgeMapping(iBndIndex);
361 // const auto &xS = * ( e->giveDofManager( edgeNodes.at(1) )->giveCoordinates() );
362 // const auto &xE = * ( e->giveDofManager( edgeNodes.at( edgeNodes.giveSize() ) )->giveCoordinates() );
363
364 std :: vector< Line >segments;
365 std :: vector< FloatArray >intersecPoints;
366 xfemElInt->partitionEdgeSegment(iBndIndex, segments, intersecPoints);
367 MaterialMode matMode = e->giveMaterialMode();
368 ir = std::make_unique<DiscontinuousSegmentIntegrationRule>(1, e, segments);
369 int numPointsPerSeg = 1;
370 ir->SetUpPointsOnLine(numPointsPerSeg, matMode);
371 } else {
372 ir = interp->giveBoundaryIntegrationRule(order, iBndIndex, e->giveGeometryType());
373 }
374
375 oTangent.clear();
376
377 for ( auto &gp: *ir ) {
378 const FloatArray &lcoords = gp->giveNaturalCoordinates();
379 FEIElementGeometryWrapper cellgeo(e);
380
381 // Evaluate the normal;
382 double detJ = interp->boundaryEvalNormal(normal, iBndIndex, lcoords, cellgeo);
383
384 // Compute global coordinates of Gauss point
385 FloatArray globalCoord;
386
387 interp->boundaryLocal2Global(globalCoord, iBndIndex, lcoords, cellgeo);
388
389 // Compute local coordinates on the element
390 FloatArray bulkElLocCoords;
391 e->computeLocalCoordinates(bulkElLocCoords, globalCoord);
392
393 // If cracks cross the edge, special treatment is necessary.
394 // Exploit the XfemElementInterface to minimize duplication of code.
395 if ( xfemElInt != NULL && domain->hasXfemManager() ) {
396
397 xfemElInt->XfemElementInterface_createEnrNmatrixAt(nMatrix, bulkElLocCoords, * e, false);
398 } else {
399 // Evaluate the velocity/displacement coefficients
400 interp->evalN(n, bulkElLocCoords, cellgeo);
401 nMatrix.beNMatrixOf(n, nsd);
402 }
403
404 E_n.resize(nsd*nsd, nsd);
405 E_n.zero();
406
407 if ( nsd == 3 ) {
408 E_n.at(1, 1) = normal.at(1);
409 E_n.at(2, 2) = normal.at(2);
410 E_n.at(3, 3) = normal.at(3);
411 E_n.at(4, 1) = normal.at(2);
412 E_n.at(5, 1) = normal.at(3);
413 E_n.at(6, 2) = normal.at(3);
414 E_n.at(7, 2) = normal.at(1);
415 E_n.at(8, 3) = normal.at(1);
416 E_n.at(9, 3) = normal.at(2);
417 } else if ( nsd == 2 ) {
418 E_n.at(1, 1) = normal.at(1);
419 E_n.at(2, 2) = normal.at(2);
420 E_n.at(3, 1) = normal.at(2);
421 E_n.at(4, 2) = normal.at(1);
422 } else {
423 E_n.at(1, 1) = normal.at(1);
424 }
425
426 contrib.beProductOf(E_n, nMatrix);
427
428 oTangent.add(detJ * gp->giveWeight(), contrib);
429 }
430}
431} /* namespace oofem */
#define REGISTER_BoundaryCondition(class)
ActiveBoundaryCondition(int n, Domain *d)
Definition activebc.h:71
int giveNextFreeDofID(int increment=1)
Definition domain.C:1519
int giveNumberOfSpatialDimensions()
Returns number of spatial dimensions.
Definition domain.C:1137
virtual FEInterpolation * giveInterpolation() const
Definition element.h:648
virtual MaterialMode giveMaterialMode()
Definition element.h:738
void giveLocationArray(IntArray &locationArray, const UnknownNumberingScheme &s, IntArray *dofIds=NULL) const
Definition element.C:429
void computeVectorOf(ValueModeType u, TimeStep *tStep, FloatArray &answer)
Definition element.C:103
virtual bool computeLocalCoordinates(FloatArray &answer, const FloatArray &gcoords)
Definition element.C:1240
virtual Element_Geometry_Type giveGeometryType() const =0
virtual void assemble(SparseMtrx &answer, TimeStep *tStep, const MatrixAssembler &ma, const UnknownNumberingScheme &s, Domain *domain)
Definition engngm.C:889
virtual std::unique_ptr< IntegrationRule > giveBoundaryIntegrationRule(int order, int boundary, const Element_Geometry_Type) const
Definition feinterpol.C:101
virtual void evalN(FloatArray &answer, const FloatArray &lcoords, const FEICellGeometry &cellgeo) const =0
int giveInterpolationOrder() const
Definition feinterpol.h:199
virtual void boundaryLocal2Global(FloatArray &answer, int boundary, const FloatArray &lcoords, const FEICellGeometry &cellgeo) const =0
virtual double boundaryEvalNormal(FloatArray &answer, int boundary, const FloatArray &lcoords, const FEICellGeometry &cellgeo) const =0
Domain * giveDomain() const
Definition femcmpnn.h:97
Domain * domain
Link to domain object, useful for communicating with other FEM components.
Definition femcmpnn.h:79
void assemble(const FloatArray &fe, const IntArray &loc)
Definition floatarray.C:616
double & at(Index i)
Definition floatarray.h:202
void beProductOf(const FloatMatrix &aMatrix, const FloatArray &anArray)
Definition floatarray.C:689
void assembleSquared(const FloatArray &fe, const IntArray &loc)
Definition floatarray.C:637
void beScaled(double s, const FloatArray &b)
Definition floatarray.C:208
void beTProductOf(const FloatMatrix &aMatrix, const FloatArray &anArray)
Definition floatarray.C:721
void times(double f)
void add(const FloatMatrix &a)
void resize(Index rows, Index cols)
Definition floatmatrix.C:79
*Sets size of receiver to be an empty matrix It will have zero rows and zero columns size void clear()
void beProductOf(const FloatMatrix &a, const FloatMatrix &b)
void beNMatrixOf(const FloatArray &n, int nsd)
void beTranspositionOf(const FloatMatrix &src)
void zero()
Zeroes all coefficient of receiver.
bool beInverseOf(const FloatMatrix &src)
double at(std::size_t i, std::size_t j) const
void beTProductOf(const FloatMatrix &a, const FloatMatrix &b)
int set
Set number for boundary condition to be applied to.
void resize(int n)
Definition intarray.C:73
bool contains(int value) const
Definition intarray.h:292
int & at(std::size_t i)
Definition intarray.h:104
int giveSize() const
Definition intarray.h:211
void integrateTangent(FloatMatrix &oTangent, Element *e, int iBndIndex)
Help function that integrates the tangent contribution from a single element boundary.
std ::unique_ptr< Node > mpSigmaHom
DOF-manager containing the unknown homogenized stress.
const IntArray & giveBoundaryList()
Definition set.C:160
virtual int assemble(const IntArray &loc, const FloatMatrix &mat)=0
double giveTargetTime()
Returns target time.
Definition timestep.h:164
void XfemElementInterface_createEnrNmatrixAt(FloatMatrix &oAnswer, const FloatArray &iLocCoord, Element &iEl, bool iSetDiscontContribToZero)
Creates enriched N-matrix.
void partitionEdgeSegment(int iBndIndex, std ::vector< Line > &oSegments, std ::vector< FloatArray > &oIntersectionPoints, const double &iTangDistPadding=0.0)
#define OOFEM_ERROR(...)
Definition error.h:79
#define OOFEM_LOG_DEBUG(...)
Definition logger.h:144
ClassFactory & classFactory
FloatMatrixF< N, N > eye()
Constructs an identity matrix.

This page is part of the OOFEM-3.0 documentation. Copyright Copyright (C) 1994-2025 Borek Patzak Bořek Patzák
Project e-mail: oofem@fsv.cvut.cz
Generated at for OOFEM by doxygen 1.15.0 written by Dimitri van Heesch, © 1997-2011