Example #1
0
def _validate_connection_members(connection_members):
    """Ensure all elements of entered connection_members are gmso.Site"""
    for partner in connection_members:
        if not isinstance(partner, Site):
            raise GMSOError("Supplied non-Site {}".format(partner))

    if len(set(connection_members)) != len(connection_members):
        raise GMSOError("Error, cannot add connection between same sites.")
    return tuple(connection_members)
Example #2
0
def _validate_two_member_type_names(types):
    """Ensure exactly 2 partners are involved in BondType"""
    if len(types) != 2 and len(types) != 0:
        raise GMSOError("Trying to create a BondType "
                        "with {} constituent types".format(len(types)))
    if not all([isinstance(t, str) for t in types]):
        raise GMSOError("Types passed to BondType "
                        "need to be strings corresponding to AtomType names")

    return types
Example #3
0
def convert_ryckaert_to_opls(ryckaert_connection_type):
    """Convert Ryckaert-Bellemans dihedral to OPLS.

    NOTE: the conventions defining the dihedral angle are different
    for OPLS and RB torsions. OPLS torsions are defined with
    phi_cis = 0 while RB torsions are defined as phi_trans = 0.
    """
    templates = PotentialTemplateLibrary()
    ryckaert_bellemans_torsion_potential = templates[
        "RyckaertBellemansTorsionPotential"]
    opls_torsion_potential = templates["OPLSTorsionPotential"]

    valid_connection_type = False
    if (ryckaert_connection_type.independent_variables ==
            ryckaert_bellemans_torsion_potential.independent_variables):
        if (sympy.simplify(ryckaert_connection_type.expression -
                           ryckaert_bellemans_torsion_potential.expression) ==
                0):
            valid_connection_type = True
    if not valid_connection_type:
        raise GMSOError("Cannot use convert_ryckaert_to_opls "
                        "function to convert a ConnectionType that is not an "
                        "RyckaertBellemansTorsionPotential")

    c0 = ryckaert_connection_type.parameters["c0"]
    c1 = ryckaert_connection_type.parameters["c1"]
    c2 = ryckaert_connection_type.parameters["c2"]
    c3 = ryckaert_connection_type.parameters["c3"]
    c4 = ryckaert_connection_type.parameters["c4"]
    c5 = ryckaert_connection_type.parameters["c5"]

    if c5 != 0.0:
        raise GMSOError("Cannot convert Ryckaert-Bellemans dihedral "
                        "to OPLS dihedral if c5 is not equal to zero.")

    converted_params = {
        "k0": 2.0 * (c0 + c1 + c2 + c3 + c4),
        "k1": (-2.0 * c1 - (3.0 / 2.0) * c3),
        "k2": (-c2 - c4),
        "k3": ((-1.0 / 2.0) * c3),
        "k4": ((-1.0 / 4.0) * c4),
    }

    name = opls_torsion_potential.name
    expression = opls_torsion_potential.expression
    variables = opls_torsion_potential.independent_variables

    opls_connection_type = gmso.DihedralType(
        name=name,
        expression=expression,
        independent_variables=variables,
        parameters=converted_params,
    )

    return opls_connection_type
Example #4
0
def convert_ryckaert_to_opls(ryckaert_connection_type):
    """Convert Ryckaert-Bellemans dihedral to OPLS

    NOTE: the conventions defining the dihedral angle are different
    for OPLS and RB torsions. OPLS torsions are defined with
    phi_cis = 0 while RB torsions are defined as phi_trans = 0.
    """
    templates = PotentialTemplateLibrary()
    ryckaert_bellemans_torsion_potential = templates[
        'RyckaertBellemansTorsionPotential']
    opls_torsion_potential = templates['OPLSTorsionPotential']

    valid_connection_type = False
    if (ryckaert_connection_type.independent_variables ==
            ryckaert_bellemans_torsion_potential.independent_variables):
        if sympy.simplify(
                ryckaert_connection_type.expression -
                ryckaert_bellemans_torsion_potential.expression) == 0:
            valid_connection_type = True
    if not valid_connection_type:
        raise GMSOError('Cannot use convert_ryckaert_to_opls '
                        'function to convert a ConnectionType that is not an '
                        'RyckaertBellemansTorsionPotential')

    c0 = ryckaert_connection_type.parameters['c0']
    c1 = ryckaert_connection_type.parameters['c1']
    c2 = ryckaert_connection_type.parameters['c2']
    c3 = ryckaert_connection_type.parameters['c3']
    c4 = ryckaert_connection_type.parameters['c4']
    c5 = ryckaert_connection_type.parameters['c5']

    if c5 != 0.0:
        raise GMSOError('Cannot convert Ryckaert-Bellemans dihedral '
                        'to OPLS dihedral if c5 is not equal to zero.')

    converted_params = {
        'k0': 2. * (c0 + c1 + c2 + c3 + c4),
        'k1': (-2. * c1 - (3. / 2.) * c3),
        'k2': (-c2 - c4),
        'k3': ((-1. / 2.) * c3),
        'k4': ((-1. / 4.) * c4)
    }

    name = opls_torsion_potential.name
    expression = opls_torsion_potential.expression
    variables = opls_torsion_potential.independent_variables

    opls_connection_type = gmso.DihedralType(name=name,
                                             expression=expression,
                                             independent_variables=variables,
                                             parameters=converted_params)

    return opls_connection_type
Example #5
0
def _validate_dihedraltype(contype):
    """Ensure connection_type is a DihedralType """
    if contype is None:
        warnings.warn("Non-parametrized Dihedral detected")
    elif not isinstance(contype, DihedralType):
        raise GMSOError("Supplied non-DihedralType {}".format(contype))
    return contype
Example #6
0
def _validate_four_partners(connection_members):
    """Ensure 4 partners are involved in Dihedral"""
    assert connection_members is not None, "connection_members is not given"
    if len(connection_members) != 4:
        raise GMSOError("Trying to create an Dihedral "
                "with {} connection members". format(len(connection_members)))
    return connection_members
Example #7
0
    def update_connection_types(self):
        """Update the connection types based on the connection collection in the topology.

        This method looks into all the connection objects (Bonds, Angles, Dihedrals) to
        check if any Potential object (BondType, AngleType, DihedralType) is not in the
        topology's respective collection and will add those objects there.

        See Also
        --------
        gmso.Topology.update_atom_types : Update atom types in the topology.
        """
        for c in self.connections:
            if c.connection_type is None:
                warnings.warn('Non-parametrized Connection {} detected'.format(c))
            elif not isinstance(c.connection_type, Potential):
                raise GMSOError('Non-Potential {} found'
                                    'in Connection {}'.format(c.connection_type, c))
            elif c.connection_type not in self._connection_types:
                c.connection_type.topology = self
                self._connection_types[c.connection_type] = c.connection_type
                if isinstance(c.connection_type, BondType):
                    self._bond_types[c.connection_type] = c.connection_type
                if isinstance(c.connection_type, AngleType):
                    self._angle_types[c.connection_type] = c.connection_type
                if isinstance(c.connection_type, DihedralType):
                    self._dihedral_types[c.connection_type] = c.connection_type
            elif c.connection_type in self.connection_types:
                if isinstance(c.connection_type, BondType):
                    c.connection_type = self._bond_types[c.connection_type]
                if isinstance(c.connection_type, AngleType):
                    c.connection_type = self._angle_types[c.connection_type]
                if isinstance(c.connection_type, DihedralType):
                    c.connection_type = self._dihedral_types[c.connection_type]
Example #8
0
def _validate_impropertype(contype):
    """Ensure connection_type is a ImproperType """
    if contype is None:
        warnings.warn("Non-parametrized Improper detected")
    elif not isinstance(contype, ImproperType):
        raise GMSOError("Supplied non-ImproperType {}".format(contype))
    return contype
Example #9
0
 def _reindex_connection_types(self, ref):
     if ref not in self._index_refs:
         raise GMSOError(f'cannot reindex {ref}. It should be one of '
                         f'{ANGLE_TYPE_DICT}, {BOND_TYPE_DICT}, '
                         f'{ANGLE_TYPE_DICT}, {DIHEDRAL_TYPE_DICT}, {IMPROPER_TYPE_DICT}')
     for i, ref_member in enumerate(self._set_refs[ref].keys()):
         self._index_refs[ref][ref_member] = i
Example #10
0
def _validate_two_partners(connection_members):
    """Ensure 2 partners are involved in Bond"""
    assert connection_members is not None, "connection_members is not given"
    if len(connection_members) != 2:
        raise GMSOError("Trying to create a Bond "
                "with {} connection members". format(len(connection_members)))
    return connection_members
Example #11
0
def _validate_connection_type(c_type):
    """Ensure given connection_type is the gmso.Potential"""
    if c_type is None:
        warnings.warn("Non-parametrized Connection detected")
    elif not isinstance(c_type, Potential):
        raise GMSOError("Supplied non-Potential {}".format(c_type))
    return c_type
Example #12
0
def _verify_potential_template_keys(_dict, name):
    assert 'name' in _dict, f"Key name not found in the potential template {name}.json"
    assert 'expression' in _dict, f"Key expression not found in the potential template {name}.json"
    assert 'independent_variables' in _dict, f"Key independent_variables not found in the potential template {name}.json"
    if str(name) != _dict['name']:
        raise GMSOError(
            f'Mismatch between Potential name {name} and {_dict["name"]}')
Example #13
0
    def update_atom_types(self):
        """Update atom types in the topology

        This method checks all the sites in the topology which have an
        associated AtomType and if that AtomType is not in the topology's
        AtomTypes collection, it will add it there.

        See Also:
        ---------
        gmso.Topology.update_connection_types :
            Update the connection types based on the connection collection in the topology
        """
        for site in self._sites:
            if site.atom_type is None:
                warnings.warn('Non-parametrized site detected {}'.format(site))
            elif not isinstance(site.atom_type, AtomType):
                raise GMSOError(
                    'Non AtomType instance found in site {}'.format(site))
            elif site.atom_type not in self._atom_types:
                site.atom_type.topology = self
                self._atom_types[site.atom_type] = site.atom_type
                self._atom_types_idx[site.atom_type] = len(
                    self._atom_types) - 1
            elif site.atom_type in self._atom_types:
                site.atom_type = self._atom_types[site.atom_type]
        self.is_typed(updated=True)
Example #14
0
def _validate_angletype(contype):
    """Ensure connection_type is a AngleType """
    if contype is None:
        warnings.warn("Non-parametrized Angle detected")
    elif not isinstance(contype, AngleType):
        raise GMSOError("Supplied non-AngleType {}".format(contype))
    return contype
    def from_template(cls, potential_template, parameters, topology=None):
        """Create a potential object from the potential_template

        Parameters
        ----------
        potential_template : gmso.lib.potential_templates.PotentialTemplate,
                            The potential template object
        parameters : dict,
                    The parameters of the potential object to create
        topology : gmso.Topology, default=None
                   The topology to which the created potential object belongs to

        Returns
        -------
        gmso.ParametricPotential
            The potential object created

        Raises
        ------
        GMSOError
            If potential_template is not of instance PotentialTemplate
        """
        from gmso.lib.potential_templates import PotentialTemplate
        if not isinstance(potential_template, PotentialTemplate):
            raise GMSOError(
                f'Object {type(potential_template)} is not an instance of PotentialTemplate.'
            )

        return cls(
            name=potential_template.name,
            expression=potential_template.expression,
            independent_variables=potential_template.independent_variables,
            parameters=parameters,
            topology=topology)
Example #16
0
def element_by_atomic_number(atomic_number):
    """Search for an element by its atomic number

    Look up an element from a list of known elements by atomic number.
    Return None if no match found.

    Parameters
    ----------
    atomic_number : int
        Element atomic number that need to look for
        if a string is provided, only numbers are considered during the search

    Returns
    -------
    matched_element : element.Element
        Return an element from the periodic table if we find a match,
        otherwise raise GMSOError
    """
    if isinstance(atomic_number, str):
        atomic_number_trimmed = int(
            sub('[a-z -]', '', atomic_number.lower()).lstrip('0'))
        msg = '''Letters and spaces are not considered when searching by element atomic number. \n
                 {} became {}'.format(atomic_number, atomic_number_trimmed)'''
        warnings.warn(msg)
    else:
        atomic_number_trimmed = atomic_number
    matched_element = atomic_dict.get(atomic_number_trimmed)
    if matched_element is None:
        raise GMSOError(
            f'Failed to find an element with atomic number {atomic_number_trimmed}'
        )
    return matched_element
Example #17
0
    def scaling_factors(self, scaling_factors):
        expected_items = [
            "vdw_12", "vdw_13", "vdw_14", "coul_12", "coul_13", "coul_14"
        ]
        if not isinstance(scaling_factors, dict):
            raise GMSOError("Scaling factors should be a dictionary")
        for item in expected_items:
            if item not in scaling_factors.keys():
                raise GMSOError(
                    f"Expected {expected_items} as keys in the scaling factors"
                )
        for val in scaling_factors.values():
            if val < 0.0 or val > 1.0:
                raise GMSOError(
                    "Scaling factors should be between 0.0 and 1.0")

        self._scaling_factors = scaling_factors
Example #18
0
def _check_compatibility(top):
    """Check Topology object for compatibility with Cassandra MCF format"""

    if not isinstance(top, Topology):
        raise GMSOError("MCF writer requires a Topology object.")
    if not all([site.atom_type.name for site in top.sites]):
        raise GMSOError(
            "MCF writing not supported without parameterized forcefield.")
    accepted_potentials = [
        potential_templates['LennardJonesPotential'],
        potential_templates['MiePotential'],
        potential_templates['HarmonicAnglePotential'],
        potential_templates['PeriodicTorsionPotential'],
        potential_templates['OPLSTorsionPotential'],
        potential_templates['RyckaertBellemansTorsionPotential'],
    ]
    check_compatibility(top, accepted_potentials)
Example #19
0
 def _reindex_connection_types(self, ref):
     """Re-generate the indices of the connection types in the topology."""
     if ref not in self._index_refs:
         raise GMSOError(
             f"cannot reindex {ref}. It should be one of "
             f"{ANGLE_TYPE_DICT}, {BOND_TYPE_DICT}, "
             f"{ANGLE_TYPE_DICT}, {DIHEDRAL_TYPE_DICT}, {IMPROPER_TYPE_DICT}"
         )
     for i, ref_member in enumerate(self._set_refs[ref].keys()):
         self._index_refs[ref][ref_member] = i
Example #20
0
def _validate_connection(site, connection):
    if not isinstance(site, Site):
        raise ValueError("Passed value {} is not a site".format(site))
    from gmso.core.connection import Connection
    if not isinstance(connection, Connection):
        raise ValueError("Passed value {} is not a Connection".format(connection))
    if site not in connection.connection_members:
        raise GMSOError("Error: Site not in connection members. Cannot add the connection.")
    if connection in site.connections:
        return None
    return connection
Example #21
0
def convert_opls_to_ryckaert(opls_connection_type):
    """Convert an OPLS dihedral to Ryckaert-Bellemans dihedral.

    Equations taken/modified from:
        http://manual.gromacs.org/documentation/2019/
        reference-manual/functions/bonded-interactions.html

    NOTE: the conventions defining the dihedral angle are different
    for OPLS and RB torsions. OPLS torsions are defined with
    phi_cis = 0 while RB torsions are defined as phi_trans = 0.
    """
    templates = PotentialTemplateLibrary()
    opls_torsion_potential = templates["OPLSTorsionPotential"]
    valid_connection_type = False
    if (opls_connection_type.independent_variables ==
            opls_torsion_potential.independent_variables):
        if (sympy.simplify(opls_connection_type.expression -
                           opls_torsion_potential.expression) == 0):
            valid_connection_type = True
    if not valid_connection_type:
        raise GMSOError("Cannot use convert_opls_to_ryckaert "
                        "function to convert a ConnectionType that is not an "
                        "OPLSTorsionPotential")

    f0 = opls_connection_type.parameters["k0"]
    f1 = opls_connection_type.parameters["k1"]
    f2 = opls_connection_type.parameters["k2"]
    f3 = opls_connection_type.parameters["k3"]
    f4 = opls_connection_type.parameters["k4"]

    converted_params = {
        "c0": (f2 + 0.5 * (f0 + f1 + f3)),
        "c1": (0.5 * (-f1 + 3.0 * f3)),
        "c2": (-f2 + 4.0 * f4),
        "c3": (-2.0 * f3),
        "c4": (-4.0 * f4),
        "c5": 0.0 * u.Unit("kJ/mol"),
    }
    ryckaert_bellemans_torsion_potential = templates[
        "RyckaertBellemansTorsionPotential"]
    name = ryckaert_bellemans_torsion_potential.name
    expression = ryckaert_bellemans_torsion_potential.expression
    variables = ryckaert_bellemans_torsion_potential.independent_variables

    ryckaert_connection_type = gmso.DihedralType(
        name=name,
        expression=expression,
        independent_variables=variables,
        parameters=converted_params,
    )

    return ryckaert_connection_type
Example #22
0
 def validate_fields(cls, values):
     connection_members = values.get('connection_members')
     if not all(isinstance(x, Site) for x in connection_members):
         raise TypeError(
             f'A non-site object provided to be a connection member')
     if len(set(connection_members)) != len(connection_members):
         raise GMSOError(
             f'Trying to create a {cls.__name__} between '
             f'same sites. A {cls.__name__} between same '
             f'{type(connection_members[0]).__name__}s is not allowed')
     if not values.get('name'):
         values['name'] = cls.__name__
     return values
Example #23
0
def convert_opls_to_ryckaert(opls_connection_type):
    """Convert an OPLS dihedral to Ryckaert-Bellemans dihedral

    Equations taken/modified from:
        http://manual.gromacs.org/documentation/2019/
        reference-manual/functions/bonded-interactions.html

    NOTE: the conventions defining the dihedral angle are different
    for OPLS and RB torsions. OPLS torsions are defined with
    phi_cis = 0 while RB torsions are defined as phi_trans = 0.
    """
    templates = PotentialTemplateLibrary()
    opls_torsion_potential = templates['OPLSTorsionPotential']
    valid_connection_type = False
    if (opls_connection_type.independent_variables ==
            opls_torsion_potential.independent_variables):
        if sympy.simplify(opls_connection_type.expression -
                          opls_torsion_potential.expression) == 0:
            valid_connection_type = True
    if not valid_connection_type:
        raise GMSOError('Cannot use convert_opls_to_ryckaert '
                        'function to convert a ConnectionType that is not an '
                        'OPLSTorsionPotential')

    f0 = opls_connection_type.parameters['k0']
    f1 = opls_connection_type.parameters['k1']
    f2 = opls_connection_type.parameters['k2']
    f3 = opls_connection_type.parameters['k3']
    f4 = opls_connection_type.parameters['k4']

    converted_params = {
        'c0': (f2 + 0.5 * (f0 + f1 + f3)),
        'c1': (0.5 * (-f1 + 3. * f3)),
        'c2': (-f2 + 4. * f4),
        'c3': (-2. * f3),
        'c4': (-4. * f4),
        'c5': 0. * u.Unit('kJ/mol')
    }
    ryckaert_bellemans_torsion_potential = templates[
        'RyckaertBellemansTorsionPotential']
    name = ryckaert_bellemans_torsion_potential.name
    expression = ryckaert_bellemans_torsion_potential.expression
    variables = ryckaert_bellemans_torsion_potential.independent_variables

    ryckaert_connection_type = gmso.DihedralType(
        name=name,
        expression=expression,
        independent_variables=variables,
        parameters=converted_params)

    return ryckaert_connection_type
Example #24
0
def element_by_smarts_string(smarts_string):
    """Search for an element by a given SMARTS string.

    Look up an element from a list of known elements by SMARTS string.
    Return None if no match found.

    Parameters
    ----------
    smarts_string : str
        SMARTS string representation of an atom type or its local chemical
        context. The Foyer SMARTS parser will be used to find the central atom
        and look up an Element. Note that this means some SMARTS grammar may
        not be parsed properly. For details, see
        https://github.com/mosdef-hub/foyer/issues/63

    Returns
    -------
    matched_element : element.Element
        Return an element from the periodic table if we find a match

    Raises
    ------
    GMSOError
        If no matching element is found for the provided smarts string
    """
    from gmso.utils.io import import_

    foyer = import_("foyer")
    SMARTS = foyer.smarts.SMARTS

    PARSER = SMARTS()

    symbols = PARSER.parse(smarts_string).iter_subtrees_topdown()

    first_symbol = None
    for symbol in symbols:
        if symbol.data == "atom_symbol":
            first_symbol = symbol.children[0]
            break

    matched_element = None
    if first_symbol is not None:
        matched_element = element_by_symbol(first_symbol)

    if matched_element is None:
        raise GMSOError(
            f"Failed to find an element from SMARTS string {smarts_string}. The "
            f"parser detected a central node with name {first_symbol}")

    return matched_element
Example #25
0
def _verify_potential_template_keys(_dict, name):
    """Verify the potential template is properly formatted."""
    assert (
        "name"
        in _dict), f"Key name not found in the potential template {name}.json"
    assert (
        "expression" in _dict
    ), f"Key expression not found in the potential template {name}.json"
    assert (
        "independent_variables" in _dict
    ), f"Key independent_variables not found in the potential template {name}.json"
    if str(name) != _dict["name"]:
        raise GMSOError(
            f'Mismatch between Potential name {name} and {_dict["name"]}')
Example #26
0
    def validate_fields(cls, values):
        connection_members = values.get("connection_members")

        if all(isinstance(member, dict) for member in connection_members):
            connection_members = [
                cls.__members_creator__(x) for x in connection_members
            ]

        if not all(isinstance(x, Site) for x in connection_members):
            raise TypeError(
                f"A non-site object provided to be a connection member")

        if len(set(connection_members)) != len(connection_members):
            raise GMSOError(
                f"Trying to create a {cls.__name__} between "
                f"same sites. A {cls.__name__} between same "
                f"{type(connection_members[0]).__name__}s is not allowed")

        if not values.get("name"):
            values["name"] = cls.__name__
        return values
Example #27
0
def element_by_smarts_string(smarts_string):
    """Search for an element by a given SMARTS string

    Look up an element from a list of known elements by SMARTS string.
    Return None if no match found.

    Parameters
    ----------
    smarts_string : str
        SMARTS string representation of an atom type or its local chemical
        context. The Foyer SMARTS parser will be used to find the central atom
        and look up an Element. Note that this means some SMARTS grammar may
        not be parsed properly. For details, see
        https://github.com/mosdef-hub/foyer/issues/63

    Returns
    -------
    matched_element : element.Element or None
        Return an element from the periodict table if we find a match,
        otherwise return None

    """
    from foyer.smarts import SMARTS

    PARSER = SMARTS()

    symbol = next(
        PARSER.parse(smarts_string).find_data('atom_symbol')).children[0]
    print(symbol)
    matched_element = element_by_symbol(symbol)

    if matched_element is None:
        raise GMSOError(
            f''
            'Failed to find an element from SMARTS string {smarts_string). The'
            'parser detected a central node with name {symbol}')

    return matched_element
Example #28
0
def element_by_atom_type(atom_type):
    """Search for an element by a given a gmso AtomType object

    Look up an element from a list of known elements by atom type.
    Return None if no match is found.

    Parameters
    ----------
    atom_type : gmso.core.atom_type.AtomType
        AtomType object to be parsed for element information. Attributes are
        looked up in the order of mass, name, and finally definition (the
        SMARTS string).  Because of the loose structure of this class, a
        successful lookup is not guaranteed.

    Returns
    -------
    matched_element : element.Element or None
        Return an element from the periodict table if we find a match,
        otherwise return None

    """
    matched_element = None

    if matched_element is None and atom_type.mass:
        matched_element = element_by_mass(atom_type.mass, exact=False)
    if matched_element is None and atom_type.name:
        matched_element = element_by_symbol(atom_type.name)
    if matched_element is None and atom_type.definition:
        matched_element = element_by_smarts_string(atom_type.definition)

    if matched_element is None:
        raise GMSOError(f'Failed to find an element from atom type'
                        '{atom_type} with '
                        'properties mass: {atom_type.mass}, name:'
                        '{atom_type.name}, and '
                        'definition: {atom_type.definition}')

    return matched_element
Example #29
0
    def is_valid_position(cls, position):
        """Validate attribute position."""
        if position is None:
            return u.unyt_array([np.nan] * 3, u.nm)

        if not isinstance(position, u.unyt_array):
            try:
                position *= u.nm
            except InvalidUnitOperation as e:
                raise GMSOError(
                    f"Converting object of type {type(position)} failed with following error: {e}"
                )
            warnings.warn("Positions are assumed to be in nm")

        try:
            position = np.reshape(position, newshape=(3, ), order="C")
            position.convert_to_units(u.nm)
        except ValueError:
            raise ValueError(
                f"Position of shape {position.shape} is not valid. "
                "Accepted values: (a.) list-like of length 3"
                "(b.) np.array or unyt.unyt_array of shape (3,)")

        return position
Example #30
0
 def combining_rule(self, rule):
     if rule not in ['lorentz', 'geometric']:
         raise GMSOError('Combining rule must be `lorentz` or `geometric`')
     self._combining_rule = rule