00001
00002
00003
00004
00005
00006
00007 #include "Albany_StateManager.hpp"
00008 #include "Albany_Utils.hpp"
00009 #include "Teuchos_VerboseObject.hpp"
00010 #include "Teuchos_TestForException.hpp"
00011
00012 Albany::StateManager::StateManager() :
00013 stateVarsAreAllocated(false),
00014 stateInfo(Teuchos::rcp(new StateInfoStruct)),
00015 time(0.0),
00016 timeOld(0.0)
00017 {
00018 }
00019
00020 Teuchos::RCP<Teuchos::ParameterList>
00021 Albany::StateManager::registerStateVariable(const std::string &name, const Teuchos::RCP<PHX::DataLayout> &dl,
00022 const Teuchos::RCP<PHX::DataLayout> &dummy,
00023 const std::string& ebName,
00024 const std::string &init_type,
00025 const double init_val,
00026 const bool registerOldState)
00027 {
00028 return registerStateVariable(name, dl, dummy, ebName, init_type, init_val, registerOldState, name);
00029 }
00030
00031 Teuchos::RCP<Teuchos::ParameterList>
00032 Albany::StateManager::registerStateVariable(const std::string &stateName, const Teuchos::RCP<PHX::DataLayout> &dl,
00033 const Teuchos::RCP<PHX::DataLayout> &dummy,
00034 const std::string& ebName,
00035 const std::string &init_type,
00036 const double init_val,
00037 const bool registerOldState,
00038 const std::string& fieldName)
00039 {
00040 const bool bOutputToExodus = true;
00041 registerStateVariable(stateName, dl, ebName, init_type, init_val, registerOldState, bOutputToExodus, fieldName);
00042
00043
00044 Teuchos::RCP<Teuchos::ParameterList> p = Teuchos::rcp(new Teuchos::ParameterList("Save or Load State "
00045 + stateName + " to/from field " + fieldName));
00046 p->set<const std::string>("State Name", stateName);
00047 p->set<const std::string>("Field Name", fieldName);
00048 p->set<const Teuchos::RCP<PHX::DataLayout> >("State Field Layout", dl);
00049 p->set<const Teuchos::RCP<PHX::DataLayout> >("Dummy Data Layout", dummy);
00050 return p;
00051 }
00052
00053 Teuchos::RCP<Teuchos::ParameterList>
00054 Albany::StateManager::registerStateVariable(const std::string &stateName, const Teuchos::RCP<PHX::DataLayout> &dl,
00055 const Teuchos::RCP<PHX::DataLayout> &dummy,
00056 const std::string& ebName,
00057 const std::string &init_type,
00058 const double init_val,
00059 const bool registerOldState,
00060 const bool outputToExodus)
00061 {
00062 registerStateVariable(stateName, dl, ebName, init_type, init_val, registerOldState, outputToExodus, stateName);
00063
00064
00065 Teuchos::RCP<Teuchos::ParameterList> p = Teuchos::rcp(new Teuchos::ParameterList("Save or Load State "
00066 + stateName + " to/from field " + stateName));
00067 p->set<const std::string>("State Name", stateName);
00068 p->set<const std::string>("Field Name", stateName);
00069 p->set<const Teuchos::RCP<PHX::DataLayout> >("State Field Layout", dl);
00070 p->set<const Teuchos::RCP<PHX::DataLayout> >("Dummy Data Layout", dummy);
00071 return p;
00072 }
00073
00074 void
00075 Albany::StateManager::registerStateVariable(const std::string &stateName,
00076 const Teuchos::RCP<PHX::DataLayout> &dl,
00077 const std::string &init_type){
00078
00079
00080 std::string ebName;
00081 Albany::StateInfoStruct::const_iterator st = stateInfo->begin();
00082 ebName = (*st)->nameMap[stateName];
00083
00084
00085 registerStateVariable(stateName, dl, ebName, init_type, 0.0, false, true, "");
00086
00087 }
00088
00089
00090 void
00091 Albany::StateManager::registerStateVariable(const std::string &stateName,
00092 const Teuchos::RCP<PHX::DataLayout> &dl,
00093 const std::string& ebName,
00094 const std::string &init_type,
00095 const double init_val,
00096 const bool registerOldState,
00097 const bool outputToExodus,
00098 const std::string &responseIDtoRequire)
00099
00100 {
00101 TEUCHOS_TEST_FOR_EXCEPT(stateVarsAreAllocated);
00102 using Albany::StateStruct;
00103
00104 if( statesToStore[ebName].find(stateName) != statesToStore[ebName].end() ) {
00105
00106
00107
00108
00109
00110
00111 return;
00112 }
00113
00114 statesToStore[ebName][stateName] = dl;
00115
00116
00117 StateStruct::MeshFieldEntity mfe_type;
00118 if(dl->rank() == 1 && dl->size() == 1)
00119 mfe_type = StateStruct::WorksetValue;
00120 else if(dl->rank() >= 1 && dl->name(0) == "Node")
00121 mfe_type = StateStruct::NodalData;
00122 else if(dl->rank() >= 1 && dl->name(0) == "Cell"){
00123 if(dl->rank() > 1 && dl->name(1) == "Node")
00124 mfe_type = StateStruct::ElemNode;
00125 else if(dl->rank() > 1 && dl->name(1) == "QuadPoint")
00126 mfe_type = StateStruct::QuadPoint;
00127 else TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
00128 "StateManager: Element Entity type - " << dl->name(1) << " - not supported" << std::endl);
00129 }
00130 else TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
00131 "StateManager: Unknown Entity type - " << dl->name(0) << " - not supported" << std::endl);
00132
00133 (*stateInfo).push_back(Teuchos::rcp(new StateStruct(stateName, mfe_type)));
00134 StateStruct& stateRef = *stateInfo->back();
00135 stateRef.setInitType(init_type);
00136 stateRef.setInitValue(init_val);
00137
00138 dl->dimensions(stateRef.dim);
00139
00140 if(stateRef.entity == StateStruct::NodalData){
00141
00142 Teuchos::RCP<Adapt::NodalDataBlock> nodalDataBlock = getNodalDataBlock();
00143
00144 if ( dl->rank() == 2 ){
00145
00146 nodalDataBlock->registerState(stateName, stateRef.dim[1]);
00147 }
00148 else if ( dl->rank() == 3 ){
00149
00150 nodalDataBlock->registerState(stateName, stateRef.dim[1]*stateRef.dim[2]);
00151 }
00152 else {
00153
00154 nodalDataBlock->registerState(stateName, 1);
00155 }
00156 }
00157
00158 stateRef.output = outputToExodus;
00159 stateRef.responseIDtoRequire = responseIDtoRequire;
00160
00161
00162 if (registerOldState) {
00163 stateRef.saveOldState = true;
00164
00165 std::string stateName_old = stateName + "_old";
00166 (*stateInfo).push_back(Teuchos::rcp(new Albany::StateStruct(stateName_old, mfe_type)));
00167 Albany::StateStruct& pstateRef = *stateInfo->back();
00168 pstateRef.initType = init_type;
00169 pstateRef.initValue = init_val;
00170 pstateRef.pParentStateStruct = &stateRef;
00171
00172 pstateRef.output = false;
00173 dl->dimensions(pstateRef.dim);
00174
00175 if(pstateRef.entity == StateStruct::NodalData){
00176
00177 Teuchos::RCP<Adapt::NodalDataBlock> nodalDataBlock = getNodalDataBlock();
00178
00179 if ( dl->rank() == 2 ){
00180
00181 nodalDataBlock->registerState(stateName_old, pstateRef.dim[1]);
00182 }
00183 else if ( dl->rank() == 3 ){
00184
00185 nodalDataBlock->registerState(stateName_old, pstateRef.dim[1]*pstateRef.dim[2]);
00186 }
00187 else {
00188
00189 nodalDataBlock->registerState(stateName_old, 1);
00190 }
00191 }
00192
00193 }
00194
00195
00196 stateRef.nameMap[stateName] = ebName;
00197
00198 }
00199
00200 Teuchos::RCP<Albany::StateInfoStruct>
00201 Albany::StateManager::getStateInfoStruct()
00202 {
00203 return stateInfo;
00204 }
00205
00206 void
00207 Albany::StateManager::setStateArrays(const Teuchos::RCP<Albany::AbstractDiscretization>& disc_)
00208 {
00209 TEUCHOS_TEST_FOR_EXCEPT(stateVarsAreAllocated);
00210 stateVarsAreAllocated = true;
00211 Teuchos::RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
00212
00213 disc = disc_;
00214
00215
00216
00217 Albany::StateArrays& sa = disc->getStateArrays();
00218 Albany::StateArrayVec& esa = sa.elemStateArrays;
00219 Albany::StateArrayVec& nsa = sa.nodeStateArrays;
00220
00221 int numElemWorksets = esa.size();
00222 int numNodeWorksets = nsa.size();
00223
00224
00225
00226 for (unsigned int i=0; i<stateInfo->size(); i++) {
00227 const std::string stateName = (*stateInfo)[i]->name;
00228 const std::string init_type = (*stateInfo)[i]->initType;
00229 const double init_val = (*stateInfo)[i]->initValue;
00230 bool have_restart = (*stateInfo)[i]->restartDataAvailable;
00231 Albany::StateStruct *pParentStruct = (*stateInfo)[i]->pParentStateStruct;
00232
00233
00234
00235
00236
00237
00238
00239
00240 *out << "StateManager: initializing state: " << stateName;
00241 switch((*stateInfo)[i]->entity){
00242
00243 case Albany::StateStruct::WorksetValue :
00244 case Albany::StateStruct::QuadPoint :
00245 case Albany::StateStruct::ElemNode :
00246
00247 if(have_restart){
00248 *out << " from restart file." << std::endl;
00249
00250 continue;
00251 }
00252 else if(pParentStruct && pParentStruct->restartDataAvailable){
00253 *out << " from restarted parent state." << std::endl;
00254
00255
00256
00257 for (int ws = 0; ws < numElemWorksets; ws++)
00258
00259 esa[ws][stateName] = esa[ws][pParentStruct->name];
00260
00261 continue;
00262 }
00263 else if (init_type == "scalar")
00264 *out << " with initialization type 'scalar' and value: " << init_val << std::endl;
00265 else if (init_type == "identity")
00266 *out << " with initialization type 'identity'" << std::endl;
00267
00268 for (int ws = 0; ws < numElemWorksets; ws++){
00269
00270 Albany::StateStruct::FieldDims dims;
00271 esa[ws][stateName].dimensions(dims);
00272 int size = dims.size();
00273
00274 if (init_type == "scalar"){
00275
00276 switch (size) {
00277
00278 case 1:
00279 esa[ws][stateName](0) = init_val;
00280 break;
00281
00282 case 2:
00283 for (int cell = 0; cell < dims[0]; ++cell)
00284 for (int qp = 0; qp < dims[1]; ++qp)
00285 esa[ws][stateName](cell, qp) = init_val;
00286 break;
00287
00288 case 3:
00289 for (int cell = 0; cell < dims[0]; ++cell)
00290 for (int qp = 0; qp < dims[1]; ++qp)
00291 for (int i = 0; i < dims[2]; ++i)
00292 esa[ws][stateName](cell, qp, i) = init_val;
00293 break;
00294
00295 case 4:
00296 for (int cell = 0; cell < dims[0]; ++cell)
00297 for (int qp = 0; qp < dims[1]; ++qp)
00298 for (int i = 0; i < dims[2]; ++i)
00299 for (int j = 0; j < dims[3]; ++j)
00300 esa[ws][stateName](cell, qp, i, j) = init_val;
00301 break;
00302
00303 default:
00304 TEUCHOS_TEST_FOR_EXCEPTION(size<2||size>4, std::logic_error,
00305 "Something is wrong during scalar state variable initialization: " << size);
00306 }
00307
00308 }
00309 else if (init_type == "identity"){
00310
00311
00312 TEUCHOS_TEST_FOR_EXCEPTION(size != 4, std::logic_error,
00313 "Something is wrong during tensor state variable initialization: " << size);
00314 TEUCHOS_TEST_FOR_EXCEPT( ! (dims[2] == dims[3]) );
00315
00316 for (int cell = 0; cell < dims[0]; ++cell)
00317 for (int qp = 0; qp < dims[1]; ++qp)
00318 for (int i = 0; i < dims[2]; ++i)
00319 for (int j = 0; j < dims[3]; ++j)
00320 if (i==j) esa[ws][stateName](cell, qp, i, i) = 1.0;
00321 else esa[ws][stateName](cell, qp, i, j) = 0.0;
00322 }
00323 }
00324 break;
00325
00326 case Albany::StateStruct::NodalData :
00327
00328 if(have_restart){
00329 *out << " from restart file." << std::endl;
00330
00331 continue;
00332 }
00333 else if(pParentStruct && pParentStruct->restartDataAvailable){
00334 *out << " from restarted parent state." << std::endl;
00335
00336
00337
00338 for (int ws = 0; ws < numNodeWorksets; ws++)
00339
00340 nsa[ws][stateName] = nsa[ws][pParentStruct->name];
00341
00342 continue;
00343 }
00344 else if (init_type == "scalar")
00345 *out << " with initialization type 'scalar' and value: " << init_val << std::endl;
00346 else if (init_type == "identity")
00347 *out << " with initialization type 'identity'" << std::endl;
00348
00349 for (int ws = 0; ws < numNodeWorksets; ws++){
00350
00351 Albany::StateStruct::FieldDims dims;
00352 nsa[ws][stateName].dimensions(dims);
00353 int size = dims.size();
00354
00355 if (init_type == "scalar")
00356
00357 switch (size) {
00358
00359 case 1:
00360 for (int node = 0; node < dims[0]; ++node)
00361 nsa[ws][stateName](node) = init_val;
00362 break;
00363
00364 case 2:
00365 for (int node = 0; node < dims[0]; ++node)
00366 for (int dim = 0; dim < dims[1]; ++dim)
00367 nsa[ws][stateName](node, dim) = init_val;
00368 break;
00369
00370 case 3:
00371 for (int node = 0; node < dims[0]; ++node)
00372 for (int dim = 0; dim < dims[1]; ++dim)
00373 for (int i = 0; i < dims[2]; ++i)
00374 nsa[ws][stateName](node, dim, i) = init_val;
00375 break;
00376
00377 default:
00378 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
00379 "Something is wrong during node scalar state variable initialization: " << size);
00380 }
00381 else if (init_type == "identity"){
00382
00383
00384 TEUCHOS_TEST_FOR_EXCEPTION(size != 3, std::logic_error,
00385 "Something is wrong during node tensor state variable initialization: " << size);
00386 TEUCHOS_TEST_FOR_EXCEPT( ! (dims[1] == dims[2]) );
00387
00388 for (int node = 0; node < dims[0]; ++node)
00389 for (int i = 0; i < dims[1]; ++i)
00390 for (int j = 0; j < dims[2]; ++j)
00391 if (i==j) nsa[ws][stateName](node, i, i) = 1.0;
00392 else nsa[ws][stateName](node, i, j) = 0.0;
00393 }
00394 }
00395 break;
00396 }
00397 }
00398 *out << std::endl;
00399 }
00400
00401
00402 Teuchos::RCP<Albany::AbstractDiscretization>
00403 Albany::StateManager::
00404 getDiscretization()
00405 {
00406 return disc;
00407 }
00408
00409
00410 void
00411 Albany::StateManager::
00412 importStateData(Albany::StateArrays& states_from)
00413 {
00414 TEUCHOS_TEST_FOR_EXCEPT(!stateVarsAreAllocated);
00415
00416
00417 Albany::StateArrays& sa = getStateArrays();
00418 Albany::StateArrayVec& esa = sa.elemStateArrays;
00419 Albany::StateArrayVec& nsa = sa.nodeStateArrays;
00420 Albany::StateArrayVec& elemStatesToCopyFrom = states_from.elemStateArrays;
00421 Albany::StateArrayVec& nodeStatesToCopyFrom = states_from.nodeStateArrays;
00422 int numElemWorksets = esa.size();
00423 int numNodeWorksets = nsa.size();
00424
00425 TEUCHOS_TEST_FOR_EXCEPT((unsigned int)numElemWorksets != elemStatesToCopyFrom.size());
00426 TEUCHOS_TEST_FOR_EXCEPT((unsigned int)numNodeWorksets != nodeStatesToCopyFrom.size());
00427
00428 Teuchos::RCP<Teuchos::FancyOStream> out(Teuchos::VerboseObjectBase::getDefaultOStream());
00429 *out << std::endl;
00430
00431 for (unsigned int i=0; i<stateInfo->size(); i++) {
00432 const std::string stateName = (*stateInfo)[i]->name;
00433
00434 switch((*stateInfo)[i]->entity){
00435
00436 case Albany::StateStruct::WorksetValue :
00437 case Albany::StateStruct::QuadPoint :
00438 case Albany::StateStruct::ElemNode :
00439
00440
00441 if( elemStatesToCopyFrom[0].find(stateName) == elemStatesToCopyFrom[0].end() ) {
00442
00443 continue;
00444 }
00445
00446 *out << "StateManager: filling state: " << stateName << std::endl;
00447 for (int ws = 0; ws < numElemWorksets; ws++)
00448 {
00449 Albany::StateStruct::FieldDims dims;
00450 esa[ws][stateName].dimensions(dims);
00451 int size = dims.size();
00452
00453 switch (size) {
00454 case 1:
00455 esa[ws][stateName](0) = elemStatesToCopyFrom[ws][stateName](0);
00456 break;
00457 case 2:
00458 for (int cell = 0; cell < dims[0]; ++cell)
00459 for (int qp = 0; qp < dims[1]; ++qp)
00460 esa[ws][stateName](cell, qp) = elemStatesToCopyFrom[ws][stateName](cell, qp);
00461 break;
00462 case 3:
00463 for (int cell = 0; cell < dims[0]; ++cell)
00464 for (int qp = 0; qp < dims[1]; ++qp)
00465 for (int i = 0; i < dims[2]; ++i)
00466 esa[ws][stateName](cell, qp, i) = elemStatesToCopyFrom[ws][stateName](cell, qp, i);
00467 break;
00468 case 4:
00469 for (int cell = 0; cell < dims[0]; ++cell)
00470 for (int qp = 0; qp < dims[1]; ++qp)
00471 for (int i = 0; i < dims[2]; ++i)
00472 for (int j = 0; j < dims[3]; ++j)
00473 esa[ws][stateName](cell, qp, i, j) = elemStatesToCopyFrom[ws][stateName](cell, qp, i, j);
00474 break;
00475 default:
00476 TEUCHOS_TEST_FOR_EXCEPTION(size<2||size>4, std::logic_error,
00477 "Something is wrong during zero state variable fill: " << size);
00478 }
00479 }
00480
00481 break;
00482
00483 case Albany::StateStruct::NodalData :
00484
00485
00486 if( nodeStatesToCopyFrom[0].find(stateName) == nodeStatesToCopyFrom[0].end() ) {
00487
00488 continue;
00489 }
00490
00491 *out << "StateManager: filling state: " << stateName << std::endl;
00492 for (int ws = 0; ws < numNodeWorksets; ws++){
00493
00494 Albany::StateStruct::FieldDims dims;
00495 nsa[ws][stateName].dimensions(dims);
00496 int size = dims.size();
00497
00498 switch (size) {
00499 case 1:
00500 for (int node = 0; node < dims[0]; ++node)
00501 nsa[ws][stateName](node) = nodeStatesToCopyFrom[ws][stateName](node);
00502 break;
00503 case 2:
00504 for (int node = 0; node < dims[0]; ++node)
00505 for (int dim = 0; dim < dims[1]; ++dim)
00506 nsa[ws][stateName](node, dim) = nodeStatesToCopyFrom[ws][stateName](node, dim);
00507 break;
00508 case 3:
00509 for (int node = 0; node < dims[0]; ++node)
00510 for (int dim = 0; dim < dims[1]; ++dim)
00511 for (int i = 0; i < dims[2]; ++i)
00512 nsa[ws][stateName](node, dim, i) = nodeStatesToCopyFrom[ws][stateName](node, dim, i);
00513 break;
00514 default:
00515 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
00516 "Something is wrong during node zero state variable fill: " << size);
00517 }
00518 }
00519 break;
00520 }
00521 }
00522
00523 *out << std::endl;
00524 }
00525
00526 Albany::StateArray&
00527 Albany::StateManager::getStateArray(SAType type, const int ws) const
00528 {
00529 TEUCHOS_TEST_FOR_EXCEPT(!stateVarsAreAllocated);
00530
00531 switch(type){
00532
00533 case ELEM:
00534 return disc->getStateArrays().elemStateArrays[ws];
00535 break;
00536 case NODE:
00537 return disc->getStateArrays().nodeStateArrays[ws];
00538 break;
00539 default:
00540 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error, "Error: Cannot match state array type in getStateArray()" << std::endl);
00541 }
00542 }
00543
00544 Albany::StateArrays&
00545 Albany::StateManager::getStateArrays() const
00546 {
00547 TEUCHOS_TEST_FOR_EXCEPT(!stateVarsAreAllocated);
00548 return disc->getStateArrays();
00549 }
00550
00551 void
00552 Albany::StateManager::updateStates()
00553 {
00554
00555 TEUCHOS_TEST_FOR_EXCEPT(!stateVarsAreAllocated);
00556
00557
00558 Albany::StateArrays& sa = disc->getStateArrays();
00559 Albany::StateArrayVec& esa = sa.elemStateArrays;
00560 Albany::StateArrayVec& nsa = sa.nodeStateArrays;
00561 int numElemWorksets = esa.size();
00562 int numNodeWorksets = nsa.size();
00563
00564
00565
00566 for (unsigned int i=0; i<stateInfo->size(); i++) {
00567 if ((*stateInfo)[i]->saveOldState) {
00568 const std::string stateName = (*stateInfo)[i]->name;
00569 const std::string stateName_old = stateName + "_old";
00570
00571 switch((*stateInfo)[i]->entity){
00572
00573 case Albany::StateStruct::WorksetValue :
00574 case Albany::StateStruct::QuadPoint :
00575 case Albany::StateStruct::ElemNode :
00576
00577 for (int ws = 0; ws < numElemWorksets; ws++)
00578 for (int j = 0; j < esa[ws][stateName].size(); j++)
00579 esa[ws][stateName_old][j] = esa[ws][stateName][j];
00580
00581 break;
00582
00583 case Albany::StateStruct::NodalData :
00584
00585 for (int ws = 0; ws < numNodeWorksets; ws++)
00586 for (int j = 0; j < nsa[ws][stateName].size(); j++)
00587 nsa[ws][stateName_old][j] = nsa[ws][stateName][j];
00588
00589 break;
00590 }
00591 }
00592 }
00593 }
00594
00595 Teuchos::RCP<Albany::EigendataStruct>
00596 Albany::StateManager::getEigenData()
00597 {
00598 return eigenData;
00599 }
00600
00601 void
00602 Albany::StateManager::setEigenData(const Teuchos::RCP<Albany::EigendataStruct>& eigdata)
00603 {
00604 eigenData = eigdata;
00605 }
00606
00607
00608 Teuchos::RCP<Epetra_MultiVector>
00609 Albany::StateManager::getAuxData()
00610 {
00611 return auxData;
00612 }
00613
00614 void
00615 Albany::StateManager::setAuxData(const Teuchos::RCP<Epetra_MultiVector>& aux_data)
00616 {
00617 auxData = aux_data;
00618 }
00619
00620
00621 std::vector<std::string>
00622 Albany::StateManager::getResidResponseIDsToRequire(std::string & elementBlockName)
00623 {
00624 std::string id, name, ebName;
00625 std::vector<std::string> idsToRequire;
00626
00627 int i = 0;
00628 for (Albany::StateInfoStruct::const_iterator st = stateInfo->begin(); st!= stateInfo->end(); st++) {
00629 name = (*st)->name;
00630 id = (*st)->responseIDtoRequire;
00631 ebName = (*st)->nameMap[name];
00632 if ( id.length() > 0 && ebName == elementBlockName ) {
00633 idsToRequire.push_back(id);
00634 #ifdef ALBANY_VERBOSE
00635 cout << "RRR1 " << name << " requiring " << id << " (" << i << ")" << endl;
00636 #endif
00637 }
00638 else {
00639 #ifdef ALBANY_VERBOSE
00640 cout << "RRR1 " << name << " empty (" << i << ")" << endl;
00641 #endif
00642 }
00643 i++;
00644 }
00645 return idsToRequire;
00646 }
00647