2069
2108
DIAG_MAT D(column(coefsAtIPs,0));
2072
2110
D *= _fweights_;
2073
2111
_Nmat_ = _fN_.transMat(D*_fN_);
2074
2112
for (int i = 0; i < nNodesPerElement_; ++i) {
2075
2113
int inode = _conn_(i);
2076
2114
for (int j = 0; j < nNodesPerElement_; ++j) {
2077
2115
int jnode = _conn_(j);
2078
tangent.add(inode, jnode, _Nmat_(i,j));
2083
// assemble partial result matrices
2085
sparse_allsum(communicator_,tangent);
2087
LammpsInterface::instance()->sparse_allsum(tangent);
2116
K.add(inode, jnode, _Nmat_(i,j));
2121
// assemble partial result matrices
2123
sparse_allsum(communicator_,K);
2125
LammpsInterface::instance()->sparse_allsum(K);
2130
//-----------------------------------------------------------------
2131
// Robin boundary flux using native quadrature
2132
// integrate \int_delV _N_I s(x,t).n dA
2133
//-----------------------------------------------------------------
2134
void FE_Engine::add_open_fluxes(const Array2D<bool> &rhsMask,
2135
const FIELDS & fields,
2136
const OPEN_SURFACE & openFaces,
2137
FIELDS &nodalSources,
2138
const FieldName Velocity) const
2141
// sizing working arrays
2142
DENS_MAT xCoords(nSD_,nNodesPerElement_);
2143
DENS_MAT faceSource;
2144
DENS_MAT localField;
2145
DENS_MAT xAtIPs(nSD_,nIPsPerFace_);
2146
DENS_MAT uAtIPs; // (nIPsPerFace_,field nCols);
2147
DENS_MAT aAtIPs(nIPsPerFace_,1);
2148
FIELDS myNodalSources;
2150
// throw error if electron velocity is not defined
2152
// element wise assembly
2153
OPEN_SURFACE::const_iterator face_iter;
2154
for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++)
2156
_fieldName_ = face_iter->first;
2157
if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue;
2158
DENS_MAT & s(nodalSources[_fieldName_].set_quantity());
2159
myNodalSources[_fieldName_] = DENS_MAT(s.nRows(),s.nCols());
2161
for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++)
2163
_fieldName_ = face_iter->first;
2164
if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue;
2166
typedef set<PAIR> FSET;
2167
const FSET *fset = (const FSET *)&(face_iter->second);
2168
FSET::const_iterator fset_iter;
2169
for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++)
2171
const PAIR &face = *fset_iter;
2172
const int elem = face.first;
2173
// if this is not our element, do not do calculations
2174
if (!feMesh_->is_owned_elt(elem)) continue;
2175
// get velocity, multiply with field (vector gives tensor)
2176
// dot with face normal
2177
_conn_ = feMesh_->element_connectivity_unique(elem);
2178
// evaluate location at ips
2179
feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_);
2180
// collect field at ips
2181
_fieldItr_ = fields.find(_fieldName_);
2182
const DENS_MAT & field = (_fieldItr_->second).quantity();
2183
feMesh_->element_field(elem, field, localField);
2184
int nFieldDOF = field.nCols();
2185
// "u" is the quantity being advected
2186
uAtIPs.reset(nIPsPerFace_,nFieldDOF);
2187
uAtIPs = _fN_*localField;
2188
// collect velocity at ips for "advection" = v.n
2189
_fieldItr_ = fields.find(Velocity);
2190
const DENS_MAT & advection = (_fieldItr_->second).quantity(); // advection velocity
2191
feMesh_->element_field(elem, advection, localField);
2192
for (int j = 0; j < nSD_; ++j) { // nSD_ == nDOF for the velocity
2193
for (int ip = 0; ip < nIPsPerFace_; ++ip) {
2194
for (int I = 0; I < nNodesPerElement_; ++I) {
2195
aAtIPs(ip,0) += (_nN_[j])(ip,I)*localField(I,j);
2199
// construct open flux at ips of this element
2200
// flux = field \otimes advection_vector \dot n
2201
faceSource.reset(nIPsPerFace_,nFieldDOF);
2202
for (int ip = 0; ip < nIPsPerFace_; ++ip) {
2203
for (int idof = 0; idof<nFieldDOF; ++idof) {
2204
// multiply field DOF value times advection vector
2205
faceSource(ip,idof) = aAtIPs(ip,1)*uAtIPs(ip,idof);//(v.n) u
2208
// assemble contributions for this direction of the face normal
2209
// _nN_[j](ip,I) nodal shapefunction(I) at ip X face normal(j)
2210
// _fweights_ diagonal nips X nips ==> facesource is nips X ndofs
2211
DENS_MAT & s(myNodalSources[_fieldName_].set_quantity());
2212
_Nmat_ = _fN_.transMat(_fweights_*faceSource);
2213
// s_Ii = \sum_ip N_I (u_i v.n)_ip wg_ip
2214
for (int i = 0; i < nNodesPerElement_; ++i) {
2215
int inode = _conn_(i);
2216
for (int idof = 0; idof < nFieldDOF; ++idof) {
2217
s(inode,idof) += _Nmat_(i,idof);
2222
// assemble partial result matrices
2223
for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++) {
2224
_fieldName_ = face_iter->first;
2225
if (!rhsMask((int) _fieldName_,OPEN_SOURCE)) continue;
2226
DENS_MAT & s(myNodalSources[_fieldName_].set_quantity());
2227
allsum(communicator_,MPI_IN_PLACE, s.ptr(), s.size());
2228
DENS_MAT & src(nodalSources[_fieldName_].set_quantity());
2233
//-----------------------------------------------------------------
2234
// Open boundary flux stiffness using native quadrature
2235
// integrate \int_delV _N_I ds/du(x,t).n dA
2236
//-----------------------------------------------------------------
2237
void FE_Engine::add_open_tangent(const Array2D<bool> &rhsMask,
2238
const FIELDS & fields,
2239
const OPEN_SURFACE & openFaces,
2241
const FieldName Velocity) const
2244
// sizing working arrays
2245
DENS_MAT xCoords(nSD_,nNodesPerElement_);
2246
DENS_MAT faceSource;
2247
DENS_MAT localField;
2248
DENS_MAT xAtIPs(nSD_,nIPsPerFace_);
2249
DENS_MAT uAtIPs; // (nIPsPerFace_,field nCols);
2250
DENS_MAT aAtIPs(nIPsPerFace_,nSD_);
2251
SPAR_MAT K(tangent.nRows(), tangent.nCols());
2252
// element wise assembly
2253
OPEN_SURFACE::const_iterator face_iter;
2254
for (face_iter=openFaces.begin(); face_iter!=openFaces.end(); face_iter++)
2256
_fieldName_ = face_iter->first;
2257
if (!rhsMask((int)_fieldName_,OPEN_SOURCE)) continue;
2258
bool convective = false;
2259
if (_fieldName_ == Velocity) convective = true;
2261
typedef set<PAIR> FSET;
2262
const FSET *fset = (const FSET *)&(face_iter->second);
2263
FSET::const_iterator fset_iter;
2264
for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++)
2266
const PAIR &face = *fset_iter;
2267
const int elem = face.first;
2268
// if this is not our element, do not do calculations
2269
if (!feMesh_->is_owned_elt(elem)) continue;
2270
_conn_ = feMesh_->element_connectivity_unique(elem);
2271
// evaluate location at ips
2272
feMesh_->face_shape_function(face, _fN_, _fdN_, _nN_, _fweights_);
2273
// collect field at ips
2274
_fieldItr_ = fields.find(_fieldName_);
2275
const DENS_MAT & field = (_fieldItr_->second).quantity();
2276
feMesh_->element_field(elem, field, localField);
2277
int nFieldDOF = field.nCols();
2278
// "u" is the quantity being advected
2279
uAtIPs.reset(nIPsPerFace_,nFieldDOF);
2280
uAtIPs = _fN_*localField;
2281
// collect velocity at ips for "advection" = v.n
2282
_fieldItr_ = fields.find(Velocity);
2283
const DENS_MAT & advection = (_fieldItr_->second).quantity(); // advection velocity
2284
feMesh_->element_field(elem, advection, localField);
2285
for (int j = 0; j < nSD_; ++j) { // nSD_ == nDOF for the velocity
2286
for (int ip = 0; ip < nIPsPerFace_; ++ip) {
2287
for (int I = 0; I < nNodesPerElement_; ++I) {
2288
aAtIPs(ip,0) += (_nN_[j])(ip,I)*localField(I,j);
2292
// K_IJ = \sum_ip N_I ( v.n I + u (x) n ) N_J wg_ip
2293
DENS_MAT D(nFieldDOF,nFieldDOF);
2295
for (int ip = 0; ip < nIPsPerFace_; ++ip) {
2296
for (int idof = 0; idof<nFieldDOF; ++idof) {
2297
D(idof,idof) -= aAtIPs(ip,0);
2299
feMesh_->face_normal(face,ip,n);
2300
for (int jdof = 0; jdof<nFieldDOF; ++jdof) {
2301
D(idof,jdof) -= uAtIPs(ip,idof)*n(jdof);
2307
_Nmat_ = _fN_.transMat(D*_fN_);
2308
for (int i = 0; i < nNodesPerElement_; ++i) {
2309
int inode = _conn_(i);
2310
for (int j = 0; j < nNodesPerElement_; ++j) {
2311
int jnode = _conn_(j);
2312
K.add(inode, jnode, _Nmat_(i,j));
2317
// assemble partial result matrices
2319
sparse_allsum(communicator_,K);
2321
LammpsInterface::instance()->sparse_allsum(K);
2091
2326
//-----------------------------------------------------------------
2644
2893
<< feMesh_->num_elements() << " elements";
2645
2894
print_msg_once(communicator_,ss.str());
2646
2895
#ifdef ATC_VERBOSE
2647
string msg = "element sizes in x:\n";
2896
print_partitions(xmin,xmax,dx);
2897
print_partitions(ymin,ymax,dy);
2898
print_partitions(zmin,zmax,dz);
2901
//-----------------------------------------------------------------
2902
void FE_Engine::print_partitions(double xmin, double xmax, Array<double> & dx) const {
2906
msg << "\nindex weight fraction location size[A] size[uc]:\n";
2648
2907
double sum = 0;
2649
2908
for (int i = 0; i < dx.size(); ++i) { sum += dx(i); }
2650
2909
double xn= 1.0/sum;
2651
2910
double xs= xn*(xmax-xmin);
2652
2911
double xl= xs/(ATC::LammpsInterface::instance()->xlattice());
2653
2912
double sumn = 0, sums = 0, suml = 0;
2654
2914
for (int i = 0; i < dx.size(); ++i) {
2655
2915
double dxn = dx(i)*xn; sumn += dxn;
2656
2916
double dxs = dx(i)*xs; sums += dxs;
2657
2917
double dxl = dx(i)*xl; suml += dxl;
2658
msg += " "+to_string(i+1)
2659
+" "+to_string(dx(i))
2662
+" "+to_string(dxl)+"\n";
2918
msg << std::setw(5) << i+1
2919
<< std::setw(7) << dx(i)
2920
<< std::setw(9) << dxn
2921
<< std::setw(9) << x
2922
<< std::setw(8) << dxs
2923
<< std::setw(9) << dxl << "\n";
2664
msg += "sum "+to_string(sum)+" "
2665
+to_string(sumn)+" "
2666
+to_string(sums)+" "
2667
+to_string(suml)+"\n";
2668
print_msg_once(communicator_,msg);
2926
msg << "sum " << setw(7) << sum
2930
<< setw(9) << suml << "\n";
2931
print_msg_once(communicator_,msg.str());
2672
2933
//-----------------------------------------------------------------
2673
2934
// create a uniform rectangular mesh on a rectangular region
2674
2935
//-----------------------------------------------------------------