def __iadd__(self, other):
        """
        'Magic' method to implement the '+=' operator (eg struc1 += struc2)
        
        Compare global IDs of particles and reassign globalIDs for particle
        container using the max ID between the two lists. Tracks these changes
        for all other (bond, angle, dihedral) containers that reference particleIDs
        """

        self.verbose = False

        # Empty container checks
        if len(other) == 0:  # If struc2 is empty (has no particles)
            return self  #   simply return unchanged current container
        if len(self
               ) == 0:  # If struc1 (this struc) is empty (has no particles)
            return copy.deepcopy(other)  #    full copy other and return

        #
        #  Special method for connecting building blocks
        #
        # Count number of connectors
        connect_along_bond = False
        if (self.type != "mol" and other.type != "mol"):
            # IF not complete molecules that will just be added
            #   connect along bonds
            connect_along_bond = True

            self.cnt_connectors()
            other.cnt_connectors()
            if (self.verbose):
                log_line = "\n building block 1 type {}  ".format(self.type)
                log_line += "\n   building block 1 has {} connections ".format(
                    self.connect_cnt)
                log_line += "\n   building block 1 has {} func connections ".format(
                    self.func_connect_cnt)
                log_line += "\n building block 2 type {}  ".format(other.type)
                log_line += "\n   building block 2 has {} connections ".format(
                    other.connect_cnt)
                log_line += "\n   building block 2 has {} func connections ".format(
                    other.func_connect_cnt)
                print log_line

            self.connect_id = "term_"
            self.cap_id = "termcap_"
            other.connect_id = "term_"
            other.cap_id = "termcap_"
            if (self.type == "unit" and other.type == "unit"):
                self.connectionpoint = 1
                other.connectionpoint = 0
                new_termpoint = 1
            elif (self.type == "term" and other.type == "unit"):
                # elif( self.connect_cnt == 1 and other.connect_cnt  == 2 ):
                self.connectionpoint = 0
                other.connectionpoint = 0
                new_termpoint = 0
            elif (self.type == "term" and other.type == "term"):
                #        elif( self.connect_cnt == 1 and other.connect_cnt  == 1 ):
                self.connectionpoint = 0
                other.connectionpoint = 0
                new_termpoint = -1
            elif (self.type == "unit" and other.type == "term"):
                #elif( self.connect_cnt == 2 and other.connect_cnt  == 1 ):
                self.connectionpoint = 1
                other.connectionpoint = 0
                new_termpoint = -1
            elif (self.type == "unit" and other.type == "func"):
                #elif( self.connect_cnt == 2 and other.connect_cnt  == 1 ):
                self.connect_id = "func_"
                self.cap_id = "funccap_"
                # this is zero since each functional possition will be removed once a functional group is added
                self.connectionpoint = 0
                other.connect_id = "term_"
                other.connectionpoint = 0
                new_termpoint = -1
            else:
                error_line = " Unknow connection configuration {} - {}  \n".format(
                    self.type, other.type)
                error_line += " building block 1 has {} connnections \n".format(
                    self.connect_cnt)
                error_line += " building block 2 has {} connnections \n".format(
                    other.connect_cnt)
                sys.exit(error_line)

            if (self.verbose):
                log_line = "\n Adding connection along bond "
                log_line += "\n  Connecting block 2 {} at point {} ".format(
                    other.connect_id, other.connectionpoint)
                log_line += "\n  to  block 1 {} at point {} ".format(
                    self.connect_id, self.connectionpoint)
                print log_line

            # Shift second buildingblock so first connector is bonded to the second connector of  first buildingblock
            if (self.verbose): print " Shift  along bonds "
            self.align_termbond(self.connectionpoint, origin="term")
            other.align_termbond(other.connectionpoint, origin="termcap")

            if (self.verbose): print " Find terminal atoms  "
            self.get_connectors(self.connectionpoint)
            other.get_connectors(other.connectionpoint)
            bond_length = np.array([
                self.ptclC[self.pid_term].radii +
                other.ptclC[other.pid_term].radii, 0.0, 0.0
            ])

            # Shift second building block to have the correct bond length
            other.ptclC.shift(
                (bond_length -
                 1.0 * np.array(other.ptclC[other.pid_term].position)))

            # Delete termcaps
            self.bondC.deletepid(self.pid_termcap)
            other.bondC.deletepid(other.pid_termcap)
            del self.ptclC[self.pid_termcap]
            del other.ptclC[other.pid_termcap]

            # Reset term tags to null
            self.ptclC[self.pid_term].tagsDict["cplytag"] = ""
            other.ptclC[other.pid_term].tagsDict["cplytag"] = ""

            # If an intern ring connection is made change the fftype
            if (self.ptclC[self.pid_term].tagsDict["ring"] > 0
                    and other.ptclC[other.pid_term].tagsDict["ring"] > 0):
                #print " ring self ", self.ptclC[self.pid_term].tagsDict["ring"]
                #print " ring other ", other.ptclC[other.pid_term].tagsDict["ring"]

                self.ptclC[self.pid_term].tagsDict["fftype"] = 'C!'
                other.ptclC[other.pid_term].tagsDict["fftype"] = 'C!'

                # sys.exit(" ring test ")

        idFromToDict = dict(
        )  # Need to keep track of all ptcl ID changes at once
        # {fromID1:toID1, fromID2:toID2...}
        # eg {1:3, 3:5, 2:20...}

        bondC = BondContainer()  # Local bond container copy so ptclIDs
        angleC = AngleContainer()  # Local angle container copy so ptclIDs
        dihC = DihedralContainer()  # Local dihedral container copy so ptclIDs

        bondC = copy.deepcopy(
            other.bondC)  #  inside can be changed (for adding below)
        angleC = copy.deepcopy(
            other.angleC)  #  inside can be changed (for adding below)
        dihC = copy.deepcopy(
            other.dihC)  #  inside can be changed (for adding below)
        impC = copy.deepcopy(
            other.impC)  #  inside can be changed (for adding below)

        keys1 = self.ptclC.particles.keys(
        )  # global IDs of particles in this object
        keys2 = other.ptclC.particles.keys(
        )  # global IDs in object being added
        self.ptclC.maxgid = max(
            keys1 + keys2)  # find max globalID in keys, set this object maxID

        self.get_max()
        other.get_max()
        # Update chain, residue and charge group number
        if (self.max_chain == other.max_chain):

            other.shift_tag("qgroup", self.max_qgroup)
            other.shift_tag("residue", self.max_residue)
            other.shift_tag("ring", self.max_ring)
        else:
            other.add_chain(self.max_chain)

        for ptclkey2 in other.ptclC.particles:
            self.ptclC.put(other.ptclC.particles[ptclkey2]
                           )  # Pushes ptcl to this struc's ptcl container
            fromPtclID = ptclkey2  # Track IDs from--->to
            toPtclID = self.ptclC.maxgid  #  --> toID (to is the maxid of this ptclC)

            idFromToDict[fromPtclID] = toPtclID  # Store ID changes

        if (connect_along_bond):
            # Add inter building block bond
            if (self.verbose): print " Add inter building block bond "
            bb_bb = Bond(self.pid_term, idFromToDict[other.pid_term])
            self.bondC.put(bb_bb)

        bondC.replacePtclIDs(idFromToDict)  # Use tracked list of ID changes
        self.bondC += bondC  # Now add bondC with 'corrected' IDs

        angleC.replacePtclIDs(
            idFromToDict)  # Now add angleC with 'corrected' IDs
        self.angleC += angleC  # Use tracked list of ID changes

        dihC.replacePtclIDs(idFromToDict)  # Use tracked list of ID changes
        self.dihC += dihC  # Now add dihC with 'corrected' IDs

        impC.replacePtclIDs(idFromToDict)  # Use tracked list of ID changes
        self.impC += impC  # Now add impC with 'corrected' IDs

        if (connect_along_bond):
            self.compressPtclIDs()
            self.bondC_nblist()
            if (new_termpoint >= 0):

                if (self.verbose): print " Add new cplytag to  "

                self.get_connectors(new_termpoint)
                self.ptclC[
                    self.pid_term].tagsDict["cplytag"] = "term_C({})".format(
                        self.pid_term)
                self.ptclC[self.pid_termcap].tagsDict[
                    "cplytag"] = "termcap_H({})_on_C({})".format(
                        self.pid_termcap, self.pid_term)

        # Append segments
        for seg_i in other.segments:
            self.segments.append(seg_i)

        return self
def main():
    """
    This shows operations on empty StructureContainer objects for 'robustness'
    """
    print "************************************************************************************"
    print " This shows operations on empty StructureContainer objects for 'robustness'"
    print "************************************************************************************ \n"

    atoms1 = ParticleContainer()
    bonds1 = BondContainer()

    p1 = Particle([0.2, 1.3, 33.0], "Si", 2.0, 1.23)
    p2 = Particle([5.0, 2.3, -22.1], "C", 1.0, 2.34)
    p3 = Particle([5.0, 2.3, -20.1], "C", 1.0, 2.34)
    b1 = Bond(1, 2, 1.233, "hooke")
    b2 = Bond(2, 3, 0.500, "hooke")

    atoms1.put(p1)
    atoms1.put(p2)
    atoms1.put(p3)
    bonds1.put(b1)
    bonds1.put(b2)

    atoms2 = ParticleContainer()
    bonds2 = BondContainer()

    polymer1 = StructureContainer(atoms1, bonds1)  # Non-empty structure 1
    polymer2 = StructureContainer(atoms2, bonds2)  # Empty     structure 2

    # ------------------------------------------------------------------------------------------------------------------------

    print "Non-empty container", polymer1
    print "Empty container", polymer2

    print "Testing empty dump"
    polymer2.dump("empty")

    print "Testing empty compressPtclIDs"
    polymer2.compressPtclIDs()

    print "Testing empty getSubStructure"
    subStruc = polymer2.getSubStructure([])
    print "subStruc = ", subStruc

    print "Testing empty getPtclPositions"
    posList = polymer2.getPtclPositions()
    print "posList = ", posList

    print "------------------------------------------------------------------------------------ \n"

    print "Testing struc add --> non-empty (polymer1) += empty (polymer2)"
    polymer1 += polymer2
    print "polymer1 = ", polymer1
    print "------------------------------------------------------------------------------------ \n"
    print "polymer2 = ", polymer2

    print "------------------------------------------------------------------------------------ \n"

    print "Testing struc add --> empty (polymer2) += non-empty (polymer1)"
    polymer2 += polymer1
    print "polymer2 = ", polymer2
    print "------------------------------------------------------------------------------------ \n"
    print "polymer1 = ", polymer1

    print "------------------------------------------------------------------------------------ \n"

    print "Testing empty getSubStructure with non-zero id list (should return ERROR)"
    polymer3 = StructureContainer()
    subStruc = polymer3.getSubStructure([1, 2])
    print "subStruc = ", subStruc
    def read_cply(self, cply_file, debug=False):
        """
        Read cply file
        """

        # Load periodic table
        pt = periodictable()

        read_lattice = False
        read_segment = False

        with open(cply_file) as f:
            for line in f:
                col = line.split()
                if (read_lattice):
                    self.latvec[lv_cnt][0] = float(col[0])
                    self.latvec[lv_cnt][1] = float(col[1])
                    self.latvec[lv_cnt][2] = float(col[2])
                    if (lv_cnt == 2):
                        read_lattice = False
                    lv_cnt += 1
                elif (read_segment):
                    if (debug):
                        print " Readin degment line ", col

                    if (str(col[0]) == 'unit'):
                        if (len(col) > 2):
                            error_line = "Each segment can only have 1 unit "
                            error_line += "\n {} in unit line will be ignored ".format(
                                str(col[2::]))
                            print error_line
                        segment_i['unit'] = col[1]
                    if (str(col[0]) == 'func'):
                        segment_i['func'] = col[1::]
                    if (str(col[0]) == 'end'):
                        read_segment = False

                elif (len(col) >= 4 and col[0] != "bond" and col[0] != "#"):

                    pt_i = Particle()
                    pt_i.type = str(col[0])
                    pt_i.position = [
                        float(col[1]),
                        float(col[2]),
                        float(col[3])
                    ]

                    add_dict = pt_i.tagsDict
                    el = pt.getelementWithSymbol(str(col[0]))
                    add_dict["symbol"] = str(col[0])
                    add_dict["number"] = el.number
                    add_dict["mass"] = el.mass

                    add_dict["cplytag"] = ""
                    add_dict["linkid"] = ""
                    add_dict["link"] = ""
                    pt_i.mass = el.mass
                    add_dict["chain"] = 1
                    add_dict["ring"] = 0
                    add_dict["residue"] = 1
                    add_dict["resname"] = "RES"
                    add_dict["qgroup"] = 1
                    add_dict["fftype"] = "??"
                    add_dict["label"] = el.symbol
                    add_dict["cov_radii"] = el.cov_radii
                    add_dict["vdw_radii"] = el.vdw_radii
                    add_dict["lmptype"] = -1
                    if (len(col) >= 14):
                        add_dict["label"] = str(col[4])
                        add_dict["fftype"] = str(col[5])
                        add_dict["mass"] = float(col[6])
                        pt_i.mass = float(col[6])
                        pt_i.charge = float(col[7])
                        add_dict["qgroup"] = int(col[8])
                        add_dict["ring"] = int(col[9])
                        add_dict["residue"] = int(col[10])
                        add_dict["resname"] = str(col[11])
                        add_dict["chain"] = int(col[12])
                        add_dict["cplytag"] = str(col[13])
                    elif (len(col) == 13):
                        add_dict["label"] = str(col[4])
                        add_dict["fftype"] = str(col[5])
                        add_dict["mass"] = float(col[6])
                        pt_i.mass = float(col[6])
                        pt_i.charge = float(col[7])
                        add_dict["qgroup"] = int(col[8])
                        add_dict["ring"] = int(col[9])
                        add_dict["residue"] = int(col[10])
                        add_dict["resname"] = str(col[11])
                        add_dict["chain"] = int(col[12])
                    elif (len(col) == 8):
                        add_dict["residue"] = int(col[5])
                        add_dict["resname"] = str(col[6])
                        add_dict["cplytag"] = str(col[7])
                    elif (len(col) == 7):
                        pt_i.charge = float(col[4])
                        add_dict["residue"] = int(col[5])
                        add_dict["resname"] = str(col[6])
                    elif (len(col) == 5):
                        add_dict["cplytag"] = str(col[4])

                    pt_i.setTagsDict(add_dict)
                    self.ptclC.put(pt_i)

                    # # # print "debug pt_i ",len(col),pt_i

                elif (len(col) >= 3):
                    if (col[0] == "bond"):
                        b_i = int(col[1])
                        b_j = int(col[2])
                        bnd = Bond(b_i, b_j)
                        #print "process_line bond line ",col
                        self.bondC.put(bnd)
                # Key word search
                if (len(col) > 0):
                    if (str(col[0]) == 'lattice'):
                        read_lattice = True
                        lv_cnt = 0
                    if (str(col[0]) == 'segment'):
                        read_segment = True
                        segment_i = {}  #segment(str(col[1]))
                        # segment_i = segment()
                        segment_i['tag'] = str(col[1])
                        segment_i['segment'] = {}
                        self.segments.append(segment_i)
        if (debug):
            print segments
            sys.exit("debug in read_cply is True ")