Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MulticopyProcessor.cxx 11.46 KiB
/*
  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
*/

//
//   multicopy element processor.
//
//   First time round, a loop creates all the Geo-transforms needed and stores them. First and subsequent calls, 
//   a second loop adds these to the toAdd list, along with copies of the object. The object-handler stores and 
//   deals with the object. This si needed to make sure sensitive volumes and alignable volumes are always added 
//   correctly. 
//
//   The copy number of the copied object can be zeroed each time the multicopy-element is processed if the 
//   zeroid attribute is set true. This has to be done here - if the attribute is given on a logvol or assembly ref, 
//   it would alwasy be zero.
//
//   There are two ways of making the n transformations:
//        loopvar attribute not set:    The transformation is raised to the power i-1
//        loopvar set to a vector name: In the loop, before each transformation is created, the vector generic name is set
//                                      equal to the next element of the vector.
//
#include "GeoModelXml/MulticopyProcessor.h"

#include "OutputDirector.h"
#include <sstream>
#include <string>
#include <cstdlib>
#include <xercesc/dom/DOM.hpp>
#include "GeoModelKernel/GeoTransform.h"
#include "GeoModelKernel/GeoIdentifierTag.h"
#include "GeoModelKernel/GeoVFullPhysVol.h"
#include "GeoModelKernel/GeoNameTag.h"
#include "GeoModelKernel/GeoDefinitions.h"

#include "xercesc/util/XMLString.hpp"
#include "GeoModelXml/GeoNodeList.h"
#include "GeoModelXml/ProcessorRegistry.h"
#include "GeoModelXml/GmxUtil.h"


using namespace xercesc;
using namespace std;

void MulticopyProcessor::process(const DOMElement *element, GmxUtil &gmxUtil, GeoNodeList &toAdd) {
char *toRelease;
XMLCh *ref = XMLString::transcode("ref");
XMLCh * alignable_tmp = XMLString::transcode("alignable");
const XMLCh *idref;
DOMDocument *doc = element->getOwnerDocument();

    bool alignable = element->hasAttribute(alignable_tmp);
//
//    How many copies?
//
    int nCopies;
    XMLCh * n_tmp = XMLString::transcode("n");
    toRelease = XMLString::transcode(element->getAttribute(n_tmp));
    nCopies = gmxUtil.evaluate(toRelease);
    XMLString::release(&toRelease);
    XMLString::release(&n_tmp);
//
//    See if it is in the map; if so, xfList is already done. If not, fill xfList.
//
    XMLCh * name_tmp = XMLString::transcode("name");
    toRelease = XMLString::transcode(element->getAttribute(name_tmp));
    string name(toRelease);
    XMLString::release(&toRelease);
    XMLString::release(&name_tmp);
    map<string, GeoNodeList>::iterator entry;
    GeoNodeList *xfList;
    if ((entry = m_map.find(name)) == m_map.end()) { // Not in registry; make a new item
//
//    Add empty node list to the map
//
        m_map[name] = GeoNodeList();
        xfList = &m_map[name];
//
//    Loopvar Variable name
//
        string varname, firstElement;
	XMLCh * loopvar_tmp = XMLString::transcode("loopvar");
        bool hasVarname = (element->getAttributeNode(loopvar_tmp) != 0);
        if (hasVarname) {
            toRelease = XMLString::transcode(element->getAttribute(loopvar_tmp));
            varname = toRelease;
            XMLString::release(&toRelease);
	    XMLString::release(&loopvar_tmp);
            // Check it is a vector
            firstElement = varname + "_0";
            if (!gmxUtil.eval.findVariable(firstElement.c_str())) {
                msglog << MSG::FATAL << "Error in .gmx file. Processing multicopy element with name " << name << 
                ". Found loopvar set to " << varname << ", but no vector with that name has been defined." << endmsg;
		std::abort();
            }
        }
//
//    Get the transformation-element
//
        DOMElement *elXf = element->getFirstElementChild();
        toRelease = XMLString::transcode(elXf->getNodeName());
        string nodeName(toRelease);
        XMLString::release(&toRelease);
        Element2GeoItem *xFormProcessor;
        if (hasVarname) {
            if (nodeName == "transformation") { // OK
                xFormProcessor = (Element2GeoItem *) &(gmxUtil.tagHandler.transformation);
            }
            else { // Not OK
                msglog << MSG::FATAL << "Error in .gmx file. Processing multicopy element with name " << name <<
                ". \nIt gives loopvar therefore should have a <transformation> and not a <transformationref> (despite the DTD)\n";
		std::abort();
            }
        }
        else {
            xFormProcessor = nodeName == "transformation"?
                             (Element2GeoItem *) &(gmxUtil.tagHandler.transformation):
                             (Element2GeoItem *) &(gmxUtil.tagHandler.transformationref);
        }
//
//    Produce all the transformations
//
        GeoAlignableTransform *geoAXf;
        GeoTransform *geoXf;
        if (hasVarname) {
            for (int i = 0; i < nCopies; ++i) {
                gmxUtil.eval.setVariable(varname.c_str(), (varname + "_" + to_string(i)).c_str());
                if (alignable) {
		  geoAXf = static_cast<GeoAlignableTransform *>( xFormProcessor->make(elXf, gmxUtil));
		  xfList->push_back((GeoGraphNode *) geoAXf);
                }
                else {
		  geoXf = static_cast<GeoTransform *>( xFormProcessor->make(elXf, gmxUtil));
		  xfList->push_back((GeoGraphNode *) geoXf);
                }
                gmxUtil.eval.removeVariable(varname.c_str()); // Avoids a warning status in evaluator
            }
        }
        else {
//
//    If varname not given, we get the CLHEP xForm and raise it to the power i, so NOT applied to first object.
//    No transform (i.e. identity) for the first; so one less transform than objects
//
	  GeoTrf::Transform3D hepXf0=GeoTrf::Transform3D::Identity();
            if (alignable) {
	      geoAXf = static_cast<GeoAlignableTransform *>( xFormProcessor->make(elXf, gmxUtil));
	      hepXf0 = geoAXf->getTransform();
            }
            else {
	      geoXf = static_cast<GeoTransform *>( xFormProcessor->make(elXf, gmxUtil));
	      hepXf0 = geoXf->getTransform();
            }
            GeoTrf::Transform3D hepXf=GeoTrf::Transform3D::Identity(); // Identity initially
            for (int i = 0; i < nCopies; ++i) {
                xfList->push_back(makeTransform(hepXf));
                hepXf = hepXf0 * hepXf;
            }
        }
    }
    else {
        xfList = &entry->second;
    }
//
//    Get object to be copied
//
    DOMElement *object = element->getLastElementChild();
    toRelease = XMLString::transcode(object->getNodeName());
    string nodeName(toRelease);
    XMLString::release(&toRelease);
    ElementProcessor *objectProcessor = gmxUtil.processorRegistry.find(nodeName);
//
//    Zero its copy number. Only needed if an item is used in 2 or more multicopies;
//    harmless in case of only one use.
//
    if (nodeName == "logvolref") {
        idref = object->getAttribute(ref);
        DOMElement *elem = doc->getElementById(idref);
        gmxUtil.tagHandler.logvol.zeroId(elem);
    }
    else if (nodeName == "assemblyref") {
        idref = object->getAttribute(ref);
        DOMElement *elem = doc->getElementById(idref);
        gmxUtil.tagHandler.assembly.zeroId(elem);
    }
    else if (nodeName == "transform") { // Object is either an assemlyref or logvolref, and there is only one of these
      XMLCh * logvolref_tmp = XMLString::transcode("logvolref");
      DOMNodeList *lvList = element->getElementsByTagName(logvolref_tmp);
      if (lvList->getLength() > 0) {
	const XMLCh *idref =  dynamic_cast<DOMElement *> (lvList->item(0))->getAttribute(ref);
	DOMElement *lv = doc->getElementById(idref);
	gmxUtil.tagHandler.logvol.zeroId(lv);
	XMLString::release(&logvolref_tmp);
      }
      else {
	XMLCh * assemblyref_tmp = XMLString::transcode("assemblyref") ;
	DOMNodeList *asList = element->getElementsByTagName(assemblyref_tmp);
	if (asList->getLength() > 0) {
	  const XMLCh *idref =  dynamic_cast<DOMElement *> (asList->item(0))->getAttribute(ref);
	  DOMDocument *doc = element->getOwnerDocument();
	  DOMElement *as = doc->getElementById(idref);
	  gmxUtil.tagHandler.assembly.zeroId(as);
	  XMLString::release(&assemblyref_tmp);
	}
	else {
	  msglog << MSG::FATAL << 
	    "multicopyprocessor: error in " << name << ". <transform> object was neither assemblyref nor logvolref\n"
                    << "Exiting program" << endmsg;
	  std::abort();
	}
      }
    }
//
//    If alignable, add transformation to DetectorManager via GmxInterface.
//    (NB. Alignable is messy because it involves both a transformation and an object as well as
//    the multicopy or transform element to tell us when to insert it and what level of alignment 
//    is wanted; and passing info from one routine to another when you try to keep a uniform interface is 
//    difficult; and we only have partial GeoModel trees so we cannot use GeoModel tree traversal at this 
//    moment (comes back to no way of knowing if we have a GeoFullPV or GeoPV)).
//
//    Also: no one is using it at the time of writing, so it is ***TOTALLY*** untested.
//    It is done now to (i) make sure there ought to be a solution (ii) implement (imperfectly) 
//    a way now while things are fresh in my mind.
//
    int level;
    if (alignable) {
        istringstream(XMLString::transcode(element->getAttribute(alignable_tmp))) >> level;
    }
//
//    Add transforms and physvols etc. to list to be added
//
    map<string, int> index;
    for (int copy = 0; copy < nCopies; ++copy) {
        toAdd.push_back((*xfList)[copy]);
        int lastTransform = toAdd.size() - 1;
        objectProcessor->process(object, gmxUtil, toAdd);
        if (alignable) {

            msglog << "copy = " << copy << "; level = " << level << endmsg;
            msglog << "Add Alignable named ";
            msglog << ((GeoNameTag *) (toAdd[lastTransform + 1]))->getName();
            msglog << " with id ";
            msglog << ((GeoIdentifierTag *) (toAdd[lastTransform + 2]))->getIdentifier() << endmsg;

            gmxUtil.positionIndex.incrementLevel(); // Logvol has unfortunately already decremented this; temp. restore it
            gmxUtil.positionIndex.indices(index, gmxUtil.eval);
            //splitting sensors where we would like multiple DetectorElements per GeoVFullPhysVol (e.g.ITk Strips)
            XMLCh * splitLevel_tmp = XMLString::transcode("splitLevel");
            bool split = element->hasAttribute(splitLevel_tmp);
            char* splitString;
	        int splitLevel = 1;
	        if (split) {
                splitString = XMLString::transcode(element->getAttribute(splitLevel_tmp));
                splitLevel = gmxUtil.evaluate(splitString);
                XMLString::release(&splitString);
                for(int i=0;i<splitLevel;i++){
                    std::string field = "eta_module";//eventually specify in Xml the field to split in?
                    std::pair<std::string,int> extraIndex(field,i);
                    gmxUtil.gmxInterface()->addSplitAlignable(level, index, extraIndex, (GeoVFullPhysVol *) toAdd[lastTransform + 3],
                                                    (GeoAlignableTransform *) toAdd[lastTransform]);
                }
            }
            else gmxUtil.gmxInterface()->addAlignable(level, index, (GeoVFullPhysVol *) toAdd[lastTransform + 3],
                                                    (GeoAlignableTransform *) toAdd[lastTransform]);
            gmxUtil.positionIndex.decrementLevel(); // Put it back where it was
            index.clear();
            XMLString::release(&splitLevel_tmp);
        }
    }

    XMLString::release(&ref);
    XMLString::release(&alignable_tmp);

}