OOFEM 3.0
Loading...
Searching...
No Matches
trplanstrssxfem.C
Go to the documentation of this file.
1/*
2 * trplanstrssxfem.C
3 *
4 * Created on: Jun 3, 2013
5 * Author: svennine
6 */
7
9
12#include "vtkxmlexportmodule.h"
15#include "xfem/enrichmentitem.h"
16#include "xfem/delaunay.h"
17#include "xfem/XFEMDebugTools.h"
18#include "feinterpol.h"
19#include "node.h"
20#include "crosssection.h"
21#include "gausspoint.h"
23#include "floatmatrix.h"
24#include "floatarray.h"
25#include "intarray.h"
26#include "mathfem.h"
27#include "classfactory.h"
28#include "dynamicinputrecord.h"
29#include "parametermanager.h"
30#include "paramkey.h"
31
32#ifdef __OOFEG
33 #include "oofeggraphiccontext.h"
34 #include "oofegutils.h"
36#endif
37
38
39
40#include <string>
41#include <sstream>
42
43
44namespace oofem {
48
49void TrPlaneStress2dXFEM :: updateYourself(TimeStep *tStep)
50{
51 TrPlaneStress2d :: updateYourself(tStep);
52 XfemStructuralElementInterface :: updateYourselfCZ(tStep);
53}
54
55void TrPlaneStress2dXFEM :: postInitialize()
56{
57 TrPlaneStress2d :: postInitialize();
58 XfemStructuralElementInterface :: initializeCZMaterial();
59}
60
61
62TrPlaneStress2dXFEM :: ~TrPlaneStress2dXFEM() { }
63
64int TrPlaneStress2dXFEM :: checkConsistency()
65{
66 TrPlaneStress2d :: checkConsistency();
67 return 1;
68}
69
71TrPlaneStress2dXFEM :: giveInterface(InterfaceType it)
72{
73 if ( it == XfemElementInterfaceType ) {
74 return static_cast< XfemElementInterface * >(this);
75 } else if ( it == VTKXMLExportModuleElementInterfaceType ) {
76 return static_cast< VTKXMLExportModuleElementInterface * >(this);
77 } else {
78 return TrPlaneStress2d :: giveInterface(it);
79 }
80}
81
82int TrPlaneStress2dXFEM :: computeNumberOfDofs()
83{
84 int ndofs = 0;
85
86 for ( int i = 1; i <= this->giveNumberOfDofManagers(); i++ ) {
87 ndofs += this->giveDofManager(i)->giveNumberOfDofs();
88 }
89
90 return ndofs;
91}
92
93void TrPlaneStress2dXFEM :: computeGaussPoints()
94{
95 if ( giveDomain()->hasXfemManager() ) {
96 XfemManager *xMan = giveDomain()->giveXfemManager();
97
98 if ( xMan->isElementEnriched(this) ) {
100 TrPlaneStress2d :: computeGaussPoints();
101 }
102 } else {
103 TrPlaneStress2d :: computeGaussPoints();
104 }
105 } else {
106 TrPlaneStress2d :: computeGaussPoints();
107 }
108}
109
110void TrPlaneStress2dXFEM :: computeBmatrixAt(GaussPoint *gp, FloatMatrix &answer, int li, int ui)
111{
112 XfemElementInterface_createEnrBmatrixAt(answer, * gp, * this);
113}
114
115void TrPlaneStress2dXFEM :: computeBHmatrixAt(GaussPoint *gp, FloatMatrix &answer)
116{
117 XfemElementInterface_createEnrBHmatrixAt(answer, * gp, * this);
118}
119
120void TrPlaneStress2dXFEM :: computeNmatrixAt(const FloatArray &iLocCoord, FloatMatrix &answer)
121{
122 XfemElementInterface_createEnrNmatrixAt(answer, iLocCoord, * this, false);
123}
124
125
126
127void
128TrPlaneStress2dXFEM :: giveDofManDofIDMask(int inode, IntArray &answer) const
129{
130 // Continuous part
131 TrPlaneStress2d :: giveDofManDofIDMask(inode, answer);
132
133 // Discontinuous part
134 if( this->giveDomain()->hasXfemManager() ) {
135 DofManager *dMan = giveDofManager(inode);
136 XfemManager *xMan = giveDomain()->giveXfemManager();
137
138 int placeInArray = domain->giveDofManPlaceInArray(dMan->giveGlobalNumber());
139 const std::vector<int> &nodeEiIndices = xMan->giveNodeEnrichmentItemIndices( placeInArray );
140 for ( size_t i = 0; i < nodeEiIndices.size(); i++ ) {
141 EnrichmentItem *ei = xMan->giveEnrichmentItem(nodeEiIndices[i]);
142 if ( ei->isDofManEnriched(* dMan) ) {
143 IntArray eiDofIdArray;
144 ei->computeEnrichedDofManDofIdArray(eiDofIdArray, *dMan);
145 answer.followedBy(eiDofIdArray);
146 }
147 }
148 }
149}
150
151void
152TrPlaneStress2dXFEM :: computeConstitutiveMatrixAt(FloatMatrix &answer, MatResponseMode rMode, GaussPoint *gp, TimeStep *tStep)
153{
154 XfemStructuralElementInterface :: XfemElementInterface_computeConstitutiveMatrixAt(answer, rMode, gp, tStep);
155}
156
157void
158TrPlaneStress2dXFEM :: computeStressVector(FloatArray &answer, const FloatArray &strain, GaussPoint *gp, TimeStep *tStep)
159{
160 XfemStructuralElementInterface :: XfemElementInterface_computeStressVector(answer, strain, gp, tStep);
161}
162
163void TrPlaneStress2dXFEM :: computeStiffnessMatrix(FloatMatrix &answer, MatResponseMode rMode, TimeStep *tStep)
164{
165 TrPlaneStress2d :: computeStiffnessMatrix(answer, rMode, tStep);
166 XfemStructuralElementInterface :: computeCohesiveTangent(answer, tStep);
167
168
169 const double tol = mRegCoeffTol;
170 const double regularizationCoeff = mRegCoeff;
171 int numRows = answer.giveNumberOfRows();
172 for(int i = 0; i < numRows; i++) {
173 if( fabs(answer(i,i)) < tol ) {
174 answer(i,i) += regularizationCoeff;
175// printf("Found zero on diagonal.\n");
176 }
177 }
178}
179
180void TrPlaneStress2dXFEM :: computeDeformationGradientVector(FloatArray &answer, GaussPoint *gp, TimeStep *tStep)
181{
183}
184
185void
186TrPlaneStress2dXFEM :: giveInternalForcesVector(FloatArray &answer, TimeStep *tStep, int useUpdatedGpRecord)
187{
188 TrPlaneStress2d :: giveInternalForcesVector(answer, tStep, useUpdatedGpRecord);
189 XfemStructuralElementInterface :: computeCohesiveForces(answer, tStep);
190
191
192#if 0
194 // Contribution from regularization
195 FloatMatrix K;
196 computeStiffnessMatrix(K, TangentStiffness, tStep);
197
198 FloatArray u;
199 this->computeVectorOf(VM_Total, tStep, u);
200 // subtract initial displacements, if defined
201 if ( initialDisplacements ) {
203 }
204
205 const double tol = mRegCoeffTol+mRegCoeff;
206 const double regularizationCoeff = mRegCoeff;
207 int numRows = K.giveNumberOfRows();
208 for(int i = 0; i < numRows; i++) {
209 if( fabs(K(i,i)) < tol ) {
210 answer(i) += regularizationCoeff*u(i);
211// printf("Found zero on diagonal.\n");
212 }
213 }
214#endif
215}
216
217
219TrPlaneStress2dXFEM :: giveGeometryType() const
220{
221 if ( this->giveDomain()->hasXfemManager() ) {
222 XfemManager *xMan = this->giveDomain()->giveXfemManager();
223 if ( xMan->isElementEnriched(this) ) {
224 return EGT_Composite;
225 } else {
226 return EGT_Composite;
227 }
228 } else {
229 return EGT_triangle_1;
230 }
231}
232
233#ifdef __OOFEG
234// TODO: FIX OOFEG implementation
235void TrPlaneStress2dXFEM :: drawRawGeometry(oofegGraphicContext &gc, TimeStep *tStep)
236{
237 if ( !gc.testElementGraphicActivity(this) ) {
238 return;
239 }
240
241 XfemManager *xf = this->giveDomain()->giveXfemManager();
242 if ( !xf->isElementEnriched(this) ) {
243 TrPlaneStress2d :: drawRawGeometry(gc, tStep);
244 } else {
245 if ( integrationRulesArray.size() > 1 ) {
246#if 0
247 for ( auto &ir: integrationRulesArray ) {
248 // TODO: Implement visualization.
249 PatchIntegrationRule *iRule = dynamic_cast< PatchIntegrationRule * >( ir );
250 if ( iRule ) {
251 iRule->givePatch()->draw(gc);
252 }
253 }
254#endif
255 } else {
256 TrPlaneStress2d :: drawRawGeometry(gc, tStep);
257 }
258 }
259}
260
261void TrPlaneStress2dXFEM :: drawScalar(oofegGraphicContext &gc, TimeStep *tStep)
262{
263 if ( !gc.testElementGraphicActivity(this) ) {
264 return;
265 }
266
267 XfemManager *xf = this->giveDomain()->giveXfemManager();
268 if ( !xf->isElementEnriched(this) ) {
269 TrPlaneStress2d :: drawScalar(gc, tStep);
270 } else {
271 if ( gc.giveIntVarMode() == ISM_local ) {
272 int indx;
273 double val;
274 FloatArray s(3), v;
275
276 indx = gc.giveIntVarIndx();
277
278 for ( auto &ir: integrationRulesArray ) {
279 PatchIntegrationRule *iRule = dynamic_cast< PatchIntegrationRule * >(ir.get());
280
281 #if 0
282 val = iRule->giveMaterial();
283 #else
284 val = 0.0;
285 for ( GaussPoint *gp: *iRule ) {
286 giveIPValue(v, gp, gc.giveIntVarType(), tStep);
287 val += v.at(indx);
288 }
289
290 val /= iRule->giveNumberOfIntegrationPoints();
291 #endif
292 s.at(1) = s.at(2) = s.at(3) = val;
293 // TODO: Implement visualization.
294 // iRule->givePatch()->drawWD(gc, s);
295 }
296 } else {
297 TrPlaneStress2d :: drawScalar(gc, tStep);
298 }
299 }
300}
301#endif
302
303void
304TrPlaneStress2dXFEM :: initializeFrom(InputRecord &ir, int priority)
305{
306 ParameterManager &ppm = giveDomain()->elementPPM;
307 TrPlaneStress2d :: initializeFrom(ir, priority);
308 XfemStructuralElementInterface :: initializeCZFrom(ir, priority);
309
312}
313
314MaterialMode TrPlaneStress2dXFEM :: giveMaterialMode()
315{
316 return XfemStructuralElementInterface :: giveMaterialMode();
317}
318
319void TrPlaneStress2dXFEM :: giveInputRecord(DynamicInputRecord &input)
320{
321 TrPlaneStress2d :: giveInputRecord(input);
322 XfemStructuralElementInterface :: giveCZInputRecord(input);
323
326
327}
328
329
330void
331TrPlaneStress2dXFEM :: computeField(ValueModeType mode, TimeStep *tStep, const FloatArray &lcoords, FloatArray &answer)
332{
333 // TODO: Validate implementation.
334
335 FloatArray u;
336 FloatMatrix n;
337
338 XfemElementInterface_createEnrNmatrixAt(n, lcoords, * this, false);
339
340 this->computeVectorOf(mode, tStep, u);
341 answer.beProductOf(n, u);
342}
343
344
345void
346TrPlaneStress2dXFEM :: giveElementDofIDMask(IntArray &answer) const
347{
348 // TODO: For now, take only the continuous part
349 TrPlaneStress2d :: giveElementDofIDMask(answer);
350}
351
352void
353TrPlaneStress2dXFEM :: giveCompositeExportData(std::vector< ExportRegion > &vtkPieces, IntArray &primaryVarsToExport, IntArray &internalVarsToExport, IntArray cellVarsToExport, TimeStep *tStep)
354{
355 vtkPieces.resize(1);
356
357 const int numCells = mSubTri.size();
358 if(numCells == 0) {
359 // Enriched but uncut element
360 // Visualize as a triangle
361 vtkPieces[0].setNumberOfCells(1);
362
363 int numTotalNodes = 3;
364 vtkPieces[0].setNumberOfNodes(numTotalNodes);
365
366 // Node coordinates
367 std :: vector< FloatArray >nodeCoords;
368 for(int i = 1; i <= 3; i++) {
369 const auto &x = giveDofManager(i)->giveCoordinates();
370 nodeCoords.push_back(x);
371
372 vtkPieces[0].setNodeCoords(i, x);
373 }
374
375 // Connectivity
376 IntArray nodes1 = {1, 2, 3};
377 vtkPieces[0].setConnectivity(1, nodes1);
378
379 // Offset
380 int offset = 3;
381 vtkPieces[0].setOffset(1, offset);
382
383 // Cell types
384 vtkPieces[0].setCellType(1, 5); // Linear triangle
385
386
387
388
389 // Export nodal variables from primary fields
390 vtkPieces[0].setNumberOfPrimaryVarsToExport(primaryVarsToExport, numTotalNodes);
391
392 for ( int fieldNum = 1; fieldNum <= primaryVarsToExport.giveSize(); fieldNum++ ) {
393 UnknownType type = ( UnknownType ) primaryVarsToExport.at(fieldNum);
394
395 for ( int nodeInd = 1; nodeInd <= numTotalNodes; nodeInd++ ) {
396
397 if ( type == DisplacementVector ) { // compute displacement
398
399 FloatArray u = {0.0, 0.0, 0.0};
400
401 // Fetch global coordinates (in undeformed configuration)
402 const FloatArray &x = nodeCoords[nodeInd-1];
403
404 // Compute local coordinates
405 FloatArray locCoord;
406 computeLocalCoordinates(locCoord, x);
407
408 // Compute displacement in point
409 FloatMatrix NMatrix;
410 computeNmatrixAt(locCoord, NMatrix);
411 FloatArray solVec;
412 computeVectorOf(VM_Total, tStep, solVec);
413 FloatArray uTemp;
414 uTemp.beProductOf(NMatrix, solVec);
415
416 if(uTemp.giveSize() == 3) {
417 u = uTemp;
418 }
419 else {
420 u = {uTemp[0], uTemp[1], 0.0};
421 }
422
423 vtkPieces[0].setPrimaryVarInNode(type, nodeInd, u);
424 } else {
425 printf("fieldNum: %d\n", fieldNum);
426 // TODO: Implement
427// ZZNodalRecoveryMI_recoverValues(values, layer, ( InternalStateType ) 1, tStep); // does not work well - fix
428// for ( int j = 1; j <= numCellNodes; j++ ) {
429// vtkPiece.setPrimaryVarInNode(fieldNum, nodeNum, values [ j - 1 ]);
430// nodeNum += 1;
431// }
432 }
433 }
434 }
435
436
437 // Export nodal variables from internal fields
438 vtkPieces[0].setNumberOfInternalVarsToExport(internalVarsToExport, numTotalNodes);
439
440
441 // Export cell variables
442 vtkPieces[0].setNumberOfCellVarsToExport(cellVarsToExport, 1);
443 for ( int i = 1; i <= cellVarsToExport.giveSize(); i++ ) {
444 InternalStateType type = ( InternalStateType ) cellVarsToExport.at(i);
445 FloatArray average;
446 std :: unique_ptr< IntegrationRule > &iRule = integrationRulesArray [ 0 ];
447 VTKXMLExportModule :: computeIPAverage(average, iRule.get(), this, type, tStep);
448 if(average.giveSize() == 0) {
449 average = {0., 0., 0., 0., 0., 0.};
450 }
451
452 if(average.giveSize() == 1) {
453 vtkPieces[0].setCellVar( type, 1, average );
454 }
455
456 if(average.giveSize() == 6) {
457 FloatArray averageV9(9);
458 averageV9.at(1) = average.at(1);
459 averageV9.at(5) = average.at(2);
460 averageV9.at(9) = average.at(3);
461 averageV9.at(6) = averageV9.at(8) = average.at(4);
462 averageV9.at(3) = averageV9.at(7) = average.at(5);
463 averageV9.at(2) = averageV9.at(4) = average.at(6);
464
465 vtkPieces[0].setCellVar( type, 1, averageV9 );
466 }
467 }
468
469
470 // Export of XFEM related quantities
471 if ( domain->hasXfemManager() ) {
472 XfemManager *xMan = domain->giveXfemManager();
473
474 int nEnrIt = xMan->giveNumberOfEnrichmentItems();
475 vtkPieces[0].setNumberOfInternalXFEMVarsToExport(xMan->vtkExportFields.giveSize(), nEnrIt, numTotalNodes);
476
477 const int nDofMan = giveNumberOfDofManagers();
478
479
480 for ( int field = 1; field <= xMan->vtkExportFields.giveSize(); field++ ) {
481 XFEMStateType xfemstype = ( XFEMStateType ) xMan->vtkExportFields [ field - 1 ];
482
483 for ( int enrItIndex = 1; enrItIndex <= nEnrIt; enrItIndex++ ) {
484 EnrichmentItem *ei = xMan->giveEnrichmentItem(enrItIndex);
485 for ( int nodeInd = 1; nodeInd <= numTotalNodes; nodeInd++ ) {
486
487 const FloatArray &x = nodeCoords[nodeInd-1];
488 FloatArray locCoord;
489 computeLocalCoordinates(locCoord, x);
490
493 interp->evalN( N, locCoord, FEIElementGeometryWrapper(this) );
494
495
496 if ( xfemstype == XFEMST_LevelSetPhi ) {
497 double levelSet = 0.0, levelSetInNode = 0.0;
498
499 for(int elNodeInd = 1; elNodeInd <= nDofMan; elNodeInd++) {
500 DofManager *dMan = giveDofManager(elNodeInd);
501 ei->evalLevelSetNormalInNode(levelSetInNode, dMan->giveGlobalNumber(), dMan->giveCoordinates() );
502
503 levelSet += N.at(elNodeInd)*levelSetInNode;
504 }
505
506
507 FloatArray valueArray = {levelSet};
508 vtkPieces[0].setInternalXFEMVarInNode(field, enrItIndex, nodeInd, valueArray);
509
510 } else if ( xfemstype == XFEMST_LevelSetGamma ) {
511 double levelSet = 0.0, levelSetInNode = 0.0;
512
513 for(int elNodeInd = 1; elNodeInd <= nDofMan; elNodeInd++) {
514 DofManager *dMan = giveDofManager(elNodeInd);
515 ei->evalLevelSetTangInNode(levelSetInNode, dMan->giveGlobalNumber(), dMan->giveCoordinates() );
516
517 levelSet += N.at(elNodeInd)*levelSetInNode;
518 }
519
520
521 FloatArray valueArray = {levelSet};
522 vtkPieces[0].setInternalXFEMVarInNode(field, enrItIndex, nodeInd, valueArray);
523
524 } else if ( xfemstype == XFEMST_NodeEnrMarker ) {
525 double nodeEnrMarker = 0.0, nodeEnrMarkerInNode = 0.0;
526
527 for(int elNodeInd = 1; elNodeInd <= nDofMan; elNodeInd++) {
528 DofManager *dMan = giveDofManager(elNodeInd);
529 ei->evalNodeEnrMarkerInNode(nodeEnrMarkerInNode, dMan->giveGlobalNumber() );
530
531 nodeEnrMarker += N.at(elNodeInd)*nodeEnrMarkerInNode;
532 }
533
534
535 FloatArray valueArray = {nodeEnrMarker};
536 vtkPieces[0].setInternalXFEMVarInNode(field, enrItIndex, nodeInd, valueArray);
537 }
538
539 }
540 }
541 }
542 }
543
544 }
545 else {
546 // Enriched and cut element
547
548 XfemStructuralElementInterface::giveSubtriangulationCompositeExportData(vtkPieces, primaryVarsToExport, internalVarsToExport, cellVarsToExport, tStep);
549
550
551 }
552
553}
554
555} /* namespace oofem */
#define N(a, b)
#define REGISTER_Element(class)
int giveGlobalNumber() const
Definition dofmanager.h:515
const FloatArray & giveCoordinates() const
Definition dofmanager.h:390
void setField(int item, InputFieldType id)
void computeVectorOf(ValueModeType u, TimeStep *tStep, FloatArray &answer)
Definition element.C:103
std::vector< std ::unique_ptr< IntegrationRule > > integrationRulesArray
Definition element.h:157
virtual int giveNumberOfDofManagers() const
Definition element.h:695
DofManager * giveDofManager(int i) const
Definition element.C:553
virtual bool computeLocalCoordinates(FloatArray &answer, const FloatArray &gcoords)
Definition element.C:1240
bool evalNodeEnrMarkerInNode(double &oNodeEnrMarker, int iNodeInd) const
bool evalLevelSetTangInNode(double &oLevelSet, int iNodeInd, const FloatArray &iGlobalCoord) const
bool isDofManEnriched(const DofManager &iDMan) const
virtual void computeEnrichedDofManDofIdArray(IntArray &oDofIdArray, DofManager &iDMan)
bool evalLevelSetNormalInNode(double &oLevelSet, int iNodeInd, const FloatArray &iGlobalCoord) const
Domain * giveDomain() const
Definition femcmpnn.h:97
Domain * domain
Link to domain object, useful for communicating with other FEM components.
Definition femcmpnn.h:79
int giveNumber() const
Definition femcmpnn.h:104
double & at(Index i)
Definition floatarray.h:202
Index giveSize() const
Returns the size of receiver.
Definition floatarray.h:261
void beProductOf(const FloatMatrix &aMatrix, const FloatArray &anArray)
Definition floatarray.C:689
void subtract(const FloatArray &src)
Definition floatarray.C:320
int giveNumberOfRows() const
Returns number of rows of receiver.
void followedBy(const IntArray &b, int allocChunk=0)
Definition intarray.C:94
int & at(std::size_t i)
Definition intarray.h:104
int giveSize() const
Definition intarray.h:211
int giveNumberOfIntegrationPoints() const
int giveIPValue(FloatArray &answer, GaussPoint *gp, InternalStateType type, TimeStep *tStep) override
std::unique_ptr< FloatArray > initialDisplacements
Initial displacement vector, describes the initial nodal displacements when element has been casted.
static ParamKey IPK_TrPlaneStress2dXFEM_RegCoeff
void computeNmatrixAt(const FloatArray &iLocCoord, FloatMatrix &answer) override
void computeStiffnessMatrix(FloatMatrix &answer, MatResponseMode rMode, TimeStep *tStep) override
static ParamKey IPK_TrPlaneStress2dXFEM_RegCoeffTol
static FEI2dTrLin interp
Definition trplanstrss.h:70
FEInterpolation * giveInterpolation() const override
Definition trplanstrss.C:70
void XfemElementInterface_createEnrBmatrixAt(FloatMatrix &oAnswer, GaussPoint &iGP, Element &iEl)
Creates enriched B-matrix.
void XfemElementInterface_createEnrBHmatrixAt(FloatMatrix &oAnswer, GaussPoint &iGP, Element &iEl)
Creates enriched BH-matrix.
void XfemElementInterface_createEnrNmatrixAt(FloatMatrix &oAnswer, const FloatArray &iLocCoord, Element &iEl, bool iSetDiscontContribToZero)
Creates enriched N-matrix.
bool isElementEnriched(const Element *elem)
IntArray vtkExportFields
const std ::vector< int > & giveNodeEnrichmentItemIndices(int iNodeIndex) const
EnrichmentItem * giveEnrichmentItem(int n)
int giveNumberOfEnrichmentItems() const
virtual void XfemElementInterface_computeDeformationGradientVector(FloatArray &answer, GaussPoint *gp, TimeStep *tStep)
void giveSubtriangulationCompositeExportData(std ::vector< ExportRegion > &vtkPieces, IntArray &primaryVarsToExport, IntArray &internalVarsToExport, IntArray cellVarsToExport, TimeStep *tStep)
VTK Interface.
bool XfemElementInterface_updateIntegrationRule() override
Updates integration rule based on the triangulation.
XFEMStateType
Definition xfemmanager.h:92
@ XfemElementInterfaceType
@ VTKXMLExportModuleElementInterfaceType
oofem::oofegGraphicContext gc[OOFEG_LAST_LAYER]
#define PM_UPDATE_PARAMETER(_val, _pm, _ir, _componentnum, _paramkey, _prio)
#define _IFT_TrPlaneStress2dXFEM_RegCoeffTol
#define _IFT_TrPlaneStress2dXFEM_RegCoeff

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