ScaleCoupledElement.h
Go to the documentation of this file.
1 // This file is part of the MercuryDPM project (https://www.mercurydpm.org).
2 // Copyright (c), The MercuryDPM Developers Team. All rights reserved.
3 // License: BSD 3-Clause License; see the LICENSE file in the root directory.
4 
5 #ifndef SCALECOUPLEDELEMENT_H
6 #define SCALECOUPLEDELEMENT_H
7 #include "Logger.h"
8 #include "generic.h"
9 //#include "Oomph/Coupling/CoupledSolidNodes.h"
10 
11 namespace oomph
12 {
13 
15 template<class ELEMENT>
16 class ScaleCoupledElement : public ELEMENT {
17 public:
18 
21  coupling_residual.clear();
22  }
23 
26  coupling_residual = residual;
27  }
28 
30  double get_coupling_residual(const unsigned& n, const unsigned& d)
31  {
32  return coupling_residual.empty()?0.0:coupling_residual[n][d];
33  }
34 
37  coupling_jacobian.clear();
38  }
39 
42  coupling_jacobian = jacobian;
43  }
44 
47  coupling_weight = weight;
48  }
49 
51  double get_coupling_weight(const unsigned& n) {
52  return coupling_weight.empty()?0.0:coupling_weight[n];
53  }
54 
57  {
58  // Call the generic residuals function with flag set to 0 using a dummy matrix argument
60 
61  // Add nodal coupling force to the residuals
63 
64  //if (!coupling_residual.empty()) logger(INFO,"fill_in_contribution_to_residuals, element %", this);
65  }
66 
69  {
70  //Call the generic routine with the flag set to 1
72 
73  // Add nodal coupling force to the residuals
74  add_internal_coupling_forces_to_residuals(residuals,jacobian,1);
75 
76  //if (!coupling_residual.empty()) logger(INFO,"fill_in_contribution_to_jacobian, element %", this);
77  }
78 
81  DenseMatrix<double> &jacobian,
82  const unsigned& flag) override
83  {
84 
85  // Get problem dimension
86  const unsigned DIM = this->dim();
87 
88  // Simply set up initial condition?
89  if (this->Solid_ic_pt!=0)
90  {
91  this->fill_in_residuals_for_solid_ic(residuals);
92  return;
93  }
94 
95  //Find out how many nodes there are
96  const unsigned n_node = this->nnode();
97 
98  //Find out how many positional dofs there are
99  const unsigned n_position_type = this->nnodal_position_type();
100 
101  //Set up memory for the shape functions
102  Shape psi(n_node,n_position_type);
103  DShape dpsidxi(n_node,n_position_type,DIM);
104 
105  //Set the value of Nintpt -- the number of integration points
106  const unsigned n_intpt = this->integral_pt()->nweight();
107 
108  //Set the vector to hold the local coordinates in the element
110 
111  // Timescale ratio (non-dim density)
112  double lambda_sq = this->lambda_sq();
113 
114  // Time factor
115  double time_factor=0.0;
116  if (lambda_sq>0)
117  {
118  time_factor=this->node_pt(0)->position_time_stepper_pt()->weight(2,0);
119  }
120 
121  //Integer to store the local equation number
122  int local_eqn=0;
123 
124  //Loop over the integration points
125  for(unsigned ipt=0;ipt<n_intpt;ipt++)
126  {
127  //Assign the values of s
128  for(unsigned i=0;i<DIM;++i) {s[i] = this->integral_pt()->knot(ipt,i);}
129 
130  //Get the integral weight
131  double w = this->integral_pt()->weight(ipt);
132 
133  //Call the derivatives of the shape functions (and get Jacobian)
134  double J = this->dshape_lagrangian_at_knot(ipt,psi,dpsidxi);
135 
136  //Get the coupling weight at the integration point
137  double coupling_w = 0;
138  if (!coupling_weight.empty()) {
139  for (unsigned l = 0; l < n_node; l++) {
140  double psi_ = psi(l);
141  coupling_w += psi_ * coupling_weight[l];
142  }
143  }
144 
145  //Calculate interpolated values of the derivative of global position
146  //wrt lagrangian coordinates
147  DenseMatrix<double> interpolated_G(DIM);
148 
149  // Setup memory for accelerations
150  Vector<double> accel(DIM);
151 
152  //Initialise to zero
153  for(unsigned i=0;i<DIM;i++)
154  {
155  // Initialise acclerations
156  accel[i]=0.0;
157  for(unsigned j=0;j<DIM;j++)
158  {
159  interpolated_G(i,j) = 0.0;
160  }
161  }
162 
163  //Storage for Lagrangian coordinates (initialised to zero)
164  Vector<double> interpolated_xi(DIM,0.0);
165 
166  //Calculate displacements and derivatives and lagrangian coordinates
167  for(unsigned l=0;l<n_node;l++)
168  {
169  //Loop over positional dofs
170  for(unsigned k=0;k<n_position_type;k++)
171  {
172  double psi_ = psi(l,k);
173  //Loop over displacement components (deformed position)
174  for(unsigned i=0;i<DIM;i++)
175  {
176  //Calculate the Lagrangian coordinates and the accelerations
177  interpolated_xi[i] += this->lagrangian_position_gen(l,k,i)*psi_;
178 
179  // Only compute accelerations if inertia is switched on
180  if ((lambda_sq>0.0)&&(this->Unsteady))
181  {
182  accel[i] += this->dnodal_position_gen_dt(2,l,k,i)*psi_;
183  }
184 
185  //Loop over derivative directions
186  for(unsigned j=0;j<DIM;j++)
187  {
188  interpolated_G(j,i) +=
189  this->nodal_position_gen(l,k,i)*dpsidxi(l,k,j);
190  }
191  }
192  }
193  }
194 
195  //Get isotropic growth factor
196  double gamma=1.0;
197  this->get_isotropic_growth(ipt,s,interpolated_xi,gamma);
198 
199 
200  //Get body force at current time
202  this->body_force(interpolated_xi,b);
203 
204  // We use Cartesian coordinates as the reference coordinate
205  // system. In this case the undeformed metric tensor is always
206  // the identity matrix -- stretched by the isotropic growth
207  double diag_entry=pow(gamma,2.0/double(DIM));
209  for(unsigned i=0;i<DIM;i++)
210  {
211  for(unsigned j=0;j<DIM;j++)
212  {
213  if(i==j) {g(i,j) = diag_entry;}
214  else {g(i,j) = 0.0;}
215  }
216  }
217 
218  //Premultiply the undeformed volume ratio (from the isotropic
219  // growth), the integral weights, the coupling weights, and the Jacobian
220  double W = gamma*w*(1.0-coupling_w)*J;
221 
222  //Declare and calculate the deformed metric tensor
224 
225  //Assign values of G
226  for(unsigned i=0;i<DIM;i++)
227  {
228  //Do upper half of matrix
229  for(unsigned j=i;j<DIM;j++)
230  {
231  //Initialise G(i,j) to zero
232  G(i,j) = 0.0;
233  //Now calculate the dot product
234  for(unsigned k=0;k<DIM;k++)
235  {
236  G(i,j) += interpolated_G(i,k)*interpolated_G(j,k);
237  }
238  }
239  //Matrix is symmetric so just copy lower half
240  for(unsigned j=0;j<i;j++)
241  {
242  G(i,j) = G(j,i);
243  }
244  }
245 
246  //Now calculate the stress tensor from the constitutive law
248  ELEMENT::get_stress(g,G,sigma);
249 
250  // Add pre-stress
251  for(unsigned i=0;i<DIM;i++)
252  {
253  for(unsigned j=0;j<DIM;j++)
254  {
255  sigma(i,j) += this->prestress(i,j,interpolated_xi);
256  }
257  }
258 
259  // Get stress derivative by FD only needed for Jacobian
260  //-----------------------------------------------------
261 
262  // Stress derivative
263  RankFourTensor<double> d_stress_dG(DIM,DIM,DIM,DIM,0.0);
264  // Derivative of metric tensor w.r.t. to nodal coords
265  RankFiveTensor<double> d_G_dX(n_node,n_position_type,DIM,DIM,DIM,0.0);
266 
267  // Get Jacobian too?
268  if (flag==1)
269  {
270  // Derivative of metric tensor w.r.t. to discrete positional dofs
271  // NOTE: Since G is symmetric we only compute the upper triangle
272  // and DO NOT copy the entries across. Subsequent computations
273  // must (and, in fact, do) therefore only operate with upper
274  // triangular entries
275  for (unsigned ll=0;ll<n_node;ll++)
276  {
277  for (unsigned kk=0;kk<n_position_type;kk++)
278  {
279  for (unsigned ii=0;ii<DIM;ii++)
280  {
281  for (unsigned aa=0;aa<DIM;aa++)
282  {
283  for (unsigned bb=aa;bb<DIM;bb++)
284  {
285  d_G_dX(ll,kk,ii,aa,bb)=
286  interpolated_G(aa,ii)*dpsidxi(ll,kk,bb)+
287  interpolated_G(bb,ii)*dpsidxi(ll,kk,aa);
288  }
289  }
290  }
291  }
292  }
293 
294  //Get the "upper triangular" entries of the derivatives of the stress
295  //tensor with respect to G
296  this->get_d_stress_dG_upper(g,G,sigma,d_stress_dG);
297  }
298 
299 //=====EQUATIONS OF ELASTICITY FROM PRINCIPLE OF VIRTUAL DISPLACEMENTS========
300 
301  //Loop over the test functions, nodes of the element
302  for(unsigned l=0;l<n_node;l++)
303  {
304  //Loop of types of dofs
305  for(unsigned k=0;k<n_position_type;k++)
306  {
307  // Offset for faster access
308  const unsigned offset5=dpsidxi.offset(l ,k);
309 
310  //Loop over the displacement components
311  for(unsigned i=0;i<DIM;i++)
312  {
313  //Get the equation number
314  local_eqn = this->position_local_eqn(l,k,i);
315 
316  /*IF it's not a boundary condition*/
317  if(local_eqn >= 0)
318  {
319  //Initialise contribution to sum
320  double sum=0.0;
321 
322  // Acceleration and body force
323  sum+=(lambda_sq*accel[i]-b[i])*psi(l,k);
324 
325  // Stress term
326  for(unsigned a=0;a<DIM;a++)
327  {
328  unsigned count=offset5;
329  for(unsigned b=0;b<DIM;b++)
330  {
331  //Add the stress terms to the residuals
332  sum+=sigma(a,b)*interpolated_G(a,i)*
333  dpsidxi.raw_direct_access(count);
334  ++count;
335  }
336  }
337  residuals[local_eqn] += W*sum;
338 
339  // Get Jacobian too?
340  if (flag==1)
341  {
342  // Offset for faster access in general stress loop
343  const unsigned offset1=d_G_dX.offset( l, k, i);
344 
345  //Loop over the nodes of the element again
346  for(unsigned ll=0;ll<n_node;ll++)
347  {
348  //Loop of types of dofs again
349  for(unsigned kk=0;kk<n_position_type;kk++)
350  {
351  //Loop over the displacement components again
352  for(unsigned ii=0;ii<DIM;ii++)
353  {
354  //Get the number of the unknown
355  int local_unknown = this->position_local_eqn(ll,kk,ii);
356 
357  /*IF it's not a boundary condition*/
358  if(local_unknown >= 0)
359  {
360  // Offset for faster access in general stress loop
361  const unsigned offset2=d_G_dX.offset( ll, kk, ii);
362  const unsigned offset4=dpsidxi.offset(ll, kk);
363 
364  // General stress term
365  //--------------------
366  double sum=0.0;
367  unsigned count1=offset1;
368  for(unsigned a=0;a<DIM;a++)
369  {
370  // Bump up direct access because we're only
371  // accessing upper triangle
372  count1+=a;
373  for(unsigned b=a;b<DIM;b++)
374  {
375  double factor=d_G_dX.raw_direct_access(count1);
376  if (a==b) factor*=0.5;
377 
378  // Offset for faster access
379  unsigned offset3=d_stress_dG.offset(a,b);
380  unsigned count2=offset2;
381  unsigned count3=offset3;
382 
383  for(unsigned aa=0;aa<DIM;aa++)
384  {
385  // Bump up direct access because we're only
386  // accessing upper triangle
387  count2+=aa;
388  count3+=aa;
389 
390  // Only upper half of derivatives w.r.t. symm tensor
391  for(unsigned bb=aa;bb<DIM;bb++)
392  {
393  sum+=factor*
394  d_stress_dG.raw_direct_access(count3)*
395  d_G_dX.raw_direct_access(count2);
396  ++count2;
397  ++count3;
398  }
399  }
400  ++count1;
401  }
402 
403  }
404 
405  // Multiply by weight and add contribution
406  // (Add directly because this bit is nonsymmetric)
407  jacobian(local_eqn,local_unknown)+=sum*W;
408 
409  // Only upper triangle (no separate test for bc as
410  // local_eqn is already nonnegative)
411  if((i==ii) && (local_unknown >= local_eqn))
412  {
413  //Initialise contribution
414  double sum=0.0;
415 
416  // Inertia term
417  sum+=lambda_sq*time_factor*psi(ll,kk)*psi(l,k);
418 
419  // Stress term
420  unsigned count4=offset4;
421  for(unsigned a=0;a<DIM;a++)
422  {
423  //Cache term
424  const double factor=
425  dpsidxi.raw_direct_access(count4);// ll ,kk
426  ++count4;
427 
428  unsigned count5=offset5;
429  for(unsigned b=0;b<DIM;b++)
430  {
431  sum+=sigma(a,b)*factor*
432  dpsidxi.raw_direct_access(count5); // l ,k
433  ++count5;
434  }
435  }
436  //Add contribution to jacobian
437  jacobian(local_eqn,local_unknown) += sum*W;
438  //Add to lower triangular section
439  if(local_eqn != local_unknown)
440  {
441  jacobian(local_unknown,local_eqn) += sum*W;
442  }
443  }
444 
445  } //End of if not boundary condition
446  }
447  }
448  }
449  }
450 
451  } //End of if not boundary condition
452 
453  } //End of loop over coordinate directions
454  } //End of loop over type of dof
455  } //End of loop over shape functions
456  } //End of loop over integration points
457  }
458 
461  DenseMatrix<double> &jacobian,
462  const unsigned& flag)
463  {
464  // No further action if no coupling forces
465  if (coupling_residual.empty()) return;
466 
467  // Find out how many nodes there are
468  const unsigned n_node = this->nnode();
469  const unsigned DIM = this->dim();
470 
471  // Find out how many positional dofs there are
472  unsigned n_position_type = this->nnodal_position_type();
473 
474  // Set up memory for the shape/test functions
475  Shape psi(n_node);
476 
477  // Loop over all nodes belonging to the element
478  for(unsigned l=0;l<n_node;l++)
479  {
480  // Loop of types of dofs
481  for(unsigned k=0; k<n_position_type; k++)
482  {
483  // Loop over the force components
484  for(unsigned i=0; i<DIM; i++)
485  {
486  // Get the local equation
487  int local_eqn = this->position_local_eqn(l,k,i);
488  // IF it's not a boundary condition and the interpolated force is nonzero
489  if (local_eqn >= 0)
490  {
491  // add the nodal coupling residual to the global residual vector
492  residuals[local_eqn] += coupling_residual[l][i];
493  }
494  // Get Jacobian too?
495  if (flag==1)
496  {
497  // No further action if no coupling forces
498  if (coupling_jacobian.empty()) continue;
499 
500  // Loop over the nodes of the element again
501  for(unsigned ll=0;ll<n_node;ll++)
502  {
503  // Loop of types of dofs again
504  for(unsigned kk=0;kk<n_position_type;kk++)
505  {
506  // Loop over the force components again
507  for(unsigned ii=0;ii<DIM;ii++)
508  {
509  // Get the number of the unknown
510  int local_unknown = this->position_local_eqn(ll,kk,ii);
511  // IF it's not a boundary condition
512  if ((local_unknown >= 0) && (i==ii) && (local_unknown >= local_eqn))
513  {
514  // add the nodal coupling jacobian to the global jacobian matrix
515  jacobian(local_eqn,local_unknown) += coupling_jacobian[l][ll];
516  if(local_eqn != local_unknown)
517  {
518  jacobian(local_unknown,local_eqn) += coupling_jacobian[l][ll];
519  }
520  }
521  }
522  }
523  }
524  }
525  }
526  }
527  }
528  }
529 
530 private:
532  // For comparison residuals[local_eqn] +=gamma*w*(1.0-coupling_w)*J*b[i]*psi(nn,d);
533  // penalty_ * psi(nn) * psi(n) * w * J * nodalVelocityDifference[n][d] * time_pt()->dt(0)
535  // Force on particle:
536  // force[d] += projectionMatrix[n][p] * el_pt->get_coupling_residual(n, d);
537 
540 
543 };
544 
545 
547 
548 }
549 
550 #endif //SCALECOUPLEDELEMENT_H
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
JacobiRotation< float > G
Definition: Jacobi_makeGivens.cpp:2
JacobiRotation< float > J
Definition: Jacobi_makeJacobi.cpp:3
RowVector3d w
Definition: Matrix_resize_int.cpp:3
Scalar * b
Definition: benchVecAdd.cpp:17
Definition: shape.h:278
unsigned offset(const unsigned long &i, const unsigned long &j) const
Definition: shape.h:487
double & raw_direct_access(const unsigned long &i)
Definition: shape.h:469
static DenseMatrix< double > Dummy_matrix
Definition: elements.h:227
A Rank 5 Tensor class.
Definition: matrices.h:2113
T & raw_direct_access(const unsigned long &i)
Definition: matrices.h:2545
unsigned offset(const unsigned long &i, const unsigned long &j, const unsigned long &k) const
Definition: matrices.h:2564
A Rank 4 Tensor class.
Definition: matrices.h:1701
T & raw_direct_access(const unsigned long &i)
Definition: matrices.h:2078
unsigned offset(const unsigned long &i, const unsigned long &j) const
Definition: matrices.h:2096
Wrapper class for solid elements to be coupled with discrete solid particles on the surfaces.
Definition: ScaleCoupledElement.h:16
void set_coupling_jacobian(Vector< Vector< double >> &jacobian)
Sets the coupling residual.
Definition: ScaleCoupledElement.h:41
void add_internal_coupling_forces_to_residuals(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Add the point source contribution to the residual vector.
Definition: ScaleCoupledElement.h:460
double get_coupling_weight(const unsigned &n)
Sets the coupling weight.
Definition: ScaleCoupledElement.h:51
Vector< Vector< double > > coupling_jacobian
Contribution of coupling to jacobian (jacobian(local_eqn,local_unknown) += coupling_jacobian[node0][n...
Definition: ScaleCoupledElement.h:539
void clear_coupling_residual()
Empties the coupling residual.
Definition: ScaleCoupledElement.h:20
void fill_in_contribution_to_residuals(Vector< double > &residuals) override
Adds the elements contribution to its residual vector (wrapper)
Definition: ScaleCoupledElement.h:56
void set_coupling_weight(Vector< double > &weight)
Sets the coupling weight.
Definition: ScaleCoupledElement.h:46
Vector< Vector< double > > coupling_residual
Contribution of coupling to residual (residuals[local_eqn] += coupling_residual[node][dim];)
Definition: ScaleCoupledElement.h:534
void set_coupling_residual(Vector< Vector< double >> &residual)
Sets the coupling residual.
Definition: ScaleCoupledElement.h:25
void fill_in_generic_contribution_to_residuals_pvd(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag) override
Definition: ScaleCoupledElement.h:80
double get_coupling_residual(const unsigned &n, const unsigned &d)
Returns the coupling residual.
Definition: ScaleCoupledElement.h:30
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian) override
Add the elements contribution to its residual vector and Jacobian matrix (wrapper)
Definition: ScaleCoupledElement.h:68
Vector< double > coupling_weight
Nodal coupling weight (interpolated to compute coupling weight at integration points)
Definition: ScaleCoupledElement.h:542
void clear_coupling_jacobian()
Empties the coupling Jacobian.
Definition: ScaleCoupledElement.h:36
Definition: shape.h:76
Definition: oomph-lib/src/generic/Vector.h:58
RealScalar s
Definition: level1_cplx_impl.h:130
const Scalar * a
Definition: level2_cplx_impl.h:32
char char char int int * k
Definition: level2_impl.h:374
#define DIM
Definition: linearised_navier_stokes_elements.h:44
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 pow(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:625
void body_force(const double &time, const Vector< double > &x, Vector< double > &result)
Definition: axisym_linear_elasticity/cylinder/cylinder.cc:96
int sigma
Definition: calibrate.py:179
Mdouble gamma(Mdouble gamma_in)
This is the gamma function returns the true value for the half integer value.
Definition: ExtendedMath.cc:116
@ W
Definition: quadtree.h:63
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
Definition: AnisotropicHookean.h:10
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2