Geant4 Cross Reference |
1 // 1 // 2 // ******************************************* 2 // ******************************************************************** 3 // * License and Disclaimer 3 // * License and Disclaimer * 4 // * 4 // * * 5 // * The Geant4 software is copyright of th 5 // * The Geant4 software is copyright of the Copyright Holders of * 6 // * the Geant4 Collaboration. It is provided 6 // * the Geant4 Collaboration. It is provided under the terms and * 7 // * conditions of the Geant4 Software License 7 // * conditions of the Geant4 Software License, included in the file * 8 // * LICENSE and available at http://cern.ch/ 8 // * LICENSE and available at http://cern.ch/geant4/license . These * 9 // * include a list of copyright holders. 9 // * include a list of copyright holders. * 10 // * 10 // * * 11 // * Neither the authors of this software syst 11 // * Neither the authors of this software system, nor their employing * 12 // * institutes,nor the agencies providing fin 12 // * institutes,nor the agencies providing financial support for this * 13 // * work make any representation or warran 13 // * work make any representation or warranty, express or implied, * 14 // * regarding this software system or assum 14 // * regarding this software system or assume any liability for its * 15 // * use. Please see the license in the file 15 // * use. Please see the license in the file LICENSE and URL above * 16 // * for the full disclaimer and the limitatio 16 // * for the full disclaimer and the limitation of liability. * 17 // * 17 // * * 18 // * This code implementation is the result 18 // * This code implementation is the result of the scientific and * 19 // * technical work of the GEANT4 collaboratio 19 // * technical work of the GEANT4 collaboration. * 20 // * By using, copying, modifying or distri 20 // * By using, copying, modifying or distributing the software (or * 21 // * any work based on the software) you ag 21 // * any work based on the software) you agree to acknowledge its * 22 // * use in resulting scientific publicati 22 // * use in resulting scientific publications, and indicate your * 23 // * acceptance of all terms of the Geant4 Sof 23 // * acceptance of all terms of the Geant4 Software license. * 24 // ******************************************* 24 // ******************************************************************** 25 // 25 // 26 // G4GDMLParser implementation 26 // G4GDMLParser implementation 27 // 27 // 28 // Author: Zoltan Torzsok, November 2007 28 // Author: Zoltan Torzsok, November 2007 29 // ------------------------------------------- 29 // -------------------------------------------------------------------- 30 30 31 #include "G4GDMLParser.hh" 31 #include "G4GDMLParser.hh" 32 32 33 #include "G4UnitsTable.hh" 33 #include "G4UnitsTable.hh" 34 #include "G4LogicalVolumeStore.hh" 34 #include "G4LogicalVolumeStore.hh" 35 #include "G4RegionStore.hh" 35 #include "G4RegionStore.hh" 36 #include "G4UserLimits.hh" 36 #include "G4UserLimits.hh" 37 #include "G4ProductionCuts.hh" 37 #include "G4ProductionCuts.hh" 38 #include "G4ReflectionFactory.hh" 38 #include "G4ReflectionFactory.hh" 39 #include "G4Track.hh" 39 #include "G4Track.hh" 40 40 41 // ------------------------------------------- 41 // -------------------------------------------------------------------- 42 G4GDMLParser::G4GDMLParser() 42 G4GDMLParser::G4GDMLParser() 43 : strip(true) 43 : strip(true) 44 { 44 { 45 reader = new G4GDMLReadStructure; 45 reader = new G4GDMLReadStructure; 46 writer = new G4GDMLWriteStructure; 46 writer = new G4GDMLWriteStructure; 47 messenger = new G4GDMLMessenger(this); 47 messenger = new G4GDMLMessenger(this); 48 48 49 xercesc::XMLPlatformUtils::Initialize(); 49 xercesc::XMLPlatformUtils::Initialize(); 50 } 50 } 51 51 52 // ------------------------------------------- 52 // -------------------------------------------------------------------- 53 G4GDMLParser::G4GDMLParser(G4GDMLReadStructure 53 G4GDMLParser::G4GDMLParser(G4GDMLReadStructure* extr) 54 : urcode(true), strip(true) 54 : urcode(true), strip(true) 55 { 55 { 56 reader = extr; 56 reader = extr; 57 writer = new G4GDMLWriteStructure; 57 writer = new G4GDMLWriteStructure; 58 messenger = new G4GDMLMessenger(this); 58 messenger = new G4GDMLMessenger(this); 59 59 60 xercesc::XMLPlatformUtils::Initialize(); 60 xercesc::XMLPlatformUtils::Initialize(); 61 } 61 } 62 62 63 // ------------------------------------------- 63 // -------------------------------------------------------------------- 64 G4GDMLParser::G4GDMLParser(G4GDMLReadStructure 64 G4GDMLParser::G4GDMLParser(G4GDMLReadStructure* extr, 65 G4GDMLWriteStructur 65 G4GDMLWriteStructure* extw) 66 : urcode(true), uwcode(true), strip(true) 66 : urcode(true), uwcode(true), strip(true) 67 { 67 { 68 reader = extr; 68 reader = extr; 69 writer = extw; 69 writer = extw; 70 messenger = new G4GDMLMessenger(this); 70 messenger = new G4GDMLMessenger(this); 71 71 72 xercesc::XMLPlatformUtils::Initialize(); 72 xercesc::XMLPlatformUtils::Initialize(); 73 } 73 } 74 74 75 // ------------------------------------------- 75 // -------------------------------------------------------------------- 76 G4GDMLParser::~G4GDMLParser() 76 G4GDMLParser::~G4GDMLParser() 77 { 77 { 78 xercesc::XMLPlatformUtils::Terminate(); 78 xercesc::XMLPlatformUtils::Terminate(); 79 if(!urcode) 79 if(!urcode) 80 { 80 { 81 delete reader; 81 delete reader; 82 } 82 } 83 if(!uwcode) 83 if(!uwcode) 84 { 84 { 85 delete writer; 85 delete writer; 86 } 86 } 87 delete ullist; 87 delete ullist; 88 delete rlist; 88 delete rlist; 89 89 90 delete messenger; 90 delete messenger; 91 } 91 } 92 92 93 // ------------------------------------------- 93 // -------------------------------------------------------------------- 94 void G4GDMLParser::ImportRegions() 94 void G4GDMLParser::ImportRegions() 95 { 95 { 96 G4ReflectionFactory* reflFactory = G4Ref 96 G4ReflectionFactory* reflFactory = G4ReflectionFactory::Instance(); 97 const G4GDMLAuxListType* auxInfoList = GetAu 97 const G4GDMLAuxListType* auxInfoList = GetAuxList(); 98 for(auto iaux = auxInfoList->cbegin(); iaux 98 for(auto iaux = auxInfoList->cbegin(); iaux != auxInfoList->cend(); ++iaux) 99 { 99 { 100 if(iaux->type != "Region") 100 if(iaux->type != "Region") 101 continue; 101 continue; 102 102 103 G4String name = iaux->value; 103 G4String name = iaux->value; 104 if(strip) 104 if(strip) 105 { 105 { 106 reader->StripName(name); 106 reader->StripName(name); 107 } 107 } 108 if(G4StrUtil::contains(name, "DefaultRegio << 108 if(name.contains("DefaultRegionForTheWorld")) 109 continue; 109 continue; 110 110 111 if(!iaux->auxList) 111 if(!iaux->auxList) 112 { 112 { 113 G4Exception("G4GDMLParser::ImportRegions 113 G4Exception("G4GDMLParser::ImportRegions()", "ReadError", FatalException, 114 "Invalid definition of geome 114 "Invalid definition of geometrical region!"); 115 } 115 } 116 else // Create region and loop over all r 116 else // Create region and loop over all region attributes 117 { 117 { 118 G4Region* aRegion = new G4Region(n 118 G4Region* aRegion = new G4Region(name); 119 G4ProductionCuts* pcuts = new G4Producti 119 G4ProductionCuts* pcuts = new G4ProductionCuts(); 120 aRegion->SetProductionCuts(pcuts); 120 aRegion->SetProductionCuts(pcuts); 121 for(auto raux = iaux->auxList->cbegin(); 121 for(auto raux = iaux->auxList->cbegin(); 122 raux != iaux->auxList->cend(); 122 raux != iaux->auxList->cend(); ++raux) 123 { 123 { 124 const G4String& tag = raux->type; 124 const G4String& tag = raux->type; 125 if(tag == "volume") 125 if(tag == "volume") 126 { 126 { 127 G4String volname = raux->value; 127 G4String volname = raux->value; 128 if(strip) 128 if(strip) 129 { 129 { 130 reader->StripName(volname); 130 reader->StripName(volname); 131 } 131 } 132 G4LogicalVolumeStore* store = G4Logi << 132 G4LogicalVolume* lvol = 133 auto pos = store->GetMap().find(voln << 133 G4LogicalVolumeStore::GetInstance()->GetVolume(volname); 134 if(pos != store->GetMap().cend()) << 134 aRegion->AddRootLogicalVolume(lvol); 135 { << 135 if(reflFactory->IsConstituent(lvol)) 136 // Scan for all possible volumes w << 136 aRegion->AddRootLogicalVolume(reflFactory->GetReflectedLV(lvol)); 137 // and set them as root logical vo << 138 // Issue a notification in case mo << 139 // with same name exist and get se << 140 // << 141 if (pos->second.size()>1) << 142 { << 143 std::ostringstream message; << 144 message << "There exists more th << 145 << "in store named: " << << 146 << "NOTE: assigning all << 147 << "volumes for region: << 148 G4Exception("G4GDMLParser::Impor << 149 "Notification", Just << 150 } << 151 for (auto vpos = pos->second.cbegi << 152 vpos != pos->second.cend << 153 { << 154 aRegion->AddRootLogicalVolume(*v << 155 if(reflFactory->IsConstituent(*v << 156 aRegion->AddRootLogicalVolume( << 157 } << 158 } << 159 else << 160 { << 161 std::ostringstream message; << 162 message << "Volume NOT found in st << 163 << " Volume " << vo << 164 << G4endl << 165 << " No region is b << 166 G4Exception("G4GDMLParser::ImportR << 167 "InvalidSetup", JustWa << 168 } << 169 } 137 } 170 else if(tag == "pcut") 138 else if(tag == "pcut") 171 { 139 { 172 const G4String& cvalue = raux->value 140 const G4String& cvalue = raux->value; 173 const G4String& cunit = raux->unit; 141 const G4String& cunit = raux->unit; 174 if(G4UnitDefinition::GetCategory(cun 142 if(G4UnitDefinition::GetCategory(cunit) != "Length") 175 { 143 { 176 G4Exception("G4GDMLParser::ImportR 144 G4Exception("G4GDMLParser::ImportRegions()", "InvalidRead", 177 FatalException, "Inval 145 FatalException, "Invalid unit for length!"); 178 } 146 } 179 G4double cut = 147 G4double cut = 180 eval.Evaluate(cvalue) * G4UnitDefi 148 eval.Evaluate(cvalue) * G4UnitDefinition::GetValueOf(cunit); 181 pcuts->SetProductionCut(cut, "proton 149 pcuts->SetProductionCut(cut, "proton"); 182 } 150 } 183 else if(tag == "ecut") 151 else if(tag == "ecut") 184 { 152 { 185 const G4String& cvalue = raux->value 153 const G4String& cvalue = raux->value; 186 const G4String& cunit = raux->unit; 154 const G4String& cunit = raux->unit; 187 if(G4UnitDefinition::GetCategory(cun 155 if(G4UnitDefinition::GetCategory(cunit) != "Length") 188 { 156 { 189 G4Exception("G4GDMLParser::ImportR 157 G4Exception("G4GDMLParser::ImportRegions()", "InvalidRead", 190 FatalException, "Inval 158 FatalException, "Invalid unit for length!"); 191 } 159 } 192 G4double cut = 160 G4double cut = 193 eval.Evaluate(cvalue) * G4UnitDefi 161 eval.Evaluate(cvalue) * G4UnitDefinition::GetValueOf(cunit); 194 pcuts->SetProductionCut(cut, "e-"); 162 pcuts->SetProductionCut(cut, "e-"); 195 } 163 } 196 else if(tag == "poscut") 164 else if(tag == "poscut") 197 { 165 { 198 const G4String& cvalue = raux->value 166 const G4String& cvalue = raux->value; 199 const G4String& cunit = raux->unit; 167 const G4String& cunit = raux->unit; 200 if(G4UnitDefinition::GetCategory(cun 168 if(G4UnitDefinition::GetCategory(cunit) != "Length") 201 { 169 { 202 G4Exception("G4GDMLParser::ImportR 170 G4Exception("G4GDMLParser::ImportRegions()", "InvalidRead", 203 FatalException, "Inval 171 FatalException, "Invalid unit for length!"); 204 } 172 } 205 G4double cut = 173 G4double cut = 206 eval.Evaluate(cvalue) * G4UnitDefi 174 eval.Evaluate(cvalue) * G4UnitDefinition::GetValueOf(cunit); 207 pcuts->SetProductionCut(cut, "e+"); 175 pcuts->SetProductionCut(cut, "e+"); 208 } 176 } 209 else if(tag == "gamcut") 177 else if(tag == "gamcut") 210 { 178 { 211 const G4String& cvalue = raux->value 179 const G4String& cvalue = raux->value; 212 const G4String& cunit = raux->unit; 180 const G4String& cunit = raux->unit; 213 if(G4UnitDefinition::GetCategory(cun 181 if(G4UnitDefinition::GetCategory(cunit) != "Length") 214 { 182 { 215 G4Exception("G4GDMLParser::ImportR 183 G4Exception("G4GDMLParser::ImportRegions()", "InvalidRead", 216 FatalException, "Inval 184 FatalException, "Invalid unit for length!"); 217 } 185 } 218 G4double cut = 186 G4double cut = 219 eval.Evaluate(cvalue) * G4UnitDefi 187 eval.Evaluate(cvalue) * G4UnitDefinition::GetValueOf(cunit); 220 pcuts->SetProductionCut(cut, "gamma" 188 pcuts->SetProductionCut(cut, "gamma"); 221 } 189 } 222 else if(tag == "ulimits") 190 else if(tag == "ulimits") 223 { 191 { 224 G4double ustepMax = DBL_MAX, utrakMa 192 G4double ustepMax = DBL_MAX, utrakMax = DBL_MAX, utimeMax = DBL_MAX; 225 G4double uekinMin = 0., urangMin = 0 193 G4double uekinMin = 0., urangMin = 0.; 226 const G4String& ulname = raux->value 194 const G4String& ulname = raux->value; 227 for(auto uaux = raux->auxList->cbegi 195 for(auto uaux = raux->auxList->cbegin(); 228 uaux != raux->auxList->cend 196 uaux != raux->auxList->cend(); ++uaux) 229 { 197 { 230 const G4String& ultag = uaux->typ 198 const G4String& ultag = uaux->type; 231 const G4String& uvalue = uaux->val 199 const G4String& uvalue = uaux->value; 232 const G4String& uunit = uaux->uni 200 const G4String& uunit = uaux->unit; 233 G4double ulvalue = eval.Evaluate(u 201 G4double ulvalue = eval.Evaluate(uvalue) * eval.Evaluate(uunit); 234 if(ultag == "ustepMax") 202 if(ultag == "ustepMax") 235 { 203 { 236 ustepMax = ulvalue; 204 ustepMax = ulvalue; 237 } 205 } 238 else if(ultag == "utrakMax") 206 else if(ultag == "utrakMax") 239 { 207 { 240 utrakMax = ulvalue; 208 utrakMax = ulvalue; 241 } 209 } 242 else if(ultag == "utimeMax") 210 else if(ultag == "utimeMax") 243 { 211 { 244 utimeMax = ulvalue; 212 utimeMax = ulvalue; 245 } 213 } 246 else if(ultag == "uekinMin") 214 else if(ultag == "uekinMin") 247 { 215 { 248 uekinMin = ulvalue; 216 uekinMin = ulvalue; 249 } 217 } 250 else if(ultag == "urangMin") 218 else if(ultag == "urangMin") 251 { 219 { 252 urangMin = ulvalue; 220 urangMin = ulvalue; 253 } 221 } 254 else 222 else 255 { 223 { 256 G4Exception("G4GDMLParser::Impor 224 G4Exception("G4GDMLParser::ImportRegions()", "ReadError", 257 FatalException, "Inv 225 FatalException, "Invalid definition of user-limits!"); 258 } 226 } 259 } 227 } 260 G4UserLimits* ulimits = new G4UserLi 228 G4UserLimits* ulimits = new G4UserLimits( 261 ulname, ustepMax, utrakMax, utimeM 229 ulname, ustepMax, utrakMax, utimeMax, uekinMin, urangMin); 262 aRegion->SetUserLimits(ulimits); 230 aRegion->SetUserLimits(ulimits); 263 } 231 } 264 else 232 else 265 continue; // Ignore unknown tags 233 continue; // Ignore unknown tags 266 } 234 } 267 } 235 } 268 } 236 } 269 } 237 } 270 238 271 // ------------------------------------------- 239 // -------------------------------------------------------------------- 272 void G4GDMLParser::ExportRegions(G4bool storeR 240 void G4GDMLParser::ExportRegions(G4bool storeReferences) 273 { 241 { 274 G4RegionStore* rstore = G4RegionS 242 G4RegionStore* rstore = G4RegionStore::GetInstance(); 275 G4ReflectionFactory* reflFactory = G4Reflect 243 G4ReflectionFactory* reflFactory = G4ReflectionFactory::Instance(); 276 for(std::size_t i = 0; i < rstore->size(); + 244 for(std::size_t i = 0; i < rstore->size(); ++i) 277 // Skip default regions associated to wor 245 // Skip default regions associated to worlds 278 { 246 { 279 const G4String& tname = (*rstore)[i]->GetN 247 const G4String& tname = (*rstore)[i]->GetName(); 280 if(G4StrUtil::contains(tname, "DefaultRegi << 248 if(tname.contains("DefaultRegionForParallelWorld")) 281 continue; 249 continue; 282 const G4String& rname = writer->Generat 250 const G4String& rname = writer->GenerateName(tname, (*rstore)[i]); 283 rlist = new G4GDMLAuxLi 251 rlist = new G4GDMLAuxListType(); 284 G4GDMLAuxStructType raux = { "Region", rna 252 G4GDMLAuxStructType raux = { "Region", rname, "", rlist }; 285 auto rlvol_iter = (*rstore)[i]->GetRootLog 253 auto rlvol_iter = (*rstore)[i]->GetRootLogicalVolumeIterator(); 286 for(std::size_t j = 0; j < (*rstore)[i]->G 254 for(std::size_t j = 0; j < (*rstore)[i]->GetNumberOfRootVolumes(); ++j) 287 { 255 { 288 G4LogicalVolume* rlvol = *rlvol_iter; 256 G4LogicalVolume* rlvol = *rlvol_iter; 289 if(reflFactory->IsReflected(rlvol)) 257 if(reflFactory->IsReflected(rlvol)) 290 continue; 258 continue; 291 G4String vname = writer->GenerateName(rl 259 G4String vname = writer->GenerateName(rlvol->GetName(), rlvol); 292 if(!storeReferences) 260 if(!storeReferences) 293 { 261 { 294 reader->StripName(vname); 262 reader->StripName(vname); 295 } 263 } 296 G4GDMLAuxStructType rsubaux = { "volume" 264 G4GDMLAuxStructType rsubaux = { "volume", vname, "", 0 }; 297 rlist->push_back(rsubaux); 265 rlist->push_back(rsubaux); 298 ++rlvol_iter; 266 ++rlvol_iter; 299 } 267 } 300 G4double gam_cut 268 G4double gam_cut 301 = (*rstore)[i]->GetProductionCuts()->Get 269 = (*rstore)[i]->GetProductionCuts()->GetProductionCut("gamma"); 302 G4GDMLAuxStructType caux1 270 G4GDMLAuxStructType caux1 303 = { "gamcut", eval.ConvertToString(gam_c 271 = { "gamcut", eval.ConvertToString(gam_cut), "mm", 0 }; 304 rlist->push_back(caux1); 272 rlist->push_back(caux1); 305 G4double e_cut = (*rstore)[i]->GetProducti 273 G4double e_cut = (*rstore)[i]->GetProductionCuts()->GetProductionCut("e-"); 306 G4GDMLAuxStructType caux2 274 G4GDMLAuxStructType caux2 307 = { "ecut", eval.ConvertToString(e_cut), 275 = { "ecut", eval.ConvertToString(e_cut), "mm", 0 }; 308 rlist->push_back(caux2); 276 rlist->push_back(caux2); 309 G4double pos_cut 277 G4double pos_cut 310 = (*rstore)[i]->GetProductionCuts()->Get 278 = (*rstore)[i]->GetProductionCuts()->GetProductionCut("e+"); 311 G4GDMLAuxStructType caux3 279 G4GDMLAuxStructType caux3 312 = { "poscut", eval.ConvertToString(pos_c 280 = { "poscut", eval.ConvertToString(pos_cut), "mm", 0 }; 313 rlist->push_back(caux3); 281 rlist->push_back(caux3); 314 G4double p_cut 282 G4double p_cut 315 = (*rstore)[i]->GetProductionCuts()->Get 283 = (*rstore)[i]->GetProductionCuts()->GetProductionCut("proton"); 316 G4GDMLAuxStructType caux4 284 G4GDMLAuxStructType caux4 317 = { "pcut", eval.ConvertToString(p_cut), 285 = { "pcut", eval.ConvertToString(p_cut), "mm", 0 }; 318 rlist->push_back(caux4); 286 rlist->push_back(caux4); 319 if((*rstore)[i]->GetUserLimits()) 287 if((*rstore)[i]->GetUserLimits()) 320 { 288 { 321 const G4Track fake_trk; 289 const G4Track fake_trk; 322 ullist = new G4GDMLAux 290 ullist = new G4GDMLAuxListType(); 323 const G4String& utype = (*rstore)[i]- 291 const G4String& utype = (*rstore)[i]->GetUserLimits()->GetType(); 324 G4GDMLAuxStructType uaux = { "ulimits", 292 G4GDMLAuxStructType uaux = { "ulimits", utype, "mm", ullist }; 325 G4double max_step 293 G4double max_step 326 = (*rstore)[i]->GetUserLimits()->GetMa 294 = (*rstore)[i]->GetUserLimits()->GetMaxAllowedStep(fake_trk); 327 G4GDMLAuxStructType ulaux1 295 G4GDMLAuxStructType ulaux1 328 = { "ustepMax", eval.ConvertToString(m 296 = { "ustepMax", eval.ConvertToString(max_step), "mm", 0 }; 329 ullist->push_back(ulaux1); 297 ullist->push_back(ulaux1); 330 G4double max_trk 298 G4double max_trk 331 = (*rstore)[i]->GetUserLimits()->GetUs 299 = (*rstore)[i]->GetUserLimits()->GetUserMaxTrackLength(fake_trk); 332 G4GDMLAuxStructType ulaux2 300 G4GDMLAuxStructType ulaux2 333 = { "utrakMax", eval.ConvertToString(m 301 = { "utrakMax", eval.ConvertToString(max_trk), "mm", 0 }; 334 ullist->push_back(ulaux2); 302 ullist->push_back(ulaux2); 335 G4double max_time 303 G4double max_time 336 = (*rstore)[i]->GetUserLimits()->GetUs 304 = (*rstore)[i]->GetUserLimits()->GetUserMaxTime(fake_trk); 337 G4GDMLAuxStructType ulaux3 305 G4GDMLAuxStructType ulaux3 338 = { "utimeMax", eval.ConvertToString(m 306 = { "utimeMax", eval.ConvertToString(max_time), "mm", 0 }; 339 ullist->push_back(ulaux3); 307 ullist->push_back(ulaux3); 340 G4double min_ekin 308 G4double min_ekin 341 = (*rstore)[i]->GetUserLimits()->GetUs 309 = (*rstore)[i]->GetUserLimits()->GetUserMinEkine(fake_trk); 342 G4GDMLAuxStructType ulaux4 310 G4GDMLAuxStructType ulaux4 343 = { "uekinMin", eval.ConvertToString(m 311 = { "uekinMin", eval.ConvertToString(min_ekin), "mm", 0 }; 344 ullist->push_back(ulaux4); 312 ullist->push_back(ulaux4); 345 G4double min_rng 313 G4double min_rng 346 = (*rstore)[i]->GetUserLimits()->GetUs 314 = (*rstore)[i]->GetUserLimits()->GetUserMinRange(fake_trk); 347 G4GDMLAuxStructType ulaux5 315 G4GDMLAuxStructType ulaux5 348 = { "urangMin", eval.ConvertToString(m 316 = { "urangMin", eval.ConvertToString(min_rng), "mm", 0 }; 349 ullist->push_back(ulaux5); 317 ullist->push_back(ulaux5); 350 rlist->push_back(uaux); 318 rlist->push_back(uaux); 351 } 319 } 352 AddAuxiliary(raux); 320 AddAuxiliary(raux); 353 } 321 } 354 } 322 } 355 323