PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/job/src/inputfile.cpp

https://gitlab.com/Molcas/MolGUI
C++ | 539 lines | 479 code | 22 blank | 38 comment | 71 complexity | ad063be26d5586c4474b8a380b60de97 MD5 | raw file
  1. /***********************************************************************
  2. * This file is part of MolGUI. *
  3. * *
  4. * MolGUI is free software; you can redistribute it and/or modify it *
  5. * under the terms of the GNU Lesser General Public License, v. 2.1. *
  6. * MolGUI is distributed in the hope that it will be useful, but it is *
  7. * provided "as is" and without any express or implied warranties. *
  8. * For more details see the full text of the license in the file *
  9. * LICENSE or in <http://www.gnu.org/licenses/>. *
  10. ***********************************************************************/
  11. #include <wx/wfstream.h>
  12. #include <wx/txtstrm.h>
  13. #include <wx/filename.h>
  14. #include <wx/tokenzr.h>
  15. #include "inputfile.h"
  16. #include "datatype.h"
  17. //#include "molcascommons.h"
  18. static const wxString FILE_GENERAL_SPACE=wxT(" ");
  19. static const wxString AI_FILE_CART_END_FLAG=wxT("ENDCART");
  20. //////////////////////////////////////////////////////////////////////
  21. // Construction/Destruction
  22. //////////////////////////////////////////////////////////////////////
  23. #ifdef __BP_CONSTRAIN_DATA_H__
  24. void INPUTFile::SaveFile(const wxString &fileName, AtomBondArray &array, StringHash &setting, ConstrainData &constrainData)
  25. {
  26. wxFileOutputStream fos(fileName);
  27. wxTextOutputStream tos(fos);
  28. SaveExport(tos);
  29. SaveOutput(tos,array,setting);
  30. SaveSettings(tos,setting);
  31. SaveJobType(tos,array,setting,constrainData);
  32. //wxString tempStr;
  33. SaveFormat(tos,fileName,setting);
  34. }
  35. #endif
  36. void INPUTFile::SaveExport(wxTextOutputStream &tos)
  37. {
  38. tos<<wxT(">> export Project=test")<<endl;
  39. //tos<<wxT(">> export WorkDir=")<<MOLCAS_M2MSI_WORK_DIR<<endl;
  40. }
  41. void INPUTFile::SaveOutputSE(wxTextOutputStream &tos, AtomBondArray &array, StringHash &setting)
  42. {
  43. unsigned int i;
  44. int elemId;
  45. for(i=0;i<array.GetCount();i++)
  46. {
  47. if(array[i].atom.elemId == DEFAULT_HYDROGEN_ID){
  48. elemId = (int)ELEM_H;
  49. }else{
  50. elemId = array[i].atom.elemId ;
  51. }
  52. //only for
  53. if(elemId >0 )
  54. tos<<ELEM_NAMES[elemId-1]
  55. <<FILE_GENERAL_SPACE;
  56. else
  57. tos<<wxT("X")
  58. <<FILE_GENERAL_SPACE;
  59. tos<<wxString::Format(wxT("%-f"),array[i].atom.pos.x)
  60. <<FILE_GENERAL_SPACE
  61. <<wxT("1")
  62. <<FILE_GENERAL_SPACE
  63. <<wxString::Format(wxT("%-f"),array[i].atom.pos.y)
  64. <<FILE_GENERAL_SPACE
  65. <<wxT("1")
  66. <<FILE_GENERAL_SPACE
  67. <<wxString::Format(wxT("%-f"),array[i].atom.pos.z)
  68. <<FILE_GENERAL_SPACE
  69. <<wxT("1")
  70. <<FILE_GENERAL_SPACE
  71. <<endl;
  72. }
  73. }
  74. #ifdef __BP_CONSTRAIN_DATA_H__
  75. void INPUTFile::SaveOutput(wxTextOutputStream &tos, AtomBondArray &array, StringHash &setting)
  76. {
  77. unsigned int i,j,label;
  78. int elem[ELEMENT_COUNT];
  79. ElementInfo* info=NULL;
  80. for(i=0;i<ELEMENT_COUNT;i++)
  81. elem[i]=0;
  82. for(i=0;i<array.GetCount();i++)
  83. {
  84. if(array[i].atom.atomId>0)
  85. {
  86. elem[array[i].atom.atomId]++;
  87. }
  88. else if(array[i].atom.atomId==-1)
  89. {
  90. elem[1]++;
  91. }
  92. }
  93. //tos<<wxT(">> export HomeDir=/home/liufenglai/molcas ")<<endl;
  94. tos<<MOLCAS_FILE_OUTPUT_BEGIN<<endl;
  95. if(setting.find(wxT("Title"))!=setting.end())
  96. {
  97. if(!setting.find(wxT("Title"))->second.IsEmpty())
  98. {
  99. tos<<MOLCAS_FILE_GENERAL_TITLE<<endl;
  100. tos<<setting.find(wxT("Title"))->second<<endl;
  101. }
  102. }
  103. for(i=ELEMENT_COUNT-1;i>0;i--)
  104. {
  105. if(elem[i]>0)
  106. {
  107. info=GetElementInfo((AtomId)i);
  108. tos<<MOLCAS_FILE_GENERAL_BASIS_SET_BEGIN<<endl;
  109. tos<<info->name
  110. <<MOLCAS_FILE_GENERAL_POINT
  111. <<setting.find(wxT("BasisSet"))->second
  112. <<MOLCAS_FILE_GENERAL_POINT
  113. <<MOLCAS_FILE_GENERAL_POINT
  114. <<MOLCAS_FILE_GENERAL_POINT
  115. <<MOLCAS_FILE_GENERAL_POINT
  116. <<MOLCAS_FILE_GENERAL_POINT
  117. <<endl;
  118. j=0;
  119. label=1;
  120. while(j<array.GetCount())
  121. {
  122. if(abs(array[j].atom.atomId)==(int)i)
  123. {
  124. tos<<info->name
  125. <<wxString::Format(wxT("%d"),label)
  126. <<FILE_GENERAL_SPACE
  127. <<wxString::Format(wxT("%f"),array[j].atom.posX)
  128. <<FILE_GENERAL_SPACE
  129. <<wxString::Format(wxT("%f"),array[j].atom.posY)
  130. <<FILE_GENERAL_SPACE
  131. <<wxString::Format(wxT("%f"),array[j].atom.posZ)
  132. <<endl;
  133. //<<FILE_GENERAL_SPACE
  134. //<<MOLCAS_FILE_POSITION_MEASURE
  135. label++;
  136. }
  137. j++;
  138. }
  139. tos<<MOLCAS_FILE_METHOD_BASIS_SET_END<<endl;
  140. }
  141. }
  142. tos<<MOLCAS_FILE_GENERAL_END<<endl;
  143. }
  144. #endif
  145. void INPUTFile::SaveSESettings(wxTextOutputStream &tos, StringHash &setting)
  146. {
  147. wxString jobType=setting.find(wxT("JobType"))->second;
  148. wxString keywords=wxEmptyString;
  149. if(setting.find(wxT("KeyWords"))!=setting.end())
  150. {
  151. keywords=setting.find(wxT("KeyWords"))->second;
  152. }
  153. //wxMessageBox(keywords);
  154. if(!jobType.IsEmpty()){
  155. if(jobType.Cmp(wxT("SP"))==0)
  156. jobType=wxT("1SCF");
  157. else if(jobType.Cmp(wxT("TS"))==0)
  158. jobType=wxT("TS");
  159. else if(jobType.Cmp(wxT("FREQ"))==0)
  160. jobType=wxT("FREQ");
  161. else
  162. jobType=wxEmptyString;
  163. }
  164. if(setting.find(wxT("SEMod"))!=setting.end()) //add SEModel
  165. {
  166. tos<<setting.find(wxT("SEMod"))->second<<FILE_GENERAL_SPACE;
  167. }
  168. tos <<jobType
  169. <<FILE_GENERAL_SPACE
  170. <<wxT("charge=")
  171. <<setting.find(wxT("Charge"))->second
  172. <<FILE_GENERAL_SPACE;
  173. wxString multi;
  174. long value;
  175. multi=setting.find(wxT("Multi"))->second;
  176. multi.ToLong(&value);
  177. switch(value)
  178. {
  179. case 2:
  180. multi=wxT("doublet");
  181. break;
  182. case 3:
  183. multi=wxT("triplet");
  184. break;
  185. default:
  186. multi=wxEmptyString;
  187. break;
  188. }
  189. tos<<multi<<FILE_GENERAL_SPACE;
  190. if(setting.find(wxT("Format"))!=setting.end()) //add SEModel
  191. {
  192. tos <<wxT("calc_surface")
  193. <<FILE_GENERAL_SPACE;
  194. }
  195. if(setting.find(wxT("OptInteration"))!=setting.end()) //add SEModel
  196. {
  197. tos <<wxT("opt=")
  198. <<setting.find(wxT("OptInteration"))->second.Trim()
  199. <<FILE_GENERAL_SPACE;
  200. }
  201. if(!keywords.IsEmpty())
  202. {
  203. keywords.Replace(wxT(","),wxT(" "));
  204. tos <<wxT("debug")
  205. <<FILE_GENERAL_SPACE
  206. <<keywords
  207. <<FILE_GENERAL_SPACE;
  208. }
  209. tos<<endl;
  210. if(setting.find(wxT("Title"))!=setting.end())
  211. {
  212. if(!setting.find(wxT("Title"))->second.IsEmpty())
  213. {
  214. tos<<setting.find(wxT("Title"))->second;
  215. }
  216. }
  217. tos<<endl;
  218. tos<<endl;
  219. }
  220. #ifdef __BP_CONSTRAIN_DATA_H__
  221. void INPUTFile::SaveSettings(wxTextOutputStream &tos, StringHash &setting)
  222. {
  223. long lv;
  224. tos<<MOLCAS_FILE_SETTING_BEGIN<<endl;
  225. if(setting.find(wxT("Title"))!=setting.end())
  226. {
  227. if(!setting.find(wxT("Title"))->second.IsEmpty())
  228. {
  229. tos<<MOLCAS_FILE_GENERAL_TITLE<<endl;
  230. tos<<setting.find(wxT("Title"))->second<<endl;
  231. }
  232. }
  233. if(setting.find(wxT("DFTFunc"))!=setting.end())
  234. {
  235. tos<<MOLCAS_FILE_METHOD_DFT<<endl;
  236. tos<<setting.find(wxT("DFTFunc"))->second<<endl;
  237. }
  238. if(setting.find(wxT("Charge"))!=setting.end())
  239. {
  240. tos<<MOLCAS_FILE_UHF_FLAG<<endl;
  241. tos<<MOLCAS_FILE_ZSPIN_FLAG<<endl;
  242. //ZSPIN
  243. setting.find(wxT("Multi"))->second.ToLong(&lv);
  244. lv--;
  245. tos<<wxString::Format(wxT("%ld"),lv)<<endl;
  246. tos<<MOLCAS_FILE_CHARGE_FLAG<<endl;
  247. tos<<setting.find(wxT("Charge"))->second<<endl;
  248. }
  249. tos<<MOLCAS_FILE_GENERAL_END<<endl;
  250. }
  251. void INPUTFile::SaveGeometryOptimization(wxTextOutputStream &tos, AtomBondArray &array,StringHash &setting, ConstrainData &constrainData)
  252. {
  253. tos<<MOLCAS_FILE_ALASKA_FLAG<<endl;
  254. tos<<MOLCAS_FILE_GENERAL_END<<endl;
  255. tos<<MOLCAS_FILE_SLAPAF_FLAG<<endl;
  256. //CONSTRAIN
  257. SaveConstraints(tos,array,constrainData,true);
  258. tos<<MOLCAS_FILE_ITERATION_FLAG<<endl;
  259. tos<<setting.find(wxT("OptInteration"))->second<<endl;
  260. tos<<MOLCAS_FILE_THRESHOLD_FLAG<<endl;
  261. tos<<setting.find(wxT("ECharge"))->second
  262. <<FILE_GENERAL_SPACE
  263. <<setting.find(wxT("Grad"))->second
  264. <<endl;
  265. tos<<MOLCAS_FILE_GENERAL_END<<endl;
  266. }
  267. void INPUTFile::SaveConstraints(wxTextOutputStream &tos, AtomBondArray &array, ConstrainData &constrainData,bool useFlag)
  268. {
  269. wxArrayString lenArray=constrainData.GetConstrainLengthArray(array,true);
  270. wxArrayString angArray=constrainData.GetConstrainAngleArray(array,true);
  271. wxArrayString dihArray=constrainData.GetConstrainDihedralArray(array,true);
  272. wxArrayString tokens;
  273. unsigned int i,j;
  274. if(lenArray.GetCount()>0||angArray.GetCount()>0||dihArray.GetCount()>0)
  275. {
  276. if(useFlag)
  277. tos << MOLCAS_FILE_CONSTRAIN_BEGIN <<endl;
  278. for(i=0;i< lenArray.GetCount();i++)
  279. {
  280. tokens=wxStringTokenize(lenArray[i],wxT("\t"),wxTOKEN_STRTOK);
  281. tos << MOLCAS_FILE_CONSTRAIN_BOND_PREFIX
  282. << wxString::Format(wxT("%d"),i+1)
  283. << MOLCAS_FILE_CONSTRAIN_OPERATOR
  284. << MOLCAS_FILE_CONSTRAIN_BOND_FLAG;
  285. for(j=0;j<2;j++)
  286. tos <<wxT(" ")<<tokens[j];
  287. tos << endl;
  288. }
  289. for(i=0;i< angArray.GetCount();i++)
  290. {
  291. tokens=wxStringTokenize(angArray[i],wxT("\t"),wxTOKEN_STRTOK);
  292. tos << MOLCAS_FILE_CONSTRAIN_ANGLE_PREFIX
  293. << wxString::Format(wxT("%d"),i+1)
  294. << MOLCAS_FILE_CONSTRAIN_OPERATOR
  295. << MOLCAS_FILE_CONSTRAIN_ANGLE_FLAG;
  296. for(j=0;j<3;j++)
  297. tos <<wxT(" ")<<tokens[j];
  298. tos <<endl;
  299. }
  300. for(i=0;i< dihArray.GetCount(); i++)
  301. {
  302. tokens=wxStringTokenize(dihArray[i],wxT("\t"),wxTOKEN_STRTOK);
  303. tos << MOLCAS_FILE_CONSTRAIN_DIHEDRAL_PREFIX
  304. << wxString::Format(wxT("%d"),i+1)
  305. << MOLCAS_FILE_CONSTRAIN_OPERATOR
  306. << MOLCAS_FILE_CONSTRAIN_DIHEDRAL_FLAG;
  307. for(j=0;j<4;j++)
  308. tos << wxT(" ")<<tokens[j];
  309. tos << endl;
  310. }
  311. tos << MOLCAS_FILE_CONSTRAIN_VALUE << endl;
  312. for(i=0;i< lenArray.GetCount();i++)
  313. {
  314. tokens=wxStringTokenize(lenArray[i],wxT("\t"),wxTOKEN_STRTOK);
  315. tos << MOLCAS_FILE_CONSTRAIN_BOND_PREFIX
  316. << wxString::Format(wxT("%d"),i+1)
  317. << MOLCAS_FILE_CONSTRAIN_OPERATOR
  318. << tokens[2]
  319. << wxT(" ")
  320. << MOLCAS_FILE_CONSTRAIN_BOND_MEASURE
  321. << endl;
  322. }
  323. for(i=0;i< angArray.GetCount();i++)
  324. {
  325. tokens=wxStringTokenize(angArray[i],wxT("\t"),wxTOKEN_STRTOK);
  326. tos << MOLCAS_FILE_CONSTRAIN_ANGLE_PREFIX
  327. << wxString::Format(wxT("%d"),i+1)
  328. << MOLCAS_FILE_CONSTRAIN_OPERATOR
  329. << tokens[3]
  330. << wxT(" ")
  331. << MOLCAS_FILE_CONSTRAIN_ANGLE_MEASURE
  332. << endl;
  333. }
  334. for(i=0;i< dihArray.GetCount(); i++)
  335. {
  336. tokens=wxStringTokenize(dihArray[i],wxT("\t"),wxTOKEN_STRTOK);
  337. tos << MOLCAS_FILE_CONSTRAIN_DIHEDRAL_PREFIX
  338. << wxString::Format(wxT("%d"),i+1)
  339. << MOLCAS_FILE_CONSTRAIN_OPERATOR
  340. << tokens[4]
  341. << wxT(" ")
  342. << MOLCAS_FILE_CONSTRAIN_DIHEDRAL_MEASURE
  343. << endl;
  344. }
  345. if(useFlag)
  346. tos << MOLCAS_FILE_CONSTRAIN_END <<endl;
  347. }
  348. }
  349. void INPUTFile::SaveTransitionState(wxTextOutputStream &tos, AtomBondArray &array, StringHash &setting, ConstrainData &constrainData)
  350. {
  351. tos<<MOLCAS_FILE_ALASKA_FLAG<<endl;
  352. tos<<MOLCAS_FILE_GENERAL_END<<endl;
  353. tos<<MOLCAS_FILE_SLAPAF_FLAG<<endl;
  354. //CONSTRAIN
  355. SaveConstraints(tos,array,constrainData,true);
  356. tos<<MOLCAS_FILE_JOB_TYPE_TRANSITION_STATE<<endl;
  357. tos<<MOLCAS_FILE_ITERATION_FLAG<<endl;
  358. tos<<setting.find(wxT("OptInteration"))->second<<endl;
  359. tos<<MOLCAS_FILE_THRESHOLD_FLAG<<endl;
  360. tos<<setting.find(wxT("ECharge"))->second
  361. <<FILE_GENERAL_SPACE
  362. <<setting.find(wxT("Grad"))->second
  363. <<endl;
  364. tos<<MOLCAS_FILE_GENERAL_END<<endl;
  365. }
  366. void INPUTFile::SaveFormat(wxTextOutputStream &tos,wxString filename,StringHash &setting)
  367. {
  368. wxFileName fname(filename);
  369. if(setting.find(wxT("Format"))!=setting.end())
  370. {
  371. tos<<MOLCAS_FILE_GRID_IT_FLAG<<endl;
  372. if(setting.find(wxT("Format"))->second.MakeUpper().Cmp(wxT("ASCII"))==0)
  373. tos<<setting.find(wxT("Format"))->second<<endl;
  374. tos<<wxT("name")<<endl;
  375. tos<<fname.GetName()<<endl;
  376. //tempStr=setting.find(wxT("FileName"))->second;
  377. tos<<MOLCAS_FILE_GENERAL_END<<endl;
  378. /*tos<<endl;
  379. //tos<<MOLCAS_FILE_GRID_IT_EXTEND<<endl;
  380. tempStr.Replace(wxT(".M2Msi"),wxEmptyString);
  381. tempStr.MakeUpper();
  382. tos<<wxT("!cp ")
  383. <<wxT("$WorkDir/molcas_tmp.")
  384. <<tempStr
  385. <<wxT(".M2Msi $HomeDir")
  386. <<endl;
  387. */
  388. }
  389. }
  390. void INPUTFile::SaveJobType(wxTextOutputStream &tos, AtomBondArray &array, StringHash &setting, ConstrainData &constrainData)
  391. {
  392. unsigned int i;
  393. wxString jobType[4]={wxT("SP"), wxT("OPT"), wxT("TS"),wxT("FREQ")};
  394. for(i=0;i<4;i++)
  395. {
  396. if(jobType[i].Cmp(setting.find(wxT("JobType"))->second)==0)
  397. break;
  398. }
  399. switch(i)
  400. {
  401. case 0://Single Point Energy
  402. break;
  403. case 1://Geometry Optimization
  404. SaveGeometryOptimization(tos,array,setting,constrainData);
  405. break;
  406. case 2://Transition State
  407. SaveTransitionState(tos,array,setting,constrainData);
  408. break;
  409. default:
  410. break;
  411. }
  412. }
  413. void INPUTFile::SaveConstrainFile(const wxString &fileName, AtomBondArray &array, ConstrainData &constrainData)
  414. {
  415. wxFileOutputStream fos(fileName);
  416. wxTextOutputStream tos(fos);
  417. SaveConstraints(tos,array,constrainData,false);
  418. }
  419. #endif
  420. void INPUTFile::SaveFileSE(const wxString &fileName, RenderingData* pData, SimuJob* pJob)
  421. {
  422. if(pData == NULL || pData->GetAtomCount()<=0)
  423. return;
  424. StringHash &setting=pJob->GetJobControl();
  425. AtomBondArray &array=pData->GetAtomBondArray();
  426. wxFileOutputStream fos(fileName);
  427. wxTextOutputStream tos(fos);
  428. //SaveExportSE(tos);
  429. SaveSESettings(tos,setting);
  430. SaveOutputSE(tos,array,setting);
  431. }
  432. void INPUTFile::SaveFileAI(const wxString &fileName, RenderingData* pData, SimuJob* pJob)
  433. {
  434. if(pData == NULL || pData->GetAtomCount()<=0)
  435. return;
  436. unsigned int i;
  437. int elemId;
  438. StringHash &setting=pJob->GetJobControl();
  439. //StringHash &surface=pCtrlInst->GetJobSurfaceSettings();
  440. AtomBondArray &array=pData->GetAtomBondArray();
  441. wxFileOutputStream fos(fileName);
  442. wxTextOutputStream tos(fos);
  443. wxString jobType=setting.find(wxT("JobType"))->second;
  444. wxString keywords=wxEmptyString;
  445. if(setting.find(wxT("Method"))!=setting.end())
  446. {
  447. wxString method=setting.find(wxT("Method"))->second;
  448. tos<< method << FILE_GENERAL_SPACE;
  449. }
  450. if(pJob->CanCalcSurface()) //add SEModel
  451. {
  452. tos <<wxT("calc_surface")
  453. <<FILE_GENERAL_SPACE;
  454. }
  455. if(setting.find(wxT("BasisSet"))!=setting.end())
  456. {
  457. tos<< setting.find(wxT("BasisSet"))->second << FILE_GENERAL_SPACE;
  458. }
  459. if(!jobType.IsEmpty()){
  460. if(jobType.Cmp(wxT("SP"))==0)
  461. jobType=wxEmptyString;
  462. else if(jobType.Cmp(wxT("OPT"))==0)
  463. jobType=wxT("OPT");
  464. else if(jobType.Cmp(wxT("TS"))==0)
  465. jobType=wxT("TSOPT");
  466. else if(jobType.Cmp(wxT("FREQ"))==0)
  467. jobType=wxT("FREQ");
  468. tos<< jobType<< FILE_GENERAL_SPACE;
  469. }
  470. if(setting.find(wxT("KeyWords"))!=setting.end())
  471. {
  472. keywords=setting.find(wxT("KeyWords"))->second;
  473. if(!keywords.IsEmpty())
  474. {
  475. keywords.Replace(wxT(","),wxT(" "));
  476. tos <<keywords<<FILE_GENERAL_SPACE;
  477. }
  478. }
  479. //wxMessageBox(keywords);
  480. if(setting.find(wxT("SEMod"))!=setting.end()) //add SEModel
  481. {
  482. tos<<setting.find(wxT("SEMod"))->second<<FILE_GENERAL_SPACE;
  483. }
  484. tos<<wxT("printlev=3")<<endl;
  485. tos<<pJob->GetJobTitle()<<endl;
  486. wxString multi,charge;
  487. long value;
  488. charge=setting.find(wxT("Charge"))->second;
  489. charge.ToLong(&value);
  490. charge=wxString::Format(wxT("%ld"),value);
  491. multi=setting.find(wxT("Multi"))->second;
  492. multi.ToLong(&value);
  493. multi=wxString::Format(wxT("%ld"),value);
  494. tos<<charge<<FILE_GENERAL_SPACE<<multi<<FILE_GENERAL_SPACE<<endl;
  495. for(i=0;i<array.GetCount();i++)
  496. {
  497. if(array[i].atom.elemId == DEFAULT_HYDROGEN_ID){
  498. elemId = (int)ELEM_H;
  499. }else{
  500. elemId = array[i].atom.elemId ;
  501. }
  502. if(elemId>0)
  503. tos <<FILE_GENERAL_SPACE
  504. <<ELEM_NAMES[elemId-1];
  505. else
  506. tos <<FILE_GENERAL_SPACE
  507. <<wxT("X");
  508. tos<<FILE_GENERAL_SPACE
  509. <<wxString::Format(wxT("%-f"),array[i].atom.pos.x)
  510. <<FILE_GENERAL_SPACE
  511. <<wxString::Format(wxT("%-f"),array[i].atom.pos.y)
  512. <<FILE_GENERAL_SPACE
  513. <<wxString::Format(wxT("%-f"),array[i].atom.pos.z)
  514. <<endl;
  515. }
  516. tos<< AI_FILE_CART_END_FLAG <<endl;
  517. }