Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-11-05 00:07:47

0001 // -*- C++ -*-
0002 //
0003 // Package:    SiPixelPhase1Summary
0004 // Class:      SiPixelPhase1Summary
0005 //
0006 /**\class 
0007 
0008  Description: Create the Phsae 1 pixel summary map
0009 
0010  Implementation:
0011      <Notes on implementation>
0012 */
0013 //
0014 // Original Author:  Duncan Leggat
0015 //         Created:  5th December 2016
0016 //
0017 //
0018 #include "DQM/SiPixelPhase1Summary/interface/SiPixelPhase1Summary.h"
0019 // Framework
0020 #include "FWCore/ServiceRegistry/interface/Service.h"
0021 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0022 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0023 // DQM Framework
0024 #include "DQM/SiPixelCommon/interface/SiPixelFolderOrganizer.h"
0025 #include "DQMServices/Core/interface/DQMStore.h"
0026 // Geometry
0027 #include "Geometry/TrackerGeometryBuilder/interface/TrackerGeometry.h"
0028 #include "Geometry/Records/interface/TrackerDigiGeometryRecord.h"
0029 #include "Geometry/CommonDetUnit/interface/PixelGeomDetUnit.h"
0030 #include "Geometry/CommonTopologies/interface/PixelTopology.h"
0031 #include "Geometry/Records/interface/TrackerTopologyRcd.h"
0032 // DataFormats
0033 #include "DataFormats/DetId/interface/DetId.h"
0034 #include "DataFormats/SiPixelDetId/interface/PixelSubdetector.h"
0035 #include "DataFormats/TrackerCommon/interface/PixelBarrelName.h"
0036 #include "DataFormats/SiPixelDetId/interface/PixelBarrelNameUpgrade.h"
0037 #include "DataFormats/TrackerCommon/interface/PixelEndcapName.h"
0038 #include "DataFormats/SiPixelDetId/interface/PixelEndcapNameUpgrade.h"
0039 //
0040 #include <string>
0041 #include <cstdlib>
0042 #include <iostream>
0043 #include <fstream>
0044 #include <sstream>
0045 
0046 using namespace std;
0047 using namespace edm;
0048 
0049 SiPixelPhase1Summary::SiPixelPhase1Summary(const edm::ParameterSet& iConfig)
0050     : DQMEDHarvester(iConfig), conf_(iConfig), firstLumi(true) {
0051   LogInfo("PixelDQM") << "SiPixelPhase1Summary::SiPixelPhase1Summary: Got DQM BackEnd interface" << endl;
0052   topFolderName_ = conf_.getParameter<std::string>("TopFolderName");
0053   runOnEndLumi_ = conf_.getParameter<bool>("RunOnEndLumi");
0054   runOnEndJob_ = conf_.getParameter<bool>("RunOnEndJob");
0055 
0056   std::vector<edm::ParameterSet> mapPSets = conf_.getParameter<std::vector<edm::ParameterSet> >("SummaryMaps");
0057 
0058   //Go through the configuration file and add in
0059   for (auto const& mapPSet : mapPSets) {
0060     summaryPlotName_[mapPSet.getParameter<std::string>("MapName")] = mapPSet.getParameter<std::string>("MapHist");
0061   }
0062   deadRocThresholds_ = conf_.getParameter<std::vector<double> >("DeadROCErrorThreshold");
0063   deadRocWarnThresholds_ = conf_.getParameter<std::vector<double> >("DeadROCWarningThreshold");
0064 }
0065 
0066 SiPixelPhase1Summary::~SiPixelPhase1Summary() {
0067   // do anything here that needs to be done at desctruction time
0068   // (e.g. close files, deallocate resources etc.)
0069   LogInfo("PixelDQM") << "SiPixelPhase1Summary::~SiPixelPhase1Summary: Destructor" << endl;
0070 }
0071 
0072 void SiPixelPhase1Summary::beginRun(edm::Run const& run, edm::EventSetup const& eSetup) {}
0073 
0074 void SiPixelPhase1Summary::dqmEndLuminosityBlock(DQMStore::IBooker& iBooker,
0075                                                  DQMStore::IGetter& iGetter,
0076                                                  const edm::LuminosityBlock& lumiSeg,
0077                                                  edm::EventSetup const& c) {
0078   if (firstLumi) {
0079     bookSummaries(iBooker);
0080     bookTrendPlots(iBooker);
0081     firstLumi = false;
0082   }
0083 
0084   if (runOnEndLumi_) {
0085     int lumiSec = lumiSeg.id().luminosityBlock();
0086     fillTrendPlots(iBooker, iGetter, lumiSec);
0087     fillSummaries(iBooker, iGetter);
0088   }
0089 
0090   //  iBooker.cd();
0091 }
0092 
0093 //------------------------------------------------------------------
0094 // Method called for every event
0095 //------------------------------------------------------------------
0096 void SiPixelPhase1Summary::dqmEndJob(DQMStore::IBooker& iBooker, DQMStore::IGetter& iGetter) {
0097   if (firstLumi) {  //Book the plots in the (maybe possible?) case that they aren't booked in the dqmEndLuminosityBlock method
0098     bookSummaries(iBooker);
0099     bookTrendPlots(iBooker);
0100     firstLumi = false;
0101   }
0102   if (runOnEndJob_) {
0103     if (!runOnEndLumi_)
0104       fillTrendPlots(
0105           iBooker,
0106           iGetter);  //If we're filling these plots at the end lumi step, it doesn't really make sense to also do them at the end job
0107     fillSummaries(iBooker, iGetter);
0108   }
0109 }
0110 
0111 //------------------------------------------------------------------
0112 // Used to book the summary plots
0113 //------------------------------------------------------------------
0114 void SiPixelPhase1Summary::bookSummaries(DQMStore::IBooker& iBooker) {
0115   iBooker.cd();
0116 
0117   std::vector<std::string> xAxisLabels_ = {"BMO",
0118                                            "BMI",
0119                                            "BPO ",
0120                                            "BPI",
0121                                            "HCMO_1",
0122                                            "HCMO_2",
0123                                            "HCMI_1",
0124                                            "HCMI_2",
0125                                            "HCPO_1",
0126                                            "HCPO_2",
0127                                            "HCPI_1",
0128                                            "HCPI_2"};  // why not having a global variable !?!?!?!
0129   std::vector<std::string> yAxisLabels_ = {
0130       "1",
0131       "2",
0132       "3",
0133       "4"};  // why not having a global variable ?!?!?!!? - I originally did, but was told not to by David Lange!
0134 
0135   iBooker.setCurrentFolder("PixelPhase1/Summary");
0136   //Book the summary plots for the variables as described in the config file
0137   for (const auto& mapInfo : summaryPlotName_) {
0138     auto name = mapInfo.first;
0139     summaryMap_[name] = iBooker.book2D("pixel" + name + "Summary", "Pixel " + name + " Summary", 12, 0, 12, 4, 0, 4);
0140   }
0141   //Make the new 6 bin ROC summary
0142   deadROCSummary = iBooker.book2D("deadROCSummary", "Percentage of dead ROCs per layer/ring", 2, 0, 2, 4, 0, 4);
0143   std::vector<std::string> xAxisLabelsReduced_ = {"Barrel", "Forward"};
0144   deadROCSummary->setAxisTitle("Subdetector", 1);
0145   for (unsigned int i = 0; i < xAxisLabelsReduced_.size(); i++) {
0146     deadROCSummary->setBinLabel(i + 1, xAxisLabelsReduced_[i]);
0147   }
0148 
0149   //Book the summary plot
0150   iBooker.setCurrentFolder("PixelPhase1/EventInfo");
0151 
0152   if (runOnEndLumi_) {
0153     //New less granular summary plot - this is currently only done online
0154     summaryMap_["Grand"] = iBooker.book2D("reportSummaryMap", "Pixel Summary Map", 2, 0, 2, 4, 0, 4);
0155     summaryMap_["Grand"]->setAxisTitle("Subdetector", 1);
0156     for (unsigned int i = 0; i < xAxisLabelsReduced_.size(); i++) {
0157       summaryMap_["Grand"]->setBinLabel(i + 1, xAxisLabelsReduced_[i]);
0158       for (unsigned int j = 0; j < 4; j++) {
0159         summaryMap_["Grand"]->Fill(i, j, -1);
0160       }
0161     }
0162   } else {
0163     //Book the original summary plot, for now juts doing this one offline.
0164     summaryMap_["Grand"] = iBooker.book2D("reportSummaryMap", "Pixel Summary Map", 12, 0, 12, 4, 0, 4);
0165   }
0166 
0167   reportSummary = iBooker.bookFloat("reportSummary");
0168 
0169   //Now set up axis and bin labels
0170   for (const auto& summaryMapEntry : summaryMap_) {
0171     if (summaryMapEntry.first == "Grand")
0172       continue;
0173     auto summaryMap = summaryMapEntry.second;
0174     for (unsigned int i = 0; i < xAxisLabels_.size(); i++) {
0175       summaryMap->setBinLabel(i + 1, xAxisLabels_[i], 1);
0176     }
0177     for (unsigned int i = 0; i < yAxisLabels_.size(); i++) {
0178       summaryMap->setBinLabel(i + 1, yAxisLabels_[i], 2);
0179     }
0180     summaryMap->setAxisTitle("Subdetector", 1);
0181     summaryMap->setAxisTitle("Layer/disk", 2);
0182     for (int i = 0; i < summaryMap->getTH1()->GetXaxis()->GetNbins(); i++) {    // !??!?!? xAxisLabels_.size() ?!?!
0183       for (int j = 0; j < summaryMap->getTH1()->GetYaxis()->GetNbins(); j++) {  // !??!?!? yAxisLabels_.size() ?!?!?!
0184         summaryMap->Fill(i, j, -1.);
0185       }
0186     }
0187   }
0188   reportSummary->Fill(-1.);
0189 
0190   //Reset the iBooker
0191   iBooker.setCurrentFolder("PixelPhase1/");
0192 }
0193 
0194 //------------------------------------------------------------------
0195 // Used to book the trend plots
0196 //------------------------------------------------------------------
0197 void SiPixelPhase1Summary::bookTrendPlots(DQMStore::IBooker& iBooker) {
0198   //We need different plots depending on if we're online (runOnEndLumi) or offline (!runOnEndLumi)
0199   iBooker.setCurrentFolder("PixelPhase1/");
0200   std::vector<string> binAxisLabels = {"Layer 1", "Layer 2", "Layer 3", "Layer 4", "Ring 1", "Ring 2"};
0201   if (runOnEndLumi_) {
0202     std::vector<trendPlots> histoOrder = {layer1, layer2, layer3, layer4, ring1, ring2};
0203     std::vector<string> varName = {"Layer_1", "Layer_2", "Layer_3", "Layer_4", "Ring_1", "Ring_2"};
0204     std::vector<int> yMax = {1536, 3584, 5632, 8192, 4224, 6528};
0205     for (unsigned int i = 0; i < histoOrder.size(); i++) {
0206       string varNameStr = "deadRocTrend" + varName[i];
0207       string varTitle = binAxisLabels[i] + " dead ROC trend";
0208       deadROCTrends_[histoOrder[i]] = iBooker.bookProfile(varNameStr, varTitle, 500, 0., 5000, 0., yMax[i], "");
0209       varNameStr = "ineffRocTrend" + varName[i];
0210       varTitle = binAxisLabels[i] + " inefficient ROC trend";
0211       ineffROCTrends_[histoOrder[i]] = iBooker.bookProfile(varNameStr, varTitle, 500, 0., 5000, 0., yMax[i], "");
0212       deadROCTrends_[histoOrder[i]]->setAxisTitle("Lumisection", 1);
0213       ineffROCTrends_[histoOrder[i]]->setAxisTitle("Lumisection", 1);
0214     }
0215   } else {
0216     deadROCTrends_[offline] = iBooker.bookProfile("deadRocTotal", "N dead ROCs", 6, 0., 6, 0., 8192, "");
0217     ineffROCTrends_[offline] = iBooker.bookProfile("ineffRocTotal", "N inefficient ROCs", 6, 0., 6, 0., 8192, "");
0218     deadROCTrends_[offline]->setAxisTitle("Subdetector", 1);
0219     ineffROCTrends_[offline]->setAxisTitle("Subdetector", 1);
0220     for (unsigned int i = 1; i <= binAxisLabels.size(); i++) {
0221       deadROCTrends_[offline]->setBinLabel(i, binAxisLabels[i - 1]);
0222       ineffROCTrends_[offline]->setBinLabel(i, binAxisLabels[i - 1]);
0223     }
0224   }
0225 }
0226 
0227 //------------------------------------------------------------------
0228 // Fill the summary histograms
0229 //------------------------------------------------------------------
0230 void SiPixelPhase1Summary::fillSummaries(DQMStore::IBooker& iBooker, DQMStore::IGetter& iGetter) {
0231   //Firstly, we will fill the regular summary maps.
0232   for (const auto& mapInfo : summaryPlotName_) {
0233     auto name = mapInfo.first;
0234     std::ostringstream histNameStream;
0235     std::string histName;
0236 
0237     for (int i = 0; i < 12; i++) {   // !??!?!? xAxisLabels_.size() ?!?!
0238       for (int j = 0; j < 4; j++) {  // !??!?!? yAxisLabels_.size() ?!?!?!
0239         if (i > 3 && j == 3)
0240           continue;
0241         bool minus = i < 2 || (i > 3 && i < 8);  // bleah !
0242         int iOver2 = floor(i / 2.);
0243         bool outer = (i > 3) ? iOver2 % 2 == 0 : i % 2 == 0;
0244         //Complicated expression that creates the name of the histogram we are interested in.
0245         histNameStream.str("");
0246         histNameStream << topFolderName_.c_str() << "PX" << ((i > 3) ? "Forward" : "Barrel") << "/"
0247                        << ((i > 3) ? "HalfCylinder" : "Shell") << "_" << (minus ? "m" : "p") << ((outer) ? "O" : "I")
0248                        << "/" << ((i > 3) ? ((i % 2 == 0) ? "PXRing_1/" : "PXRing_2/") : "")
0249                        << summaryPlotName_[name].c_str() << "_PX" << ((i > 3) ? "Disk" : "Layer") << "_"
0250                        << ((i > 3) ? ((minus) ? "-" : "+") : "") << (j + 1);
0251         histName = histNameStream.str();
0252         MonitorElement* me = iGetter.get(histName);
0253 
0254         if (!me) {
0255           edm::LogWarning("SiPixelPhase1Summary") << "ME " << histName << " is not available !!";
0256           continue;  // Ignore non-existing MEs, as this can cause the whole thing to crash
0257         }
0258 
0259         if (summaryMap_[name] == nullptr) {
0260           edm::LogWarning("SiPixelPhase1Summary") << "Summary map " << name << " is not available !!";
0261           continue;  // Based on reported errors it seems possible that we're trying to access a non-existant summary map, so if the map doesn't exist but we're trying to access it here we'll skip it instead.
0262         }
0263         if (!(me->getQReports()).empty())
0264           summaryMap_[name]->setBinContent(i + 1, j + 1, (me->getQReports())[0]->getQTresult());
0265         else
0266           summaryMap_[name]->setBinContent(i + 1, j + 1, -1);
0267       }
0268     }
0269   }
0270 
0271   //Fill the dead ROC summary
0272   std::vector<trendPlots> trendOrder = {layer1, layer2, layer3, layer4, ring1, ring2};
0273   std::vector<int> nRocsPerTrend = {1536, 3584, 5632, 8192, 4224, 6528};
0274   for (unsigned int i = 0; i < trendOrder.size(); i++) {
0275     int xBin = i < 4 ? 1 : 2;
0276     int yBin = i % 4 + 1;
0277     float nROCs = 0.;
0278     if (runOnEndLumi_) {  //Online case
0279       TH1* tempProfile = deadROCTrends_[trendOrder[i]]->getTH1();
0280       nROCs = tempProfile->GetBinContent(tempProfile->FindLastBinAbove());
0281     } else {  //Offline case
0282       TH1* tempProfile = deadROCTrends_[offline]->getTH1();
0283       nROCs = tempProfile->GetBinContent(i + 1);
0284     }
0285     deadROCSummary->setBinContent(xBin, yBin, nROCs / nRocsPerTrend[i]);
0286     deadROCSummary->setBinContent(2, 3, -1);
0287     deadROCSummary->setBinContent(2, 4, -1);
0288   }
0289 
0290   //Sum of non-negative bins for the reportSummary
0291   float sumOfNonNegBins = 0.;
0292   //Now we will use the other summary maps to create the overall map.
0293   //For now we only want to do this offline
0294   if (!runOnEndLumi_) {
0295     for (int i = 0; i < 12; i++) {  // !??!?!? xAxisLabels_.size() ?!?!
0296       if (summaryMap_["Grand"] == nullptr) {
0297         edm::LogWarning("SiPixelPhase1Summary") << "Grand summary does not exist!";
0298         break;
0299       }
0300       for (int j = 0; j < 4; j++) {  // !??!?!? yAxisLabels_.size() ?!?!?!
0301         summaryMap_["Grand"]->setBinContent(
0302             i + 1,
0303             j + 1,
0304             1);  // This resets the map to be good. We only then set it to 0 if there has been a problem in one of the other summaries.
0305         for (auto const& mapInfo : summaryPlotName_) {  //Check summary maps
0306           auto name = mapInfo.first;
0307           if (summaryMap_[name] == nullptr) {
0308             edm::LogWarning("SiPixelPhase1Summary") << "Summary " << name << " does not exist!";
0309             continue;
0310           }
0311           if (summaryMap_["Grand"]->getBinContent(i + 1, j + 1) > summaryMap_[name]->getBinContent(i + 1, j + 1))
0312             summaryMap_["Grand"]->setBinContent(i + 1, j + 1, summaryMap_[name]->getBinContent(i + 1, j + 1));
0313         }
0314         if (summaryMap_["Grand"]->getBinContent(i + 1, j + 1) > -0.1)
0315           sumOfNonNegBins += summaryMap_["Grand"]->getBinContent(i + 1, j + 1);
0316       }
0317     }
0318     reportSummary->Fill(sumOfNonNegBins / 40.);  // The average of the 40 useful bins in the summary map.
0319   }
0320 
0321   //Fill the new overall map
0322   //  if (!runOnEndLumi_) return;
0323   else {  //Do this for online only
0324     for (int i = 0; i < 2; i++) {
0325       if (summaryMap_["Grand"] == nullptr) {
0326         edm::LogWarning("SiPixelPhase1Summary") << "Grand summary does not exist!";
0327         break;
0328       }
0329       for (int j = 0; j < 4; j++) {  // !??!?!? yAxisLabels_.size() ?!?!?!
0330         //Ignore the bins without detectors in them
0331         if (i == 1 && j > 1) {
0332           summaryMap_["Grand"]->setBinContent(i + 1, j + 1, -1);
0333         } else {
0334           if (deadROCSummary->getBinContent(i + 1, j + 1) < deadRocWarnThresholds_[i * 4 + j])
0335             summaryMap_["Grand"]->setBinContent(i + 1, j + 1, 1);
0336 
0337           else if (deadROCSummary->getBinContent(i + 1, j + 1) > deadRocWarnThresholds_[i * 4 + j] &&
0338                    deadROCSummary->getBinContent(i + 1, j + 1) < deadRocThresholds_[i * 4 + j])
0339             summaryMap_["Grand"]->setBinContent(i + 1, j + 1, 0.8);
0340 
0341           else
0342             summaryMap_["Grand"]->setBinContent(i + 1, j + 1, 0);
0343 
0344           sumOfNonNegBins += summaryMap_["Grand"]->getBinContent(i + 1, j + 1);
0345         }
0346       }
0347     }
0348   }
0349 }
0350 //------------------------------------------------------------------
0351 // Fill the trend plots
0352 //------------------------------------------------------------------
0353 void SiPixelPhase1Summary::fillTrendPlots(DQMStore::IBooker& iBooker, DQMStore::IGetter& iGetter, int lumiSec) {
0354   // If we're running in online mode and the lumi section is not modulo 10, return. Offline running always uses lumiSec=0, so it will pass this test.
0355   if (lumiSec % 10 != 0)
0356     return;
0357 
0358   if (runOnEndLumi_) {
0359     MonitorElement* nClustersAll = iGetter.get("PixelPhase1/Phase1_MechanicalView/num_clusters_per_Lumisection_PXAll");
0360     if (nClustersAll == nullptr) {
0361       edm::LogWarning("SiPixelPhase1Summary") << "All pixel cluster trend plot not available!!";
0362       return;
0363     }
0364     if (nClustersAll->getTH1()->GetBinContent(lumiSec) < 100)
0365       return;
0366   }
0367 
0368   std::string histName;
0369 
0370   //Find the total number of filled bins and hi efficiency bins
0371   std::vector<trendPlots> trendOrder = {layer1, layer2, layer3, layer4, ring1, ring2};
0372   std::vector<int> nFilledROCs(trendOrder.size(), 0);
0373   std::vector<int> hiEffROCs(trendOrder.size(), 0);
0374   std::vector<int> nRocsPerTrend = {1536, 3584, 5632, 8192, 4224, 6528};
0375   std::vector<string> trendNames = {};
0376 
0377   for (auto it : {1, 2, 3, 4}) {
0378     histName = "PXBarrel/digi_occupancy_per_SignedModuleCoord_per_SignedLadderCoord_PXLayer_" + std::to_string(it);
0379     trendNames.push_back(histName);
0380   }
0381   for (auto it : {1, 2}) {
0382     histName = "PXForward/digi_occupancy_per_SignedDiskCoord_per_SignedBladePanelCoord_PXRing_" + std::to_string(it);
0383     trendNames.push_back(histName);
0384   }
0385   //Loop over layers. This will also do the rings, but we'll skip the ring calculation for
0386   for (unsigned int trendIt = 0; trendIt < trendOrder.size(); trendIt++) {
0387     iGetter.cd();
0388     histName = "PixelPhase1/Phase1_MechanicalView/" + trendNames[trendIt];
0389     MonitorElement* tempLayerME = iGetter.get(histName);
0390     if (tempLayerME == nullptr)
0391       continue;
0392     float lowEffValue = 0.25 * (tempLayerME->getTH1()->Integral() / nRocsPerTrend[trendIt]);
0393     for (int i = 1; i <= tempLayerME->getTH1()->GetXaxis()->GetNbins(); i++) {
0394       for (int j = 1; j <= tempLayerME->getTH1()->GetYaxis()->GetNbins(); j++) {
0395         if (tempLayerME->getBinContent(i, j) > 0.)
0396           nFilledROCs[trendIt]++;
0397         if (tempLayerME->getBinContent(i, j) > lowEffValue)
0398           hiEffROCs[trendIt]++;
0399       }
0400     }
0401     if (runOnEndLumi_) {
0402       tempLayerME->Reset();  //If we're doing online monitoring, reset the digi maps.
0403     }
0404   }  // Close layers/ring loop
0405 
0406   if (!runOnEndLumi_) {  //offline
0407     for (unsigned int i = 0; i < trendOrder.size(); i++) {
0408       deadROCTrends_[offline]->Fill(i, nRocsPerTrend[i] - nFilledROCs[i]);
0409       ineffROCTrends_[offline]->Fill(i, nFilledROCs[i] - hiEffROCs[i]);
0410     }
0411   } else {  //online
0412     for (unsigned int i = 0; i < trendOrder.size(); i++) {
0413       deadROCTrends_[trendOrder[i]]->Fill(lumiSec - 1, nRocsPerTrend[i] - nFilledROCs[i]);
0414       ineffROCTrends_[trendOrder[i]]->Fill(lumiSec - 1, nFilledROCs[i] - hiEffROCs[i]);
0415     }
0416   }
0417 
0418   if (!runOnEndLumi_)
0419     return;  // The following only occurs in the online
0420   //Reset some MEs every 10LS here
0421   for (auto it : {1, 2, 3, 4}) {  //PXBarrel
0422     histName = "PixelPhase1/Phase1_MechanicalView/PXBarrel/clusterposition_zphi_PXLayer_" + std::to_string(it);
0423     MonitorElement* toReset = iGetter.get(histName);
0424     if (toReset != nullptr) {
0425       toReset->Reset();
0426     }
0427   }
0428   for (auto it : {"-3", "-2", "-1", "+1", "+2", "+3"}) {  //PXForward
0429     histName = "PixelPhase1/Phase1_MechanicalView/PXForward/clusterposition_xy_PXDisk_" + std::string(it);
0430     MonitorElement* toReset = iGetter.get(histName);
0431     if (toReset != nullptr) {
0432       toReset->Reset();
0433     }
0434   }
0435 }
0436 
0437 //define this as a plug-in
0438 DEFINE_FWK_MODULE(SiPixelPhase1Summary);