From efb8cb69dc0aba1dc992b26c2a57f936db75466e Mon Sep 17 00:00:00 2001 From: CalCraven Date: Thu, 20 Aug 2026 11:54:43 -0500 Subject: [PATCH 1/9] Fix handling on empty special_pair objects in hoomd-blue --- gmso/external/convert_hoomd.py | 21 +++++----- gmso/tests/test_hoomd.py | 76 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/gmso/external/convert_hoomd.py b/gmso/external/convert_hoomd.py index 53545c820..367e86397 100644 --- a/gmso/external/convert_hoomd.py +++ b/gmso/external/convert_hoomd.py @@ -1197,13 +1197,16 @@ def _parse_coulombic( # Use same method as to_hoomd_snapshot to generate pairs list pairs_dict = generate_pairs_lists(top) for i, pair_type in enumerate(pairs_dict): - if scaling_factors[i] and pairs_dict[pair_type]: + if not scaling_factors[i] in (0, 1) and pairs_dict[pair_type]: for pair in pairs_dict[pair_type]: pair_name = "-".join( sorted([pair[0].atom_type.name, pair[1].atom_type.name]) ) special_coulombic.params[pair_name] = dict(alpha=scaling_factors[i]) special_coulombic.r_cut[pair_name] = r_cut + # remove special_coulombic if necessary + if len(list(special_coulombic.params.keys())) == 0: + return [*coulombic] return [*coulombic, special_coulombic] @@ -1269,24 +1272,19 @@ def _parse_lj(top, atypes, combining_rule, r_cut, nlist, scaling_factors): } lj.r_cut[(site.molecule.name, site.molecule.name)] = r_cut - # Handle 1-2, 1-3, and 1-4 scaling - # TODO: Figure out a more general way to do this - # and handle molecule scaling factors + # TODO: handle per-molecule scaling factors if not np.any(scaling_factors): return [lj] special_lj = hoomd.md.special_pair.LJ() pairs_dict = generate_pairs_lists(top) for i, pair_type in enumerate(pairs_dict): - if scaling_factors[i] and pairs_dict[pair_type]: + # NOTE: special pairs cannot use tuple keys, must use a string + if not scaling_factors[i] in (0, 1) and pairs_dict[pair_type]: for pair in pairs_dict[pair_type]: if pair[0].atom_type in atypes and pair[1].atom_type in atypes: adjscale = scaling_factors[i] - pair.sort(key=lambda site: site.atom_type.name) - pair_name = ( - pair[0].atom_type.name, - pair[1].atom_type.name, - ) + pair_name = tuple(sorted([x.atom_type.name for x in pair])) scaled_epsilon = adjscale * calculated_params[pair_name]["epsilon"] sigma = calculated_params[pair_name]["sigma"] special_lj.params["-".join(pair_name)] = { @@ -1294,6 +1292,9 @@ def _parse_lj(top, atypes, combining_rule, r_cut, nlist, scaling_factors): "epsilon": scaled_epsilon, } special_lj.r_cut["-".join(pair_name)] = r_cut + # remove special_lj if necessary + if len(list(special_lj.params.keys())) == 0: + return [lj] return [lj, special_lj] diff --git a/gmso/tests/test_hoomd.py b/gmso/tests/test_hoomd.py index 0f0a98a36..989d7875d 100644 --- a/gmso/tests/test_hoomd.py +++ b/gmso/tests/test_hoomd.py @@ -372,6 +372,82 @@ def test_zero_charges(self): assert not isinstance(force, hoomd.md.long_range.pppm.Coulomb) assert not isinstance(force, hoomd.md.special_pair.Coulomb) + def test_skip_special_pairs(self): + compound = mb.load("CC", smiles=True) + com_box = mb.packing.fill_box(compound, box=[5, 5, 5], n_compounds=2) + top = from_mbuild(com_box) + top.identify_connections() + oplsaa = ForceField("oplsaa") + oplsaa.scaling_factors["nonBonded14Scale"] = 1 # no special pairs + oplsaa.scaling_factors["electrostatics14Scale"] = ( + 1 # no scaling of special pairs + ) + top = apply(top, oplsaa, remove_untyped=True) + + gmso_forces, _ = to_hoomd_forcefield( + top=top, + r_cut=1.4, + ) + for force in gmso_forces["nonbonded"]: + assert not isinstance(force, hoomd.md.special_pair.Coulomb) + assert not isinstance(force, hoomd.md.special_pair.LJ) + if isinstance( + force, + ( + hoomd.md.pair.LJ, + hoomd.md.long_range.pppm.Coulomb, + hoomd.md.pair.Ewald, + ), + ): + assert force.nlist.exclusions == [ + "bond", + "1-3", + ] # 1-4 are not excluded here. + + def test_special_pairs(self): + compound = mb.load("CC", smiles=True) + com_box = mb.packing.fill_box(compound, box=[5, 5, 5], n_compounds=2) + top = from_mbuild(com_box) + top.identify_connections() + oplsaa = ForceField("oplsaa") + # use 1-2, 1-3, 1-4 special pairs + for scale_factor in ["12", "13", "14"]: + oplsaa.scaling_factors[f"nonBonded{scale_factor}Scale"] = 0.5 + oplsaa.scaling_factors[f"electrostatics{scale_factor}Scale"] = 0.25 + top = apply(top, oplsaa, remove_untyped=True) + + gmso_forces, _ = to_hoomd_forcefield( + top=top, + r_cut=1.4, + ) + for force in gmso_forces["nonbonded"]: + if isinstance( + force, + hoomd.md.pair.LJ, + ): + lj_force = force + break + for force in gmso_forces["nonbonded"]: + if isinstance( + force, + ( + hoomd.md.pair.LJ, + hoomd.md.long_range.pppm.Coulomb, + hoomd.md.pair.Ewald, + ), + ): + assert force.nlist.exclusions == ["bond", "1-3", "1-4"] + elif isinstance(force, hoomd.md.special_pair.Coulomb): + for key in force.params.keys(): + assert force.params[key]["alpha"] == 0.25 + elif isinstance(force, hoomd.md.special_pair.LJ): + for key in force.params.keys(): + ljKey = tuple(key.split("-")) + assert ( + force.params[key]["epsilon"] + == lj_force.params[ljKey]["epsilon"] * 0.5 + ) + @pytest.mark.skipif(not has_hoomd, reason="hoomd is not installed") @pytest.mark.skipif(not has_mbuild, reason="mbuild not installed") @pytest.mark.skipif( From 768a761e4da23eb6b0935966ae234cf5b22d5468 Mon Sep 17 00:00:00 2001 From: CalCraven Date: Thu, 20 Aug 2026 12:14:06 -0500 Subject: [PATCH 2/9] Apply pre-commit suggestions --- gmso/external/convert_hoomd.py | 129 +++++++++++++++++---------------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/gmso/external/convert_hoomd.py b/gmso/external/convert_hoomd.py index 367e86397..6c07e4ef1 100644 --- a/gmso/external/convert_hoomd.py +++ b/gmso/external/convert_hoomd.py @@ -63,7 +63,7 @@ def get_cell_nlist(top, buffer=0.4): else: outVals = [] # store neighborlists for scalar in [nb_scaling_factors, coul_scaling_factors]: - exclusions = list() + exclusions = [] for i, val in enumerate(scalar): if val == 1: # skip values that are not exclusions continue @@ -382,7 +382,7 @@ def _parse_particle_information( The dictionary holding base units (mass, length, and energy) shift_coords : bool If True, shift coordinates from (0, L) to (-L/2, L/2) if neccessary. - box_lengths : list() of length 3 + box_lengths : [] of length 3 Lengths of box in x, y, z moleculeDict : dictionary of molecule.name : [[sites_molecule2],[sites_molecule1]...] Sorted info about all sites to index into topology @@ -484,7 +484,7 @@ def _parse_particle_information( "all of the rigid molecules must come first in the mBuild/GMSO hierarchy." ) - rigid_body_sets = dict() + rigid_body_sets = {} for site in top.sites: if site.molecule.isrigid: if site.molecule.name in rigid_body_sets: @@ -522,14 +522,14 @@ def _parse_particle_information( if rid not in group_indices_map: group_indices_map[rid] = [] group_indices_map[rid].append(i) - for rid in group_indices_map: - group_indices_map[rid] = np.array(group_indices_map[rid], dtype=np.intp) + for rid, item in group_indices_map.items: + group_indices_map[rid] = np.array(item, dtype=np.intp) rigid_constraint = hoomd.md.constrain.Rigid() mol_count = 0 - for rigid_mol in rigid_body_sets: - sorted_ids = sorted(rigid_body_sets[rigid_mol]) + for rigid_mol, value in rigid_body_sets.items(): + sorted_ids = sorted(value) for idx, _id in enumerate(sorted_ids): group_indices = group_indices_map[_id] group_positions = xyz[group_indices] @@ -554,7 +554,7 @@ def _parse_particle_information( "positions": group_positions, "orientations": group_orientations, } - mol_count += len(rigid_body_sets[rigid_mol]) + mol_count += len(value) # Prepend rigid body data unique_types = list(rigid_body_sets.keys()) + unique_types @@ -618,11 +618,11 @@ def _parse_pairs_information(snapshot, top, site_indexMap, n_rigid=0): Used to adjust pair group indices. """ - pair_types = list() - pair_typeids = list() - pairs = list() + pair_types = [] + pair_typeids = [] + pairs = [] - scaled_pairs = list() + scaled_pairs = [] pairs_dict = generate_pairs_lists(top, refer_from_scaling_factor=True) for pair_type in pairs_dict: scaled_pairs.extend(pairs_dict[pair_type]) @@ -630,10 +630,10 @@ def _parse_pairs_information(snapshot, top, site_indexMap, n_rigid=0): for pair in scaled_pairs: if pair[0].atom_type and pair[1].atom_type: pair.sort(key=lambda site: site.atom_type.name) - pair_type = "-".join([pair[0].atom_type.name, pair[1].atom_type.name]) + pair_type = f"{pair[0].atom_type.name}-{pair[1].atom_type.name}" else: pair.sort(key=lambda site: site.name) - pair_type = "-".join([pair[0].name, pair[1].name]) + pair_type = f"{pair[0].name}-{pair[1].name}" if pair_type not in pair_types: pair_types.append(pair_type) pair_typeids.append(pair_types.index(pair_type)) @@ -676,8 +676,8 @@ def _parse_bond_information(snapshot, top, site_indexMap, n_rigid=0): bond_types = [] for bond in top.bonds: - if all([site.atom_type for site in bond.connection_members]): - if not bond.connection_members[0].atom_type.atomclass == "": + if all(site.atom_type for site in bond.connection_members): + if bond.connection_members[0].atom_type.atomclass != "": connection_members = sort_connection_members(bond, "atomclass") bond_type = "-".join( [site.atom_type.atomclass for site in connection_members] @@ -734,7 +734,7 @@ def _parse_angle_information(snapshot, top, site_indexMap, n_rigid=0): angle_types = [] for angle in top.angles: - if all([site.atom_type for site in angle.connection_members]): + if all(site.atom_type for site in angle.connection_members): connection_members = sort_connection_members(angle, "atomclass") angle_type = "-".join( [site.atom_type.atomclass for site in connection_members] @@ -786,7 +786,7 @@ def _parse_dihedral_information(snapshot, top, site_indexMap, n_rigid=0): dihedral_types = [] for dihedral in top.dihedrals: - if all([site.atom_type for site in dihedral.connection_members]): + if all(site.atom_type for site in dihedral.connection_members): connection_members = sort_connection_members(dihedral, "atomclass") dihedral_type = "-".join( [site.atom_type.atomclass for site in connection_members] @@ -837,7 +837,7 @@ def _parse_improper_information(snapshot, top, site_indexMap, n_rigid=0): improper_types = [] for improper in top.impropers: - if all([site.atom_type for site in improper.connection_members]): + if all(site.atom_type for site in improper.connection_members): connection_members = sort_connection_members(improper, "atomclass") improper_type = "-".join( [site.atom_type.atomclass for site in connection_members] @@ -890,7 +890,7 @@ def to_hoomd_forcefield( top, r_cut, nlist=None, - pppm_kwargs={"resolution": (8, 8, 8), "order": 4}, + pppm_kwargs=None, base_units=None, auto_scale=False, kT=None, @@ -909,6 +909,7 @@ def to_hoomd_forcefield( If None, the default value used will be a hoomd.md.nlist.Cell(exclusions=exclusions, buffer=0.4). pppm_kwargs : dict Keyword arguments to pass to hoomd.md.long_range.make_pppm_coulomb_forces(). + Default is {"resolution": (8, 8, 8), "order": 4} base_units : dict or str, optional, default=None The dictionary of base units to be converted to. Entries restricted to "energy", "length", and "mass". There is also option to used predefined @@ -940,11 +941,13 @@ def to_hoomd_forcefield( raise EngineIncompatibilityError( "GMSO is only compatible with HOOMD-blue >= 4.0" ) + if pppm_kwargs is None: + pppm_kwargs = {"resolution": (8, 8, 8), "order": 4} potential_types = _validate_compatibility(top) base_units = _validate_base_units(base_units, top, auto_scale, potential_types) # Reference json dict of all the potential in the PotentialTemplate - potential_refs = dict() + potential_refs = {} for json_file in PotentialTemplateLibrary().json_refs: with open(json_file) as f: cont = json.load(f) @@ -1064,7 +1067,7 @@ def _parse_nonbonded_forces( ) # Grouping atomtype by group name - groups = dict() + groups = {} for atype in unique_atypes: if isinstance(atype, VirtualType): atype.virtual_potential.name = atype.name @@ -1083,10 +1086,10 @@ def _parse_nonbonded_forces( groups[group].append(atype) # Perform units conversion based on the provided base_units - for group in groups: + for group, value in groups.items(): expected_units_dim = potential_refs[group]["expected_parameters_dimensions"] groups[group] = convert_params_units( - groups[group], + value, expected_units_dim, base_units, ) @@ -1113,7 +1116,7 @@ def _parse_nonbonded_forces( "Incorrect values supplied for nlist. Should be of type hoomd.md.nlist" ) - nbonded_forces = list() + nbonded_forces = [] nbonded_forces.extend( _parse_coulombic( top=top, @@ -1124,11 +1127,11 @@ def _parse_nonbonded_forces( r_cut=r_cut, ) ) - for group in groups: + for group, value in groups.items(): nbonded_forces.extend( atype_parsers[group]( top=top, - atypes=groups[group], + atypes=value, combining_rule=top.combining_rule, r_cut=r_cut, nlist=nlist_nb, @@ -1147,7 +1150,7 @@ def _parse_nonbonded_forces( "HOOMDDPDForce": _parse_dpd, } # Grouping pairtype by group name - pair_categoryDict = dict() + pair_categoryDict = {} for pairtype in top.pairpotential_types: pair_category = potential_types[pairtype] if pair_category not in pair_categoryDict: @@ -1155,11 +1158,11 @@ def _parse_nonbonded_forces( else: pair_categoryDict[pair_category].append(pairtype) - for pair_category in pair_categoryDict: + for pair_category, value in pair_categoryDict.items(): nbonded_forces.extend( pairtype_parsers[pair_category]( top=top, - pairtypes=pair_categoryDict[pair_category], + pairtypes=value, r_cut=r_cut, nlist=nlist_nb, kT=kT, @@ -1178,9 +1181,7 @@ def _parse_coulombic( r_cut, ): """Parse coulombic forces.""" - charge_groups = any( - [site.charge.to_value(u.elementary_charge) for site in top.sites] - ) + charge_groups = any(site.charge.to_value(u.elementary_charge) for site in top.sites) if not charge_groups: logger.info("No charged group detected, skipping electrostatics.") return [] @@ -1202,7 +1203,7 @@ def _parse_coulombic( pair_name = "-".join( sorted([pair[0].atom_type.name, pair[1].atom_type.name]) ) - special_coulombic.params[pair_name] = dict(alpha=scaling_factors[i]) + special_coulombic.params[pair_name] = {"alpha": scaling_factors[i]} special_coulombic.r_cut[pair_name] = r_cut # remove special_coulombic if necessary if len(list(special_coulombic.params.keys())) == 0: @@ -1231,7 +1232,7 @@ def _parse_dpd(top, pairtypes, r_cut, nlist, kT): def _parse_lj(top, atypes, combining_rule, r_cut, nlist, scaling_factors): """Parse LJ forces and special pairs LJ forces.""" lj = hoomd.md.pair.LJ(nlist=nlist) - calculated_params = dict() + calculated_params = {} for pairs in itertools.combinations_with_replacement(atypes, 2): pairs = list(pairs) pairs.sort(key=lambda atype: atype.name) @@ -1364,7 +1365,7 @@ def _parse_bond_forces( The dictionary holding base units (mass, length, and energy) """ unique_btypes = top.bond_types(filter_by=PotentialFilters.UNIQUE_NAME_CLASS) - groups = dict() + groups = {} for btype in unique_btypes: group = potential_types[btype] if group not in groups: @@ -1372,10 +1373,10 @@ def _parse_bond_forces( else: groups[group].append(btype) - for group in groups: + for group, value in groups.items(): expected_units_dim = potential_refs[group]["expected_parameters_dimensions"] groups[group] = convert_params_units( - groups[group], + value, expected_units_dim, base_units, ) @@ -1390,12 +1391,12 @@ def _parse_bond_forces( "parser": _parse_fene_bond, }, } - bond_forces = list() - for group in groups: + bond_forces = [] + for group, value in groups.items(): bond_forces.append( btype_group_map[group]["parser"]( container=btype_group_map[group]["container"](), - btypes=groups[group], + btypes=value, ) ) return bond_forces @@ -1453,7 +1454,7 @@ def _parse_angle_forces( The dictionary holding base units (mass, length, and energy) """ unique_agtypes = top.angle_types(filter_by=PotentialFilters.UNIQUE_NAME_CLASS) - groups = dict() + groups = {} for agtype in unique_agtypes: group = potential_types[agtype] if group not in groups: @@ -1461,10 +1462,10 @@ def _parse_angle_forces( else: groups[group].append(agtype) - for group in groups: + for group, value in groups.items(): expected_units_dim = potential_refs[group]["expected_parameters_dimensions"] groups[group] = convert_params_units( - groups[group], + value, expected_units_dim, base_units, ) @@ -1475,12 +1476,12 @@ def _parse_angle_forces( "parser": _parse_harmonic_angle, }, } - angle_forces = list() - for group in groups: + angle_forces = [] + for group, value in groups.items(): angle_forces.append( agtype_group_map[group]["parser"]( container=agtype_group_map[group]["container"](), - agtypes=groups[group], + agtypes=value, ) ) return angle_forces @@ -1527,7 +1528,7 @@ def _parse_dihedral_forces( [site.atom_type.atomclass for site in dihedral.connection_members] ) unique_dihedrals[unique_members] = dihedral - groups = dict() + groups = {} for dihedral in unique_dihedrals.values(): group = potential_types[dihedral.dihedral_type] if group not in groups: @@ -1567,14 +1568,14 @@ def _parse_dihedral_forces( "parser": _parse_hoomd_periodic_dihedral, } - dihedral_forces = list() - for group in groups: + dihedral_forces = [] + for group, value in groups.items(): container = dtype_group_map[group]["container"] if isinstance(container(), hoomd.md.dihedral.OPLS): dihedral_forces.append( dtype_group_map[group]["parser"]( container=container(), - dihedrals=groups[group], + dihedrals=value, expected_units_dim=expected_unitsDict[group], base_units=base_units, ) @@ -1583,7 +1584,7 @@ def _parse_dihedral_forces( dihedral_forces.extend( dtype_group_map[group]["parser"]( container=dtype_group_map[group]["container"](), - dihedrals=groups[group], + dihedrals=value, expected_units_dim=expected_unitsDict[group], base_units=base_units, ) @@ -1621,7 +1622,7 @@ def _parse_periodic_dihedral(container, dihedrals, expected_units_dim, base_unit if len(tuple(containersList[i].params.keys())) == 0: continue # add in extra parameters - for key in containersList[0].params.keys(): + for key in containersList[0].params: if key not in tuple(containersList[i].params.keys()): containersList[i].params[key] = { "k": 0, @@ -1665,7 +1666,7 @@ def _parse_hoomd_periodic_dihedral( if len(tuple(containersList[i].params.keys())) == 0: continue # add in extra parameters - for key in containersList[0].params.keys(): + for key in containersList[0].params: if key not in tuple(containersList[i].params.keys()): containersList[i].params[key] = { "k": 0, @@ -1735,7 +1736,7 @@ def _parse_improper_forces( The dictionary holding base units (mass, length, and energy) """ unique_dtypes = top.improper_types(filter_by=PotentialFilters.UNIQUE_NAME_CLASS) - groups = dict() + groups = {} for itype in unique_dtypes: group = potential_types[itype] if group not in groups: @@ -1743,10 +1744,10 @@ def _parse_improper_forces( else: groups[group].append(itype) - for group in groups: + for group, item in groups.items(): expected_units_dim = potential_refs[group]["expected_parameters_dimensions"] groups[group] = convert_params_units( - groups[group], + item, expected_units_dim, base_units, ) @@ -1774,12 +1775,12 @@ def _parse_improper_forces( }, } - improper_forces = list() - for group in groups: + improper_forces = [] + for group, value in groups.items(): improper_forces.append( itype_group_map[group]["parser"]( container=itype_group_map[group]["container"](), - itypes=groups[group], + itypes=value, ) ) return improper_forces @@ -1871,7 +1872,7 @@ def _validate_base_units(base_units, top, auto_scale, potential_types=None): if unique_atypes: if not potential_types: potential_types = _validate_compatibility(top) - atype_classes = dict() + atype_classes = {} # Separate atypes by their classes for atype in unique_atypes: if potential_types[atype] not in atype_classes: @@ -1880,7 +1881,7 @@ def _validate_base_units(base_units, top, auto_scale, potential_types=None): atype_classes[potential_types[atype]].append(atype) # Appending lengths and energy - lengths, energies = list(), list() + lengths, energies = [], [] for atype_class in atype_classes: if atype_class == "LennardJonesPotential": for atype in unique_atypes: @@ -1916,7 +1917,7 @@ def _validate_base_units(base_units, top, auto_scale, potential_types=None): f"Base unit of {key} must be of type u.Unit or u.unyt_quantity." ) - missing = list() + missing = [] for base in ["energy", "mass", "length"]: if base not in base_units: missing.append(base) @@ -1960,7 +1961,7 @@ def _convert_single_param_units( base_units, ): """Convert parameters' units in the potential to that specified in the base_units.""" - converted_params = dict() + converted_params = {} for parameter in potential.parameters: unit_dim = expected_units_dim[parameter] ind_units = re.sub("[^a-zA-Z]+", " ", unit_dim).split() From 60c878b60612739e81e8321b611703b0e1cbd5a7 Mon Sep 17 00:00:00 2001 From: CalCraven Date: Thu, 20 Aug 2026 12:44:52 -0500 Subject: [PATCH 3/9] Fix bug on items --- gmso/external/convert_hoomd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gmso/external/convert_hoomd.py b/gmso/external/convert_hoomd.py index 6c07e4ef1..ed47f5023 100644 --- a/gmso/external/convert_hoomd.py +++ b/gmso/external/convert_hoomd.py @@ -522,7 +522,7 @@ def _parse_particle_information( if rid not in group_indices_map: group_indices_map[rid] = [] group_indices_map[rid].append(i) - for rid, item in group_indices_map.items: + for rid, item in group_indices_map.items(): group_indices_map[rid] = np.array(item, dtype=np.intp) rigid_constraint = hoomd.md.constrain.Rigid() From 22fc7dd758ad6268fe1ae4023cf052e9c9712e5b Mon Sep 17 00:00:00 2001 From: CalCraven Date: Thu, 3 Sep 2026 11:31:16 -0500 Subject: [PATCH 4/9] Fix ruff/flake linting suggestions --- gmso/abc/abstract_potential.py | 6 +- gmso/abc/abstract_site.py | 8 +-- gmso/abc/serialization_utils.py | 2 +- gmso/core/atom_type.py | 2 +- gmso/core/box.py | 15 ++--- gmso/core/forcefield.py | 10 +-- gmso/core/topology.py | 113 ++++++++++++++++---------------- gmso/utils/expression.py | 28 ++++---- 8 files changed, 91 insertions(+), 93 deletions(-) diff --git a/gmso/abc/abstract_potential.py b/gmso/abc/abstract_potential.py index 1d2ea2a59..8689188be 100644 --- a/gmso/abc/abstract_potential.py +++ b/gmso/abc/abstract_potential.py @@ -118,9 +118,7 @@ def tag_names_iter(self) -> Iterator[str]: @field_serializer("potential_expression_") def serialize_expression(self, potential_expression_: PotentialExpression): expr = str(potential_expression_.expression) - ind = sorted( - list(str(ind) for ind in potential_expression_.independent_variables) - ) + ind = sorted(str(ind) for ind in potential_expression_.independent_variables) params = { param: unyt_to_dict(val) for param, val in potential_expression_.parameters.items() @@ -133,7 +131,7 @@ def serialize_expression(self, potential_expression_: PotentialExpression): @field_serializer("tags_") def serialize_tags(self, tags_): - return_dict = dict() + return_dict = {} for key, val in tags_.items(): if isinstance(val, u.unyt_array): return_dict[key] = unyt_to_dict(val) diff --git a/gmso/abc/abstract_site.py b/gmso/abc/abstract_site.py index b4d8b1612..06ae6931d 100644 --- a/gmso/abc/abstract_site.py +++ b/gmso/abc/abstract_site.py @@ -2,7 +2,7 @@ import logging from collections.abc import Sequence -from typing import Any, ClassVar, TypeVar, Union +from typing import Any, ClassVar, TypeVar import numpy as np import unyt as u @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) -PositionType = Union[Sequence[float], np.ndarray, u.unyt_array] +PositionType = Sequence[float] | np.ndarray | u.unyt_array class Molecule(GMSOBase): @@ -83,7 +83,7 @@ def __eq__(self, other): """Test if two objects are equivalent.""" if isinstance(other, (list, tuple)): return all( - [val1 == val2 for val1, val2 in zip(self.__dict__.values(), other)] + val1 == val2 for val1, val2 in zip(self.__dict__.values(), other) ) else: return self.__dict__ == other.__dict__ @@ -135,7 +135,7 @@ def __eq__(self, other): """Test if two objects are equivalent.""" if isinstance(other, (list, tuple)): return all( - [val1 == val2 for val1, val2 in zip(self.__dict__.values(), other)] + val1 == val2 for val1, val2 in zip(self.__dict__.values(), other) ) else: return self.__dict__ == other.__dict__ diff --git a/gmso/abc/serialization_utils.py b/gmso/abc/serialization_utils.py index 4424af3c6..33bb598c8 100644 --- a/gmso/abc/serialization_utils.py +++ b/gmso/abc/serialization_utils.py @@ -27,7 +27,7 @@ def dict_to_unyt(dict_obj) -> None: dict_to_unyt(value) else: np_array = np.array(value["array"], dtype=float) - if np_array.shape == tuple(): + if np_array.shape == (): unyt_func = u.unyt_quantity else: unyt_func = u.unyt_array diff --git a/gmso/core/atom_type.py b/gmso/core/atom_type.py index 9ffd0a20e..e1e3f4a14 100644 --- a/gmso/core/atom_type.py +++ b/gmso/core/atom_type.py @@ -175,7 +175,7 @@ def clone(self, fast_copy=False): charge=u.unyt_quantity(self.charge.value, self.charge.units), atomclass=self.atomclass, doi=self.doi, - overrides=(set(o for o in self.overrides) if self.overrides else None), + overrides=(set(self.overrides) if self.overrides else None), description=self.description, definition=self.definition, ) diff --git a/gmso/core/box.py b/gmso/core/box.py index 732008514..dca9fe74c 100644 --- a/gmso/core/box.py +++ b/gmso/core/box.py @@ -202,13 +202,8 @@ def __eq__(self, other): if self is other: return True - if not isinstance(other, Box): - return False - - if not allclose_units(self.lengths, other.lengths, rtol=1e-5, atol=1e-8): - return False - - if not allclose_units(self.angles, other.angles, rtol=1e-5, atol=1e-8): - return False - - return True + return ( + isinstance(other, Box) + and allclose_units(self.lengths, other.lengths, rtol=1e-5, atol=1e-8) + and allclose_units(self.angles, other.angles, rtol=1e-5, atol=1e-8) + ) diff --git a/gmso/core/forcefield.py b/gmso/core/forcefield.py index d9d7fe1eb..7d2cb8522 100644 --- a/gmso/core/forcefield.py +++ b/gmso/core/forcefield.py @@ -173,10 +173,10 @@ def non_element_types(self): """Get the non-element types in the ForceField.""" non_element_types = set() - for name, atom_type in self.atom_types.items(): + for atom_type in self.atom_types.values(): element_symbol = atom_type.get_tag( "element" - ) # FixMe: Should we make this a first class citizen? + ) # TODO: Should we make this a first class citizen? if element_symbol: element = element_by_symbol(element_symbol) non_element_types.add(element_symbol) if not element else None @@ -842,8 +842,8 @@ def from_xml(cls, xmls_or_etrees, strict=True, greedy=True): should_parse_xml = False if not ( - all(map(lambda x: isinstance(x, str), xmls_or_etrees)) - or all(map(lambda x: isinstance(x, etree._ElementTree), xmls_or_etrees)) + all(isinstance(x, str) for x in xmls_or_etrees) + or all(isinstance(x, etree._ElementTree) for x in xmls_or_etrees) ): raise TypeError( "Please provide an iterable of strings " @@ -851,7 +851,7 @@ def from_xml(cls, xmls_or_etrees, strict=True, greedy=True): "or equivalent element Trees" ) - if all(map(lambda x: isinstance(x, str), xmls_or_etrees)): + if all(isinstance(x, str) for x in xmls_or_etrees): should_parse_xml = True versions = [] diff --git a/gmso/core/topology.py b/gmso/core/topology.py index 5d757e3b7..51a68127a 100644 --- a/gmso/core/topology.py +++ b/gmso/core/topology.py @@ -339,7 +339,6 @@ def unique_site_labels(self, label_type="molecule", name_only=False): unique_tags.add(copy(getattr(site, label_type))) return unique_tags - @property def atom_types(self, include_virtual_types=False): """Return all atom_types in the topology. @@ -622,36 +621,36 @@ def pairpotential_types(self): @property def atom_type_expressions(self): """Return all atom_type expressions in the topology.""" - return list(set([atype.expression for atype in self.atom_types])) + return list({atype.expression for atype in self.atom_types}) @property def connection_type_expressions(self): """Return all connection_type expressions in the topology.""" - return list(set([contype.expression for contype in self.connection_types])) + return list({contype.expression for contype in self.connection_types}) @property def bond_type_expressions(self): """Return all bond_type expressions in the topology.""" - return list(set([btype.expression for btype in self.bond_types])) + return list({btype.expression for btype in self.bond_types}) @property def angle_type_expressions(self): """Return all angle_type expressions in the topology.""" - return list(set([atype.expression for atype in self.angle_types])) + return list({atype.expression for atype in self.angle_types}) @property def dihedral_type_expressions(self): """Return all dihedral_type expressions in the topology.""" - return list(set([dtype.expression for dtype in self.dihedral_types])) + return list({dtype.expression for dtype in self.dihedral_types}) @property def improper_type_expressions(self): """Return all improper_type expressions in the topology.""" - return list(set([itype.expression for itype in self.improper_types])) + return list({itype.expression for itype in self.improper_types}) @property def pairpotential_type_expressions(self): - return list(set([ptype.expression for ptype in self._pairpotential_types])) + return list({ptype.expression for ptype in self._pairpotential_types}) def get_lj_scale(self, *, molecule_id=None, interaction=None): """Return the selected lj_scales defined for this topology.""" @@ -969,11 +968,12 @@ def add_pairpotentialtype(self, pairpotentialtype, update=True): if not isinstance(pairpotentialtype, PairPotentialType): raise GMSOError(f"Non-PairPotentialType {pairpotentialtype} provided") for atype in pairpotentialtype.member_types: - if atype not in {t.name for t in self.atom_types}: - if atype not in {t.atomclass for t in self.atom_types}: - raise GMSOError( - f"There is no name/atomclass of AtomType {atype} in current topology" - ) + if atype not in {t.name for t in self.atom_types} and atype not in { + t.atomclass for t in self.atom_types + }: + raise GMSOError( + f"There is no name/atomclass of AtomType {atype} in current topology" + ) self._pairpotential_types.add(pairpotentialtype) def remove_pairpotentialtype(self, pair_of_types): @@ -1047,9 +1047,9 @@ def is_fully_typed(self, group="topology", updated=False): } if group == "topology": - result = list() - for subgroup in typed_status: - result.append(typed_status[subgroup](self)) + result = [] + for extractor in typed_status.values(): + result.append(extractor(self)) return all(result) elif group in typed_status: return typed_status[group](self) @@ -1075,7 +1075,7 @@ def get_untyped(self, group): Dictionary of all untyped object, key of the dictionary corresponds to object group names define above. """ - untyped = dict() + untyped = {} untyped_extractors = { "sites": self._get_untyped_sites, "bonds": self._get_untyped_bonds, @@ -1084,8 +1084,8 @@ def get_untyped(self, group): "impropers": self._get_untyped_impropers, } if group == "topology": - for subgroup in untyped_extractors: - untyped.update(untyped_extractors[subgroup]()) + for extractor in untyped_extractors.values(): + untyped.update(extractor()) elif isinstance(group, (list, tuple, set)): for subgroup in group: untyped.update(untyped_extractors[subgroup]()) @@ -1104,7 +1104,7 @@ def _add_virtual_site(self, site): def _get_untyped_sites(self): "Return a list of untyped sites" - untyped = {"sites": list()} + untyped = {"sites": []} for site in self._sites: if not site.atom_type: untyped["sites"].append(site) @@ -1112,7 +1112,7 @@ def _get_untyped_sites(self): def _get_untyped_bonds(self): "Return a list of untyped bonds" - untyped = {"bonds": list()} + untyped = {"bonds": []} for bond in self._bonds: if not bond.bond_type: untyped["bonds"].append(bond) @@ -1120,7 +1120,7 @@ def _get_untyped_bonds(self): def _get_untyped_angles(self): "Return a list of untyped angles" - untyped = {"angles": list()} + untyped = {"angles": []} for angle in self._angles: if not angle.angle_type: untyped["angles"].append(angle) @@ -1128,7 +1128,7 @@ def _get_untyped_angles(self): def _get_untyped_dihedrals(self): "Return a list of untyped dihedrals" - untyped = {"dihedrals": list()} + untyped = {"dihedrals": []} for dihedral in self._dihedrals: if not dihedral.dihedral_type: untyped["dihedrals"].append(dihedral) @@ -1136,7 +1136,7 @@ def _get_untyped_dihedrals(self): def _get_untyped_impropers(self): "Return a list of untyped impropers" - untyped = {"impropers": list()} + untyped = {"impropers": []} for improper in self._impropers: if not improper.improper_type: untyped["impropers"].append(improper) @@ -1335,8 +1335,8 @@ def to_dataframe(self, parameter="sites", site_attrs=None, unyts_bool=True): "This topology is not typed, please type this object before converting to a pandas dataframe" ) if parameter == "sites": - df["atom_types"] = list(site.atom_type.name for site in self.sites) - df["names"] = list(site.name for site in self.sites) + df["atom_types"] = [site.atom_type.name for site in self.sites] + df["names"] = [site.name for site in self.sites] for attr in site_attrs: df = self._parse_dataframe_attrs(df, attr, parameter, unyts_bool) elif parameter in ["bonds", "angles", "dihedrals", "impropers"]: @@ -1567,7 +1567,7 @@ def iter_connections_by_site(self, site, connections=None): if connections is None: connections = ["bonds", "angles", "dihedrals", "impropers"] else: - connections = set([option.lower() for option in connections]) + connections = {option.lower() for option in connections} for option in connections: if option not in ["bonds", "angles", "dihedrals", "impropers"]: raise ValueError( @@ -1600,7 +1600,7 @@ def create_subtop(self, label_type, label): molecule_impropers, ) - of_group = True if label_type == "group" else False + of_group = label_type == "group" sites_dict = { site: (idx, site.clone()) for idx, site in enumerate(self.iter_sites(label_type, label)) @@ -1631,7 +1631,7 @@ def create_subtop(self, label_type, label): new_top = gmso.Topology(name=label if isinstance(label, str) else label[0]) - for ref_site, new_site in sites_dict.items(): + for new_site in sites_dict.values(): new_top.add_site(new_site[1]) for ref_conn, conn_idx in bonds_dict.items(): bond = gmso.Bond( @@ -1729,11 +1729,11 @@ def _pandas_from_parameters(self, df, parameter, site_attrs=None, unyts_bool=Tru site_attrs = [] sites_per_connection = len(getattr(self, parameter)[0].connection_members) for site_index in np.arange(sites_per_connection): - df["Atom" + str(site_index)] = list( + df["Atom" + str(site_index)] = [ str(connection.connection_members[site_index].name) + f"({self.get_index(connection.connection_members[site_index])})" for connection in getattr(self, parameter) - ) + ] for attr in site_attrs: df = self._parse_dataframe_attrs( df, attr, parameter, sites_per_connection, unyts_bool @@ -1749,36 +1749,36 @@ def _parse_dataframe_attrs( if "." in attr: try: attr1, attr2 = attr.split(".") - df[attr] = list( + df[attr] = [ _return_float_for_unyt( getattr(getattr(site, attr1), attr2), unyts_bool, ) for site in self.sites - ) + ] except AttributeError: raise AttributeError( f"The attribute {attr} is not in this gmso object." ) elif attr == "positions" or attr == "position": for i, dimension in enumerate(["x", "y", "z"]): - df[dimension] = list( + df[dimension] = [ _return_float_for_unyt(site.position[i], unyts_bool) for site in self.sites - ) + ] elif attr == "charge" or attr == "charges": - df["charge (e)"] = list( + df["charge (e)"] = [ site.charge.in_units( u.Unit("elementary_charge", registry=UnitReg.default_reg()) ).to_value() for site in self.sites - ) + ] else: try: - df[attr] = list( + df[attr] = [ _return_float_for_unyt(getattr(site, attr), unyts_bool) for site in self.sites - ) + ] except AttributeError: raise AttributeError( f"The attribute {attr} is not in this gmso object." @@ -1789,7 +1789,7 @@ def _parse_dataframe_attrs( if "." in attr: try: attr1, attr2 = attr.split(".") - df[attr + " Atom" + str(site_index)] = list( + df[attr + " Atom" + str(site_index)] = [ _return_float_for_unyt( getattr( getattr( @@ -1801,35 +1801,35 @@ def _parse_dataframe_attrs( unyts_bool, ) for connection in getattr(self, parameter) - ) + ] except AttributeError: raise AttributeError( f"The attribute {attr} is not in this gmso object." ) elif attr == "positions" or attr == "position": - df["x Atom" + str(site_index) + " (nm)"] = list( + df["x Atom" + str(site_index) + " (nm)"] = [ _return_float_for_unyt( connection.connection_members[site_index].position[0], unyts_bool, ) for connection in getattr(self, parameter) - ) - df["y Atom" + str(site_index) + " (nm)"] = list( + ] + df["y Atom" + str(site_index) + " (nm)"] = [ _return_float_for_unyt( connection.connection_members[site_index].position[1], unyts_bool, ) for connection in getattr(self, parameter) - ) - df["z Atom" + str(site_index) + " (nm)"] = list( + ] + df["z Atom" + str(site_index) + " (nm)"] = [ _return_float_for_unyt( connection.connection_members[site_index].position[2], unyts_bool, ) for connection in getattr(self, parameter) - ) + ] elif attr == "charge" or attr == "charges": - df["charge Atom" + str(site_index) + " (e)"] = list( + df["charge Atom" + str(site_index) + " (e)"] = [ connection.connection_members[site_index] .charge.in_units( u.Unit( @@ -1839,10 +1839,10 @@ def _parse_dataframe_attrs( ) .value for connection in getattr(self, parameter) - ) + ] else: try: - df[f"{attr} Atom {site_index}"] = list( + df[f"{attr} Atom {site_index}"] = [ _return_float_for_unyt( getattr( connection.connection_members[site_index], @@ -1851,7 +1851,7 @@ def _parse_dataframe_attrs( unyts_bool, ) for connection in getattr(self, parameter) - ) + ] except AttributeError: raise AttributeError( f"The attribute {attr} is not in this gmso object." @@ -1870,13 +1870,13 @@ def _parse_parameter_expression(self, df, parameter, unyts_bool): ): df[ f"Parameter {i} ({param}) {getattr(getattr(self, parameter)[0], parameter[:-1] + '_type').parameters[param].units}" - ] = list( + ] = [ _return_float_for_unyt( getattr(connection, parameter[:-1] + "_type").parameters[param], unyts_bool, ) for connection in getattr(self, parameter) - ) + ] return df @classmethod @@ -1888,12 +1888,12 @@ def load(cls, filename, **kwargs): loader = LoadersRegistry.get_callable(filename.suffix) return loader(filename, **kwargs) - def convert_potential_styles(self, expressionMap={}): + def convert_potential_styles(self, expressionMap=None): """Convert from one parameter form to another. Parameters ---------- - expressionMap : dict, default={} + expressionMap : dict, default=None Map where the keys represent the current potential type and the corresponding values represent the desired potential type. The desired potential style can be @@ -1908,6 +1908,9 @@ def convert_potential_styles(self, expressionMap={}): """ # TODO: raise warnings for improper values or keys in expressionMap + if expressionMap is None: + expressionMap = {} + return convert_topology_expressions(self, expressionMap) def convert_unit_styles(self, unitsystem, exp_unitsDict): diff --git a/gmso/utils/expression.py b/gmso/utils/expression.py index d61283c70..15d8b81c1 100644 --- a/gmso/utils/expression.py +++ b/gmso/utils/expression.py @@ -272,17 +272,17 @@ def _validate_expression(expression): def _validate_parameters(parameters): """Check to see that parameters is a valid dictionary with units.""" if not isinstance(parameters, dict): - raise ValueError("Please enter a dictionary for parameters") + raise TypeError("Please enter a dictionary for parameters") for key, val in parameters.items(): if isinstance(val, list): for params in val: if not isinstance(params, u.unyt_array): - raise ValueError(f"Parameter value {val} lacks a unyt") + raise TypeError(f"Parameter value {val} lacks a unyt") else: if not isinstance(val, u.unyt_array): - raise ValueError(f"Parameter value {val} lacks a unyt") + raise TypeError(f"Parameter value {val} lacks a unyt") if not isinstance(key, str): - raise ValueError(f"Parameter key {key} is not a str") + raise TypeError(f"Parameter key {key} is not a str") return parameters @@ -312,19 +312,19 @@ def _validate_independent_variables(indep_vars): elif isinstance(indep_vars, sympy.Symbol): indep_vars = {indep_vars} elif isinstance(indep_vars, (list, set)): - if all([isinstance(val, sympy.Symbol) for val in indep_vars]): + if all(isinstance(val, sympy.Symbol) for val in indep_vars): pass - elif all([isinstance(val, str) for val in indep_vars]): - indep_vars = set([sympy.symbols(val) for val in indep_vars]) + elif all(isinstance(val, str) for val in indep_vars): + indep_vars = {sympy.symbols(val) for val in indep_vars} else: - raise ValueError( + raise TypeError( "`independent_variables` argument was a list " "or set of mixed variables. Please enter a " "list or set of either only strings or only " "sympy symbols" ) else: - raise ValueError( + raise TypeError( "Please enter a string, sympy expression, " "list or set thereof for independent_variables" ) @@ -380,9 +380,9 @@ def json(potential_expression): else: json_dict = { "expression": str(potential_expression.expression), - "independent_variables": list( + "independent_variables": [ str(idep) for idep in potential_expression.independent_variables - ), + ], } if potential_expression.is_parametric: json_dict["parameters"] = potential_expression.parameters @@ -477,7 +477,9 @@ def from_non_parametric( ) def evaluate( - self, independent_namespace: dict = None, independent_parameters: dict = None + self, + independent_namespace: dict | None = None, + independent_parameters: dict | None = None, ): """Evaluate the sympy expression with the given parameters @@ -623,7 +625,7 @@ class norm(Function) in the module for the definition and evaluation procedure f class NullPotentialExpression(PotentialExpression): """A null/empty PotentialExpression for AtomTypes without intrinsic expressions.""" - def __init__(self): # noqa: super-init-not-called + def __init__(self): # Intentionally not calling super().__init__() — this object represents # the absence of a potential expression and overrides all properties. self._expression = None From e9306f6dc81b058ee058fe5940c3cc98ec4fc10f Mon Sep 17 00:00:00 2001 From: CalCraven Date: Thu, 3 Sep 2026 18:56:04 -0500 Subject: [PATCH 5/9] Fixes to many pre-commit ruff linting suggestions --- gmso/core/topology.py | 11 +- gmso/core/views.py | 6 +- gmso/external/convert_mbuild.py | 20 ++-- gmso/external/convert_parmed.py | 52 ++++---- gmso/formats/gro.py | 6 +- gmso/formats/lammpsdata.py | 44 ++++--- gmso/formats/mcf.py | 27 +++-- gmso/formats/mol2.py | 10 +- gmso/formats/networkx.py | 6 +- gmso/formats/top.py | 112 +++++++++--------- gmso/parameterization/foyer_utils.py | 2 +- gmso/parameterization/parameterize.py | 24 ++-- .../topology_parameterizer.py | 30 ++--- gmso/tests/base_test.py | 13 +- .../parameterization_base_test.py | 14 +-- .../test_impropers_parameterization.py | 4 +- .../parameterization/test_molecule_utils.py | 24 ++-- gmso/tests/test_atom_type.py | 6 +- gmso/tests/test_bond.py | 10 +- gmso/tests/test_conversions.py | 16 +-- gmso/tests/test_convert_mbuild.py | 2 +- gmso/tests/test_convert_parmed.py | 44 ++++--- gmso/tests/test_equation_compare.py | 36 +++--- gmso/tests/test_expression.py | 4 +- gmso/tests/test_forcefield.py | 4 +- gmso/tests/test_gro.py | 12 +- gmso/tests/test_gsd.py | 8 +- gmso/tests/test_hoomd.py | 50 ++++---- gmso/tests/test_itp.py | 4 +- gmso/tests/test_lammps.py | 51 ++++---- gmso/tests/test_mcf.py | 45 +++---- gmso/tests/test_mol2.py | 6 +- gmso/tests/test_networkx.py | 44 +++---- gmso/tests/test_potential.py | 6 +- gmso/tests/test_reference_xmls.py | 14 +-- gmso/tests/test_specific_ff_to_residue.py | 40 +++---- gmso/tests/test_top.py | 16 +-- gmso/tests/test_topology.py | 10 +- gmso/tests/test_units.py | 34 +++--- gmso/tests/test_xml_handling.py | 2 +- gmso/tests/test_xyz.py | 8 +- gmso/utils/compatibility.py | 2 +- gmso/utils/connectivity.py | 10 +- gmso/utils/conversions.py | 46 +++---- gmso/utils/equation_compare.py | 62 +++++----- gmso/utils/ff_utils.py | 50 ++++---- gmso/utils/geometry.py | 4 +- gmso/utils/io.py | 16 +-- gmso/utils/nx_utils.py | 28 +++-- gmso/utils/sorting.py | 8 +- gmso/utils/specific_ff_to_residue.py | 28 ++--- gmso/utils/units.py | 14 +-- 52 files changed, 547 insertions(+), 598 deletions(-) diff --git a/gmso/core/topology.py b/gmso/core/topology.py index 51a68127a..ecea969dd 100644 --- a/gmso/core/topology.py +++ b/gmso/core/topology.py @@ -339,6 +339,7 @@ def unique_site_labels(self, label_type="molecule", name_only=False): unique_tags.add(copy(getattr(site, label_type))) return unique_tags + @property def atom_types(self, include_virtual_types=False): """Return all atom_types in the topology. @@ -938,11 +939,11 @@ def update_topology(self): def _bookkeep_potentials(self): self._potentials_count = { - "atom_types": len(self.atom_types), - "bond_types": len(self.bond_types), - "angle_types": len(self.angle_types), - "dihedral_types": len(self.dihedral_types), - "improper_types": len(self.improper_types), + "atom_types": len(self.atom_types()), + "bond_types": len(self.bond_types()), + "angle_types": len(self.angle_types()), + "dihedral_types": len(self.dihedral_types()), + "improper_types": len(self.improper_types()), "pairpotential_types": len(self._pairpotential_types), } diff --git a/gmso/core/views.py b/gmso/core/views.py index 3acf1b3f2..5cbfdffaa 100644 --- a/gmso/core/views.py +++ b/gmso/core/views.py @@ -40,7 +40,7 @@ def get_parameters(potential): """Return hashable version of parameters for a potential.""" return ( tuple(potential.get_parameters().keys()), - tuple(map(lambda x: x.to_value(), potential.get_parameters().values())), + tuple(x.to_value() for x in potential.get_parameters().values()), ) @@ -72,11 +72,11 @@ class PotentialFilters: @staticmethod def all(): - return set( + return { f"{PotentialFilters.__name__}.{k}" for k, v in PotentialFilters.__dict__.items() if not k.startswith("__") and not callable(v) - ) + } potential_identifiers = { diff --git a/gmso/external/convert_mbuild.py b/gmso/external/convert_mbuild.py index b6b35b1a8..3a006a14a 100644 --- a/gmso/external/convert_mbuild.py +++ b/gmso/external/convert_mbuild.py @@ -160,7 +160,7 @@ def to_mbuild(topology: Topology, infer_hierarchy: bool = True) -> "mb.Compound" else: compound.name = topology.name - particle_map = dict() + particle_map = {} if not infer_hierarchy: particle_list = [] for site in topology.sites: @@ -173,8 +173,8 @@ def to_mbuild(topology: Topology, infer_hierarchy: bool = True) -> "mb.Compound" for molecule_tag in topology.unique_site_labels(label_type="molecule"): mb_molecule = mb.Compound() mb_molecule.name = molecule_tag.name if molecule_tag else "DefaultMolecule" - residue_dict = dict() - residue_dict_particles = dict() + residue_dict = {} + residue_dict_particles = {} if molecule_tag: sites_iter = topology.iter_sites("molecule", molecule_tag) @@ -228,7 +228,7 @@ def from_mbuild_box(mb_box: "mb.Box") -> "Box | None": # TODO: Unit tests if not isinstance(mb_box, mb.Box): - raise ValueError("Argument mb_box is not an mBuild Box") + raise TypeError("Argument mb_box is not an mBuild Box") if np.allclose(mb_box.lengths, [0, 0, 0]): logger.info("No box or boundingbox information detected, setting box to None") @@ -287,10 +287,9 @@ def _parse_site(site_map, particle, search_method, infer_element=False): def _parse_molecule_residue(site_map, compound): """Parse information necessary for residue and molecule labels when converting from mbuild.""" connected_subgraph = compound.bond_graph.connected_components() - molecule_tracker = dict() - residue_tracker = dict() - total_molecule_count = 0 - for molecule in connected_subgraph: + molecule_tracker = {} + residue_tracker = {} + for total_molecule_coun, molecule in enumerate(connected_subgraph): if len(molecule) == 1: ancestors = [molecule[0]] else: @@ -312,7 +311,6 @@ def _parse_molecule_residue(site_map, compound): else: molecule_tracker[molecule_tag.name] = 0 molecule_number = molecule_tracker[molecule_tag.name] - total_molecule_count += 1 """End of molecule parsing""" for particle in molecule: @@ -347,7 +345,7 @@ def _parse_group(site_map, compound, custom_groups): for particle in part.particles(): site_map[particle]["group"] = part.name try: - applied_groups = set(map(lambda x: x["group"], site_map.values())) + applied_groups = {x["group"] for x in site_map.values()} assert applied_groups == set(custom_groups) except AssertionError: logger.info( @@ -355,7 +353,7 @@ def _parse_group(site_map, compound, custom_groups): traversing compound hierachy. Only {applied_groups} are used.)""" ) elif not compound.children or not np.any( - list(map(lambda c: len(c.children), compound.children)) + [len(c.children) for c in compound.children] ): for particle in compound.particles(): site_map[particle]["group"] = compound.name diff --git a/gmso/external/convert_parmed.py b/gmso/external/convert_parmed.py index a0f3e3c1b..ada86ca79 100644 --- a/gmso/external/convert_parmed.py +++ b/gmso/external/convert_parmed.py @@ -48,7 +48,7 @@ def from_parmed(structure: "pmd.Structure", refer_type: bool = True) -> "gmso.To assert isinstance(structure, pmd.Structure), msg top = gmso.Topology(name=structure.title) - site_map = dict() + site_map = {} if np.all(structure.box): # add gmso box from structure @@ -377,13 +377,9 @@ def _add_conn_type_from_pmd( The independent variables. """ try: - member_types = list( - map(lambda x: x.atom_type.name, gmso_conn.connection_members) - ) + member_types = [x.atom_type.name for x in gmso_conn.connection_members] except AttributeError: - member_types = list( - map(lambda x: f"{x}: {x.atom_type})", gmso_conn.connection_members) - ) + member_types = [f"{x}: {x.atom_type})" for x in gmso_conn.connection_members] raise AttributeError( f"Parmed structure is missing atomtypes. One of the atomtypes in \ {member_types} is missing a type from the ParmEd structure.\ @@ -396,12 +392,9 @@ def get_classes(x): member_classes = list(map(get_classes, gmso_conn.connection_members)) except AttributeError: - member_classes = list( - map( - lambda x: f"{x}: {x.atom_type.name})", - gmso_conn.connection_members, - ) - ) + member_classes = [ + f"{x}: {x.atom_type.name})" for x in gmso_conn.connection_members + ] top_conntype = getattr(gmso, connStr)( name=name, parameters=conn_params, @@ -454,10 +447,10 @@ def to_parmed(top: "gmso.Topology", refer_type: bool = True) -> "pmd.Structure": ) # Maps - atom_map = dict() # Map site to atom - bond_map = dict() # Map top's bond to structure's bond - angle_map = dict() # Map top's angle to strucutre's angle - dihedral_map = dict() # Map top's dihedral to structure's dihedral + atom_map = {} # Map site to atom + bond_map = {} # Map top's bond to structure's bond + angle_map = {} # Map top's angle to strucutre's angle + dihedral_map = {} # Map top's dihedral to structure's dihedral # Set up unparametrized system # Build up atom @@ -543,7 +536,7 @@ def _check_independent_residues(structure): """Check to see if residues will constitute independent graphs.""" # Copy from foyer forcefield.py for res in structure.residues: - atoms_in_residue = set([*res.atoms]) + atoms_in_residue = {*res.atoms} bond_partners_in_residue = [ item for sublist in [atom.bond_partners for atom in res.atoms] @@ -571,12 +564,13 @@ def _atom_types_from_gmso(top, structure, atom_map): The destination parmed Structure """ # Maps - atype_map = dict() + atype_map = {} for atom_type in top.atom_types(filter_by=PotentialFilters.UNIQUE_NAME_CLASS): msg = f"Atom type {atom_type.name} expression does not match Parmed AtomType default expression" - assert expand(atom_type.expression) == expand( + if not expand(atom_type.expression) == expand( "4*epsilon*(-sigma**6/r**6 + sigma**12/r**12)" - ), msg + ): + raise GMSOError(msg) # Extract Topology atom type information atype_name = atom_type.name # Convert charge to elementary_charge @@ -623,10 +617,11 @@ def _bond_types_from_gmso(top, structure, bond_map): structure: parmed.Structure The destination parmed Structure """ - btype_map = dict() + btype_map = {} for bond_type in top.bond_types(filter_by=pfilter): msg = f"Bond type {bond_type.name} expression does not match Parmed BondType default expression" - assert expand(bond_type.expression) == expand("0.5 * k * (r-r_eq)**2"), msg + if not expand(bond_type.expression) == expand("0.5 * k * (r-r_eq)**2"): + raise GMSOError(msg) # Extract Topology bond_type information btype_k = 0.5 * float( bond_type.parameters["k"].to("kcal / (angstrom**2 * mol)").value @@ -659,12 +654,11 @@ def _angle_types_from_gmso(top, structure, angle_map): structure: parmed.Structure The destination parmed Structure """ - agltype_map = dict() + agltype_map = {} for angle_type in top.angle_types(filter_by=pfilter): msg = f"Angle type {angle_type.name} expression does not match Parmed AngleType default expression" - assert expand(angle_type.expression) == expand( - "0.5 * k * (theta-theta_eq)**2" - ), msg + if not expand(angle_type.expression) == expand("0.5 * k * (theta-theta_eq)**2"): + raise GMSOError(msg) # Extract Topology angle_type information agltype_k = 0.5 * float( angle_type.parameters["k"].to("kcal / (radian**2 * mol)").value @@ -674,7 +668,7 @@ def _angle_types_from_gmso(top, structure, angle_map): agltype = pmd.AngleType(agltype_k, agltype_theta_eq) # Type map to match Topology AngleType with Parmed AngleType # - for key, value in agltype_map.items(): + for value in agltype_map.values(): if value == agltype: agltype = value break @@ -704,7 +698,7 @@ def _dihedral_types_from_gmso(top, structure, dihedral_map): structure: parmed.Structure The destination parmed Structure """ - dtype_map = dict() + dtype_map = {} for dihedral_type in top.dihedral_types(filter_by=pfilter): msg = f"Dihedral type {dihedral_type.name} expression does not match Parmed DihedralType default expressions (Periodics, RBTorsions)" if expand(dihedral_type.expression) == expand( diff --git a/gmso/formats/gro.py b/gmso/formats/gro.py index 230a86c6e..48220877e 100644 --- a/gmso/formats/gro.py +++ b/gmso/formats/gro.py @@ -136,7 +136,7 @@ def write_gro( "{} written by GMSO {} at {}\n".format( top.name if top.name is not None else "", gmso.__version__, - str(datetime.datetime.now()), + str(datetime.datetime.now(datetime.timezone.utc).astimezone()), ) ) out_file.write(f"{top.n_sites:d}\n") @@ -168,8 +168,8 @@ def _prepare_atoms(top, updated_positions, n_decimals): ) # we need to sort through the sites to provide a unique number for each molecule/residue # we will store the unique id in dictionary where the key is the idx - site_res_id = dict() - seen = dict() + site_res_id = {} + seen = {} for idx, site in enumerate(top.sites): if site.molecule: if site.molecule not in seen: diff --git a/gmso/formats/lammpsdata.py b/gmso/formats/lammpsdata.py index 419df1b3e..96dedb109 100644 --- a/gmso/formats/lammpsdata.py +++ b/gmso/formats/lammpsdata.py @@ -320,10 +320,9 @@ def _get_connection(filename, topology, base_unyts, connection_type): if types is False: return topology templates = PotentialTemplateLibrary() - connection_type_lines = open(filename, "r").readlines()[ - i + 2 : i + n_connection_types + 2 - ] - connection_type_list = list() + with open(filename, "r") as f: + connection_type_lines = f.readlines()[i + 2 : i + n_connection_types + 2] + connection_type_list = [] for line in connection_type_lines: if connection_type == "bond": template_potential = templates["LAMMPSHarmonicBondPotential"] @@ -407,7 +406,8 @@ def _get_connection(filename, topology, base_unyts, connection_type): n_connections = int(line.split()[0]) if connection_type.capitalize() + "s" in line.split(): break - connection_lines = open(filename, "r").readlines()[i + 2 : i + n_connections + 2] + with open(filename, "r") as f: + connection_lines = f.readlines()[i + 2 : i + n_connections + 2] # Determine number of sites to generate if connection_type == "bond": n_sites = 2 @@ -416,12 +416,12 @@ def _get_connection(filename, topology, base_unyts, connection_type): else: n_sites = 4 for i, line in enumerate(connection_lines): - site_list = list() + site_list = [] for j in range(n_sites): site = topology.sites[int(line.split()[j + 2]) - 1] site_list.append(site) ctype = copy.copy(connection_type_list[int(line.split()[1]) - 1]) - ctype.member_types = tuple(map(lambda x: x.atom_type.name, site_list)) + ctype.member_types = tuple(x.atom_type.name for x in site_list) ctype.member_classes = ctype.member_types if connection_type == "bond": connection = Bond( @@ -456,7 +456,8 @@ def _get_atoms(filename, topology, base_unyts, type_list): n_atoms = int(line.split()[0]) if "Atoms" in line.split(): break - atom_lines = open(filename, "r").readlines()[i + 2 : i + n_atoms + 2] + with open(filename, "r") as f: + atom_lines = f.readlines()[i + 2 : i + n_atoms + 2] for line in atom_lines: atom_line = line.split() atom_type = atom_line[2] @@ -544,8 +545,9 @@ def _get_ff_information(filename, base_unyts, topology): break if types is False: return topology - mass_lines = open(filename, "r").readlines()[i + 2 : i + n_atomtypes + 2] - type_list = list() + with open(filename, "r") as f: + mass_lines = f.readlines()[i + 2 : i + n_atomtypes + 2] + type_list = [] for line in mass_lines: atom_type = AtomType( name=line.split()[0], @@ -560,7 +562,8 @@ def _get_ff_information(filename, base_unyts, topology): # Need to figure out if we're going have mixing rules printed out # Currently only reading in LJ params warn_ljcutBool = False - pair_lines = open(filename, "r").readlines()[i + 2 : i + n_atomtypes + 2] + with open(filename, "r") as f: + pair_lines = f.readlines()[i + 2 : i + n_atomtypes + 2] for i, pair in enumerate(pair_lines): if len(pair.split()) == 3: type_list[i].parameters["sigma"] = float(pair.split()[2]) * get_units( @@ -642,7 +645,7 @@ def _write_header(out_file, top, atom_style, dihedral_parser): "{} written by {} at {} using the GMSO LAMMPS Writer\n\n\n".format( top.name if top.name is not None else "Topology", os.environ.get("USER"), - str(datetime.datetime.now()), + str(datetime.datetime.now(datetime.timezone.utc).astimezone()), ) ) out_file.write(f"{top.n_sites:d} atoms\n") @@ -1059,10 +1062,7 @@ def _write_impropertypes(out_file, top, base_unyts, parser, cfactorsDict): base_msg = "{}\t" # handles index end_msg = "# {}\t{}\t{}\t{}\n" - if ( - parser.__name__ == "parse_cvff_style_improper" - or "parse_harmonic_style_improper" - ): # one cvff set per improper layer + if True: # one cvff set per improper layer ndecimalsDict = {"k": 6, "n": 0, "phi_eq": 0} idx = 0 improper_typesList = [] @@ -1220,17 +1220,13 @@ def _try_default_potential_conversions(top, potentialsDict): def _default_lj_val(top, source): """Generate default lj non-dimensional values from topology.""" if source == "length": - return copy.deepcopy( - max(list(map(lambda x: x.parameters["sigma"], top.atom_types))) - ) + return copy.deepcopy(max([x.parameters["sigma"] for x in top.atom_types])) elif source == "energy": - return copy.deepcopy( - max(list(map(lambda x: x.parameters["epsilon"], top.atom_types))) - ) + return copy.deepcopy(max([x.parameters["epsilon"] for x in top.atom_types])) elif source == "mass": - return copy.deepcopy(max(list(map(lambda x: x.mass, top.atom_types)))) + return copy.deepcopy(max([x.mass for x in top.atom_types])) elif source == "charge": - return copy.deepcopy(max(list(map(lambda x: x.charge, top.atom_types)))) + return copy.deepcopy(max([x.charge for x in top.atom_types])) else: raise ValueError( f"Provided {source} for default LJ cannot be found in the topology." diff --git a/gmso/formats/mcf.py b/gmso/formats/mcf.py index c532e121b..b7e0e143c 100644 --- a/gmso/formats/mcf.py +++ b/gmso/formats/mcf.py @@ -61,11 +61,10 @@ def write_mcf(top: "Topology", filename: str | Path) -> None: for molecule in top.unique_site_labels(name_only=True): subtops.append(top.create_subtop("molecule", (molecule, 0))) - if len(subtops) > 1: - if len(filename) != len(subtops): - raise ValueError( - "write_mcf: Number of filenames must match number of unique species in the Topology object" - ) + if len(subtops) > 1 and len(filename) != len(subtops): + raise ValueError( + "write_mcf: Number of filenames must match number of unique species in the Topology object" + ) for idx, subtop in enumerate(subtops): _check_compatibility(subtop) @@ -88,7 +87,7 @@ def write_mcf(top: "Topology", filename: str | Path) -> None: "!***************************************" "****************************************\n" f"!File {filename} written by gmso {__version__} " - f"at {datetime.datetime.now()!s}\n\n" + f"at {datetime.datetime.now(datetime.timezone.utc).astimezone()!s}\n\n" ) mcf.write(header) @@ -193,12 +192,12 @@ def _id_rings_fragments(top): for idx in adjacent_atoms: adj_to_ring[idx] = True # Now ID the other fragments - for idx in neigh_dict: - if len(neigh_dict[idx]) > 1: + for idx, value in neigh_dict.items(): + if len(value) > 1: if in_ring[idx] is True: continue else: - frag_list.append([idx] + neigh_dict[idx]) + frag_list.append([idx] + value) # Now find connectivity (shared bonds) for i in range(len(frag_list)): frag1 = frag_list[i] @@ -639,7 +638,7 @@ 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 for site in top.sites]): + if not all(site.atom_type for site in top.sites): raise GMSOError("MCF writing not supported without parameterized forcefield.") accepted_potentials = ( potential_templates["LennardJonesPotential"], @@ -692,7 +691,9 @@ def _get_dihedral_style(dihedral): def _get_potential_style(styles, potential): """Return the potential style.""" for style, ref in styles.items(): - if ref.independent_variables == potential.independent_variables: - if symengine.expand(ref.expression - potential.expression) == 0: - return style + if ( + ref.independent_variables == potential.independent_variables + and symengine.expand(ref.expression - potential.expression) == 0 + ): + return style return False diff --git a/gmso/formats/mol2.py b/gmso/formats/mol2.py index f83f79a56..8ac77b129 100644 --- a/gmso/formats/mol2.py +++ b/gmso/formats/mol2.py @@ -61,12 +61,12 @@ def read_mol2( with open(filename, "r") as f: fcontents = f.readlines() - sections = {"Meta": list()} + sections = {"Meta": []} section_key = "Meta" # Used to parse the meta info at top of the file for line in fcontents: if "@" in line: section_key = line.strip("\n") - sections[section_key] = list() + sections[section_key] = [] else: sections[section_key].append(line) @@ -78,14 +78,14 @@ def read_mol2( "@FF_PBC": _parse_box, "@MOLECULE": _parse_molecule, } - for section in sections: + for section, value in sections.items(): if section not in supported_rti: logger.info( f"The record type indicator {section} is not supported. " "Skipping current section and moving to the next RTI header." ) else: - supported_rti[section](topology, sections[section], verbose) + supported_rti[section](topology, value, verbose) # TODO: read in parameters to correct attribute as well. This can be saved in various rti sections. return topology @@ -118,7 +118,7 @@ def write_mol2( "{} written by GMSO {} at {}\n".format( top.name if top.name is not None else "", gmso_version, - str(datetime.datetime.now()), + str(datetime.datetime.now(datetime.timezone.utc).astimezone()), ) ) _write_molecule_info(top, out_file) diff --git a/gmso/formats/networkx.py b/gmso/formats/networkx.py index fe4813c1b..5f1138ace 100644 --- a/gmso/formats/networkx.py +++ b/gmso/formats/networkx.py @@ -148,7 +148,7 @@ def interactive_networkx_bonds(topology, additional_labels=None): widgets.Dropdown( options=names_tuple, layout=widgets.Layout(width="30%"), - style=dict(description_width="initial"), + style={"description_width": "initial"}, description=descriptions[i], ) ) @@ -227,7 +227,7 @@ def interactive_networkx_angles(topology): widgets.Dropdown( options=names_tuple, layout=widgets.Layout(width="30%"), - style=dict(description_width="initial"), + style={"description_width": "initial"}, description=descriptions[i], ) ) @@ -312,7 +312,7 @@ def interactive_networkx_dihedrals(topology): widgets.Dropdown( options=(names_tuple), layout=widgets.Layout(width="30%"), - style=dict(description_width="initial"), + style={"description_width": "initial"}, description=descriptions[i], ) ) diff --git a/gmso/formats/top.py b/gmso/formats/top.py index cefd1e416..f35e7010f 100644 --- a/gmso/formats/top.py +++ b/gmso/formats/top.py @@ -85,14 +85,14 @@ def write_top( out_file.write( "; File {} written by GMSO at {}\n\n".format( top.name if top.name is not None else "", - str(datetime.datetime.now()), + str(datetime.datetime.now(datetime.timezone.utc).astimezone()), ) ) out_file.write( "[ defaults ]\n; nbfunc\tcomb-rule\tgen-pairs\tfudgeLJ\t\tfudgeQQ\n" ) out_file.write( - "{0}\t\t{1}\t\t{2}\t\t{3}\t\t{4}\n\n".format( + "{}\t\t{}\t\t{}\t\t{}\t\t{}\n\n".format( top_vars["nbfunc"], top_vars["comb-rule"], top_vars["gen-pairs"], @@ -106,7 +106,7 @@ def write_top( ) out_file.writelines( - "{0:12s}{1:4s}{2:12.5f}{3:12.5f}\t{4:4s}{5:12.5f}{6:12.5f}\n".format( + "{:12s}{:4s}{:12.5f}{:12.5f}\t{:4s}{:12.5f}{:12.5f}\n".format( atom_type.name, str(_lookup_atomic_number(atom_type)), atom_type.mass.in_units(u.amu).value, @@ -148,7 +148,7 @@ def write_top( out_file.write("\n[ moleculetype ]\n; name\tnrexcl\n") # TODO: Lookup and join nrexcl from each molecule object - out_file.write("{0}\t{1}\n\n".format(tag, top_vars["nrexcl"])) + out_file.write("{}\t{}\n\n".format(tag, top_vars["nrexcl"])) """Write out atoms for each unique molecule.""" out_file.write( @@ -157,11 +157,11 @@ def write_top( # Each unique molecule need to be reindexed (restarting from 0) # The shifted_idx_map is needed to make sure all the atom index used in # latter connection sections are acurate - shifted_idx_map = dict() + shifted_idx_map = {} for idx, site in enumerate(unique_molecules[tag]["sites"]): shifted_idx_map[top.get_index(site)] = idx out_file.write( - "{0:8s}{1:12s}{2:8s}{3:12s}{4:8s}{5:4s}{6:12.5f}{7:12.5f}\n".format( + "{:8s}{:12s}{:8s}{:12s}{:8s}{:4s}{:12.5f}{:12.5f}\n".format( str(idx + 1), site.atom_type.name, str(site.molecule.number + 1 if site.molecule else 1), @@ -209,9 +209,7 @@ def write_top( "; OW_idx\tfunct\tdoh\tdhh\n" ) out_file.write( - "{0:4s}{1:4s}{2:15.5f}{3:15.5f}\n".format( - str(ow_idx), "1", doh, dhh - ) + "{:4s}{:4s}{:15.5f}{:15.5f}\n".format(str(ow_idx), "1", doh, dhh) ) # Write exclusion @@ -244,8 +242,8 @@ def write_top( ) elif conn_group in ["dihedrals", "impropers"]: proper_groups = { - "RyckaertBellemansTorsionPotential": list(), - "PeriodicTorsionPotential": list(), + "RyckaertBellemansTorsionPotential": [], + "PeriodicTorsionPotential": [], } for dihedral in unique_molecules[tag][conn_group]: ptype = pot_types[dihedral.connection_type] @@ -316,7 +314,7 @@ def write_top( out_file.write("[ molecules ]\n; molecule\tnmols\n") for tag in unique_molecules: out_file.write( - "{0}\t{1}\n".format(tag, len(unique_molecules[tag]["subtags"])) + "{}\t{}\n".format(tag, len(unique_molecules[tag]["subtags"])) ) @@ -349,7 +347,7 @@ def _validate_compatibility(top): def _get_top_vars(top, top_vars): """Generate a dictionary of values for the defaults directive.""" combining_rule_to_gmx = {"lorentz": 2, "geometric": 3} - default_top_vars = dict() + default_top_vars = {} default_top_vars["nbfunc"] = 1 # modify this to check for lj or buckingham default_top_vars["comb-rule"] = combining_rule_to_gmx[top.combining_rule] default_top_vars["gen-pairs"] = "yes" @@ -366,7 +364,7 @@ def _get_top_vars(top, top_vars): def _get_unique_molecules(top): unique_molecules = { tag: { - "subtags": list(), + "subtags": [], } for tag in top.unique_site_labels("molecule", name_only=True) } @@ -375,58 +373,54 @@ def _get_unique_molecules(top): unique_molecules[molecule.name]["subtags"].append(molecule) if len(unique_molecules) == 0: - unique_molecules[top.name] = dict() + unique_molecules[top.name] = {} unique_molecules[top.name]["subtags"] = [top.name] unique_molecules[top.name]["sites"] = list(top.sites) - unique_molecules[top.name]["position_restraints"] = list( + unique_molecules[top.name]["position_restraints"] = [ site for site in top.sites if site.restraint - ) + ] unique_molecules[top.name]["pairs"] = generate_pairs_lists( top, refer_from_scaling_factor=True )["pairs14"] unique_molecules[top.name]["bonds"] = list(top.bonds) - unique_molecules[top.name]["bond_restraints"] = list( + unique_molecules[top.name]["bond_restraints"] = [ bond for bond in top.bonds if bond.restraint - ) + ] unique_molecules[top.name]["angles"] = list(top.angles) - unique_molecules[top.name]["angle_restraints"] = list( + unique_molecules[top.name]["angle_restraints"] = [ angle for angle in top.angles if angle.restraint - ) + ] unique_molecules[top.name]["dihedrals"] = list(top.dihedrals) - unique_molecules[top.name]["dihedral_restraints"] = list( + unique_molecules[top.name]["dihedral_restraints"] = [ dihedral for dihedral in top.dihedrals if dihedral.restraint - ) + ] unique_molecules[molecule.name]["impropers"] = list(top.impropers) else: - for tag in unique_molecules: - molecule = unique_molecules[tag]["subtags"][0] - unique_molecules[tag]["sites"] = list( - top.iter_sites(key="molecule", value=molecule) - ) - unique_molecules[tag]["position_restraints"] = list( + for molecules in unique_molecules.values(): + molecule = molecules["subtags"][0] + molecules["sites"] = list(top.iter_sites(key="molecule", value=molecule)) + molecules["position_restraints"] = [ site for site in top.sites if (site.restraint and site.molecule == molecule) - ) - unique_molecules[tag]["pairs"] = generate_pairs_lists(top, molecule)[ - "pairs14" ] - unique_molecules[tag]["bonds"] = list(molecule_bonds(top, molecule)) - unique_molecules[tag]["bond_restraints"] = list( + molecules["pairs"] = generate_pairs_lists(top, molecule)["pairs14"] + molecules["bonds"] = list(molecule_bonds(top, molecule)) + molecules["bond_restraints"] = [ bond for bond in molecule_bonds(top, molecule) if bond.restraint - ) - unique_molecules[tag]["angles"] = list(molecule_angles(top, molecule)) - unique_molecules[tag]["angle_restraints"] = list( + ] + molecules["angles"] = list(molecule_angles(top, molecule)) + molecules["angle_restraints"] = [ angle for angle in molecule_angles(top, molecule) if angle.restraint - ) - unique_molecules[tag]["dihedrals"] = list(molecule_dihedrals(top, molecule)) - unique_molecules[tag]["dihedral_restraints"] = list( + ] + molecules["dihedrals"] = list(molecule_dihedrals(top, molecule)) + molecules["dihedral_restraints"] = [ dihedral for dihedral in molecule_dihedrals(top, molecule) if dihedral.restraint - ) - unique_molecules[tag]["impropers"] = list(molecule_impropers(top, molecule)) + ] + molecules["impropers"] = list(molecule_impropers(top, molecule)) return unique_molecules @@ -455,7 +449,7 @@ def _write_pairs(top, pair, shifted_idx_map): shifted_idx_map[top.get_index(pair[1])] + 1, ] - line = "{0:8s}{1:8s}{2:4s}\n".format( + line = "{:8s}{:8s}{:4s}\n".format( str(pair_idx[0]), str(pair_idx[1]), "1", @@ -479,9 +473,9 @@ def _write_connection(top, connection, potential_name, shifted_idx_map): def _harmonic_bond_potential_writer(top, bond, shifted_idx_map): """Write harmonic bond information.""" eq_connsList = bond.equivalent_members() - indexList = [tuple(map(lambda x: top.get_index(x), conn)) for conn in eq_connsList] - sorted_indicesList = sorted(indexList)[0] - line = "{0:8s}{1:8s}{2:4s}{3:15.5f}{4:15.5f}\n".format( + indexList = [tuple(top.get_index(x) for x in conn) for conn in eq_connsList] + sorted_indicesList = min(indexList) + line = "{:8s}{:8s}{:4s}{:15.5f}{:15.5f}\n".format( str(shifted_idx_map[sorted_indicesList[0]] + 1), str(shifted_idx_map[sorted_indicesList[1]] + 1), "1", @@ -494,9 +488,9 @@ def _harmonic_bond_potential_writer(top, bond, shifted_idx_map): def _fene_bond_potential_writer(top, bond, shifted_idx_map): """Write FENE bond information.""" eq_connsList = bond.equivalent_members() - indexList = [tuple(map(lambda x: top.get_index(x), conn)) for conn in eq_connsList] - sorted_indicesList = sorted(indexList)[0] - line = "{0:8s}{1:8s}{2:4s}{3:15.5f}{4:15.5f}\n".format( + indexList = [tuple(top.get_index(x) for x in conn) for conn in eq_connsList] + sorted_indicesList = min(indexList) + line = "{:8s}{:8s}{:4s}{:15.5f}{:15.5f}\n".format( str(shifted_idx_map[sorted_indicesList[0]] + 1), str(shifted_idx_map[sorted_indicesList[1]] + 1), "7", @@ -509,10 +503,10 @@ def _fene_bond_potential_writer(top, bond, shifted_idx_map): def _harmonic_angle_potential_writer(top, angle, shifted_idx_map): """Write harmonic angle information.""" eq_connsList = angle.equivalent_members() - indexList = [tuple(map(lambda x: top.get_index(x), conn)) for conn in eq_connsList] - sorted_indicesList = sorted(indexList)[0] + indexList = [tuple(top.get_index(x) for x in conn) for conn in eq_connsList] + sorted_indicesList = min(indexList) - line = "{0:8s}{1:8s}{2:8s}{3:4s}{4:15.5f}{5:15.5f}\n".format( + line = "{:8s}{:8s}{:8s}{:4s}{:15.5f}{:15.5f}\n".format( str(shifted_idx_map[sorted_indicesList[0]] + 1), str(shifted_idx_map[sorted_indicesList[1]] + 1), str(shifted_idx_map[sorted_indicesList[2]] + 1), @@ -525,7 +519,7 @@ def _harmonic_angle_potential_writer(top, angle, shifted_idx_map): def _ryckaert_bellemans_torsion_writer(top, dihedral, shifted_idx_map): """Write Ryckaert-Bellemans Torsion information.""" - line = "{0:8s}{1:8s}{2:8s}{3:8s}{4:4s}{5:15.5f}{6:15.5f}{7:15.5f}{8:15.5f}{9:15.5f}{10:15.5f}\n".format( + line = "{:8s}{:8s}{:8s}{:8s}{:4s}{:15.5f}{:15.5f}{:15.5f}{:15.5f}{:15.5f}{:15.5f}\n".format( str(shifted_idx_map[top.get_index(dihedral.connection_members[0])] + 1), str(shifted_idx_map[top.get_index(dihedral.connection_members[1])] + 1), str(shifted_idx_map[top.get_index(dihedral.connection_members[2])] + 1), @@ -560,9 +554,9 @@ def _periodic_torsion_writer(top, dihedral, shifted_idx_map): else: raise TypeError(f"Type {type(dihedral)} not supported.") - lines = list() + lines = [] for i in range(layers): - line = "{0:8s}{1:8s}{2:8s}{3:8s}{4:4s}{5:15.5f}{6:15.5f}{7:4}\n".format( + line = "{:8s}{:8s}{:8s}{:8s}{:4s}{:15.5f}{:15.5f}{:4}\n".format( str(shifted_idx_map[top.get_index(dihedral.connection_members[0])] + 1), str(shifted_idx_map[top.get_index(dihedral.connection_members[1])] + 1), str(shifted_idx_map[top.get_index(dihedral.connection_members[2])] + 1), @@ -592,7 +586,7 @@ def _write_restraint(top, site_or_conn, type, shifted_idx_map): def _position_restraints_writer(top, site, shifted_idx_map): """Write site position restraint information.""" - line = "{0:8s}{1:4s}{2:15.5f}{3:15.5f}{4:15.5f}\n".format( + line = "{:8s}{:4s}{:15.5f}{:15.5f}{:15.5f}\n".format( str(shifted_idx_map[top.get_index(site)] + 1), "1", site.restraint["kx"].in_units(u.Unit("kJ/(mol * nm**2)")).value, @@ -604,7 +598,7 @@ def _position_restraints_writer(top, site, shifted_idx_map): def _bond_restraint_writer(top, bond, shifted_idx_map): """Write bond restraint information.""" - line = "{0:8s}{1:8s}{2:4s}{3:15.5f}{4:15.5f}\n".format( + line = "{:8s}{:8s}{:4s}{:15.5f}{:15.5f}\n".format( str(shifted_idx_map[top.get_index(bond.connection_members[0])] + 1), str(shifted_idx_map[top.get_index(bond.connection_members[1])] + 1), "6", @@ -616,7 +610,7 @@ def _bond_restraint_writer(top, bond, shifted_idx_map): def _angle_restraint_writer(top, angle, shifted_idx_map): """Write angle restraint information.""" - line = "{0:8s}{1:8s}{2:8s}{3:8s}{4:4s}{5:15.5f}{6:15.5f}{7:4}\n".format( + line = "{:8s}{:8s}{:8s}{:8s}{:4s}{:15.5f}{:15.5f}{:4}\n".format( str(shifted_idx_map[top.get_index(angle.connection_members[1])] + 1), str(shifted_idx_map[top.get_index(angle.connection_members[0])] + 1), str(shifted_idx_map[top.get_index(angle.connection_members[1])] + 1), @@ -631,7 +625,7 @@ def _angle_restraint_writer(top, angle, shifted_idx_map): def _dihedral_restraint_writer(top, dihedral, shifted_idx_map): """Write dihedral restraint information.""" - line = "{0:8s}{1:8s}{2:8s}{3:8s}{4:4s}{5:15.5f}{6:15.5f}{7:15.5f}\n".format( + line = "{:8s}{:8s}{:8s}{:8s}{:4s}{:15.5f}{:15.5f}{:15.5f}\n".format( str(shifted_idx_map[top.get_index(dihedral.connection_members[0])] + 1), str(shifted_idx_map[top.get_index(dihedral.connection_members[1])] + 1), str(shifted_idx_map[top.get_index(dihedral.connection_members[2])] + 1), diff --git a/gmso/parameterization/foyer_utils.py b/gmso/parameterization/foyer_utils.py index 80b56db99..2846eab04 100644 --- a/gmso/parameterization/foyer_utils.py +++ b/gmso/parameterization/foyer_utils.py @@ -41,7 +41,7 @@ def get_topology_graph( if label_type: assert label_type in ("group", "molecule"), label_type - is_group = True if label_type == "group" else False + is_group = label_type == "group" pseudo_top = namedtuple("PseudoTop", ("sites", "bonds")) gmso_topology = pseudo_top( tuple(gmso_topology.iter_sites(label_type, label)), diff --git a/gmso/parameterization/parameterize.py b/gmso/parameterization/parameterize.py index 7e31135a5..235f61255 100644 --- a/gmso/parameterization/parameterize.py +++ b/gmso/parameterization/parameterize.py @@ -16,7 +16,7 @@ def apply( identify_connections: bool = False, speedup_by_molgraph: bool = False, speedup_by_moltag: bool = False, - ignore_params: list[str] | set[str] | tuple[str, ...] = ["improper"], + ignore_params: list[str] | set[str] | tuple[str, ...] | None = None, remove_untyped: bool = True, fast_copy: bool = True, ) -> Topology: @@ -90,17 +90,19 @@ def apply( >>> ff_ethanol = ForceField("oplsaa.xml") >>> typed_top = apply(top, {"water": ff_water, "ethanol": ff_ethanol}) """ - ignore_params = set([option.lower().rstrip("s") for option in ignore_params]) + if ignore_params is None: + ignore_params = ["improper"] + ignore_params = {option.lower().rstrip("s") for option in ignore_params} config = TopologyParameterizationConfig.model_validate( - dict( - match_ff_by=match_ff_by, - identify_connections=identify_connections, - speedup_by_molgraph=speedup_by_molgraph, - speedup_by_moltag=speedup_by_moltag, - ignore_params=ignore_params, - remove_untyped=remove_untyped, - fast_copy=fast_copy, - ) + { + "match_ff_by": match_ff_by, + "identify_connections": identify_connections, + "speedup_by_molgraph": speedup_by_molgraph, + "speedup_by_moltag": speedup_by_moltag, + "ignore_params": ignore_params, + "remove_untyped": remove_untyped, + "fast_copy": fast_copy, + } ) parameterizer = TopologyParameterizer( topology=top, forcefields=forcefields, config=config diff --git a/gmso/parameterization/topology_parameterizer.py b/gmso/parameterization/topology_parameterizer.py index 09f9422fd..3ec3b21a4 100644 --- a/gmso/parameterization/topology_parameterizer.py +++ b/gmso/parameterization/topology_parameterizer.py @@ -144,7 +144,7 @@ def _parameterize_connections( dihedrals = entry["dihedrals"] impropers = entry["impropers"] else: - is_group = True if label_type == "group" else False + is_group = label_type == "group" bonds = molecule_bonds(top, label, is_group) angles = molecule_angles(top, label, is_group) dihedrals = molecule_dihedrals(top, label, is_group) @@ -156,20 +156,20 @@ def _parameterize_connections( impropers = top.impropers self._apply_connection_parameters( - bonds, ff, False if "bond" in self.config.ignore_params else True + bonds, ff, not "bond" in self.config.ignore_params ) self._apply_connection_parameters( - angles, ff, False if "angle" in self.config.ignore_params else True + angles, ff, not "angle" in self.config.ignore_params ) self._apply_connection_parameters( dihedrals, ff, - False if "dihedral" in self.config.ignore_params else True, + not "dihedral" in self.config.ignore_params, ) self._apply_connection_parameters( impropers, ff, - False if "improper" in self.config.ignore_params else True, + not "improper" in self.config.ignore_params, ) def _parameterize_virtual_sites(self, top, sites, bonds, ff): @@ -192,16 +192,16 @@ def _parameterize_virtual_sites(self, top, sites, bonds, ff): self._apply_virtual_site_parameters( virtual_sites, ff, - False if "virtual_site" in self.config.ignore_params else True, + not "virtual_site" in self.config.ignore_params, ) def _apply_connection_parameters(self, connections, ff, error_on_missing=True): """Find and assign potentials from the forcefield for the provided connections.""" - visited = dict() - sig_cache = dict() + visited = {} + sig_cache = {} for connection in connections: use_classes = all( - [site.atom_type.atomclass for site in connection.connection_members] + site.atom_type.atomclass for site in connection.connection_members ) if use_classes: sig = tuple( @@ -257,7 +257,7 @@ def _apply_connection_parameters(self, connections, ff, error_on_missing=True): def _apply_virtual_site_parameters(self, virtual_sites, ff, error_on_missing=True): """Find and assign potentials from the forcefield for the provided virtual_sites.""" - visited = dict() + visited = {} for virtual_site in virtual_sites: group, vtype_identifiers = self.virtual_site_identifier(virtual_site) @@ -306,7 +306,7 @@ def _parameterize( if label and label_type: forcefield = self.get_ff(label) sites = top.iter_sites(label_type, label) - bonds = molecule_bonds(top, label, True if label_type == "group" else False) + bonds = molecule_bonds(top, label, label_type == "group") else: forcefield = self.get_ff(top.name) sites = top.sites @@ -324,7 +324,7 @@ def _parameterize( def _set_combining_rule(self): """Verify all the provided forcefields have the same combining rule and set it for the Topology.""" if isinstance(self.forcefields, dict): - all_comb_rules = set(ff.combining_rule for ff in self.forcefields.values()) + all_comb_rules = {ff.combining_rule for ff in self.forcefields.values()} else: all_comb_rules = {self.forcefields.combining_rule} @@ -483,8 +483,8 @@ def virtual_site_identifier( """Return the group and list of identifiers for a virtual site to query the forcefield for its potential.""" group = POTENTIAL_GROUPS[type(virtual_site)] return group, [ - list(member.atom_type.name for member in virtual_site.parent_sites), - list(member.atom_type.atomclass for member in virtual_site.parent_sites), + [member.atom_type.name for member in virtual_site.parent_sites], + [member.atom_type.atomclass for member in virtual_site.parent_sites], ] @staticmethod @@ -506,7 +506,7 @@ def _get_atomtypes( if speedup_by_moltag: # Iterate through foyer_topology_graph, which is a subgraph of label_type - typemap, reference = dict(), dict() + typemap, reference = {}, {} for connected_component in nx.connected_components(foyer_topology_graph): subgraph = foyer_topology_graph.subgraph(connected_component) nodes_idx = tuple(subgraph.nodes) diff --git a/gmso/tests/base_test.py b/gmso/tests/base_test.py index a961bfcc9..7db9bebc9 100644 --- a/gmso/tests/base_test.py +++ b/gmso/tests/base_test.py @@ -447,11 +447,9 @@ def test_connection_equality(conn1, conn2): return False if conn1.name != conn2.name: return False - if getattr(conn1, connection_types_attrs_map[type(conn1)]) != getattr( + return getattr(conn1, connection_types_attrs_map[type(conn1)]) == getattr( conn2, connection_types_attrs_map[type(conn2)] - ): - return False - return True + ) return test_connection_equality @@ -462,10 +460,7 @@ def test_box_equivalence(top1, top2): return u.allclose_units( top1.box.lengths, top2.box.lengths ) and u.allclose_units(top1.box.angles, top2.box.angles) - elif not top1.box and not top2.box: - return True - else: - return False + return bool(not top1.box and not top2.box) return test_box_equivalence @@ -557,7 +552,7 @@ def pairpotentialtype_top(self): expression="r + 1", independent_variables="r", parameters={}, - member_types=tuple(["a1", "a2"]), + member_types=("a1", "a2"), ) top.add_pairpotentialtype(pptype12) diff --git a/gmso/tests/parameterization/parameterization_base_test.py b/gmso/tests/parameterization/parameterization_base_test.py index df0425db7..6999a4e43 100644 --- a/gmso/tests/parameterization/parameterization_base_test.py +++ b/gmso/tests/parameterization/parameterization_base_test.py @@ -52,23 +52,21 @@ def _assert_same_connection_params(top1, top2, connection_type="bonds"): for connection in getattr(top1, connection_type): eq_connsList = connection.equivalent_members() indexList = [ - tuple(map(lambda x: top1.get_index(x), conn)) - for conn in eq_connsList + tuple(top1.get_index(x) for x in conn) for conn in eq_connsList ] - atom_indicesList = sorted(indexList)[0] + atom_indicesList = min(indexList) connection_types_top1[atom_indicesList] = connection connection_types_top2 = {} for connection in getattr(top2, connection_type): eq_connsList = connection.equivalent_members() indexList = [ - tuple(map(lambda x: top2.get_index(x), conn)) - for conn in eq_connsList + tuple(top2.get_index(x) for x in conn) for conn in eq_connsList ] - atom_indicesList = sorted(indexList)[0] + atom_indicesList = min(indexList) connection_types_top2[atom_indicesList] = connection - for key in connection_types_top1: - conn1 = connection_types_top1[key] + for key, val in connection_types_top1.items(): + conn1 = val conn2 = connection_types_top2[key] conn_type_attr = connection_type[:-1] + "_type" conn_type1 = getattr(conn1, conn_type_attr) diff --git a/gmso/tests/parameterization/test_impropers_parameterization.py b/gmso/tests/parameterization/test_impropers_parameterization.py index c4036d12d..76e7b38d1 100644 --- a/gmso/tests/parameterization/test_impropers_parameterization.py +++ b/gmso/tests/parameterization/test_impropers_parameterization.py @@ -17,7 +17,7 @@ class TestImpropersParameterization(ParameterizationBaseTest): def test_improper_parameterization(self, fake_improper_ff_gmso, ethane): ethane.identify_connections() - apply(ethane, fake_improper_ff_gmso, ignore_params=list()) + apply(ethane, fake_improper_ff_gmso, ignore_params=[]) lib = PotentialTemplateLibrary() template_improper_type = lib["PeriodicImproperPotential"] @@ -57,7 +57,7 @@ def test_improper_parameterization(self, fake_improper_ff_gmso, ethane): def test_improper_assertion_error(self, ethane_methane_top, oplsaa_gmso): with pytest.raises(ParameterizationError): - apply(ethane_methane_top, oplsaa_gmso, ignore_params=list()) + apply(ethane_methane_top, oplsaa_gmso, ignore_params=[]) @pytest.mark.parametrize( "mol2_loc", diff --git a/gmso/tests/parameterization/test_molecule_utils.py b/gmso/tests/parameterization/test_molecule_utils.py index 3b8ad622d..22625070f 100644 --- a/gmso/tests/parameterization/test_molecule_utils.py +++ b/gmso/tests/parameterization/test_molecule_utils.py @@ -58,10 +58,7 @@ def test_molecule_bonds(self, top_from_mbuild): for bond in bonds: assert _conn_in_molecule(bond, molecule) - bond_members = map( - lambda b: tuple(map(lambda s: s.name, b.connection_members)), - bonds, - ) + bond_members = (tuple(s.name for s in b.connection_members) for b in bonds) expected_members = {("C", "H"), ("C", "C"), ("H", "C")} assert all(b_member in expected_members for b_member in bond_members) @@ -72,9 +69,8 @@ def test_molecule_angles(self, top_from_mbuild): for angle in angles: assert _conn_in_molecule(angle, molecule) - angle_members = map( - lambda a: tuple(map(lambda s: s.name, a.connection_members)), - angles, + angle_members = ( + tuple(s.name for s in a.connection_members) for a in angles ) expected_members = { ("H", "C", "H"), @@ -90,9 +86,8 @@ def test_molecule_dihedrals(self, top_from_mbuild): for dihedral in dihedrals: assert _conn_in_molecule(dihedral, molecule) - dihedral_members = map( - lambda d: tuple(map(lambda s: s.name, d.connection_members)), - dihedrals, + dihedral_members = ( + tuple(s.name for s in d.connection_members) for d in dihedrals ) expected_members = {("H", "C", "C", "H")} assert all(a_member in expected_members for a_member in dihedral_members) @@ -104,12 +99,9 @@ def test_molecule_impropers(self, top_from_mbuild): for improper in impropers: assert _conn_in_molecule(improper, molecule) - improper_members = list( - map( - lambda i: tuple(map(lambda s: s.name, i.connection_members)), - impropers, - ) - ) + improper_members = [ + tuple(s.name for s in i.connection_members) for i in impropers + ] expected_members = { ("C", "C", "H", "H"), ("C", "H", "H", "C"), diff --git a/gmso/tests/test_atom_type.py b/gmso/tests/test_atom_type.py index 5d9eb391d..06fa3061f 100644 --- a/gmso/tests/test_atom_type.py +++ b/gmso/tests/test_atom_type.py @@ -86,7 +86,7 @@ def test_expression_consistency(self, charge): symbol_x, symbol_y, symbol_z = sympy.symbols("x y z") correct_expr = sympy.sympify("x+y*z") - assert new_type.expression.free_symbols == set([symbol_x, symbol_y, symbol_z]) + assert new_type.expression.free_symbols == {symbol_x, symbol_y, symbol_z} assert correct_expr == new_type.expression def test_equivalance(self, charge): @@ -328,8 +328,8 @@ def test_metadata_empty_tags(self, atomtype_metadata): assert list(atomtype_metadata.tag_names_iter) == [] def test_metadata_add_tags(self, atomtype_metadata): - atomtype_metadata.add_tag("tag1", dict([("tag_name_1", "value_1")])) - atomtype_metadata.add_tag("tag2", dict([("tag_name_2", "value_2")])) + atomtype_metadata.add_tag("tag1", {"tag_name_1": "value_1"}) + atomtype_metadata.add_tag("tag2", {"tag_name_2": "value_2"}) atomtype_metadata.add_tag("int_tag", 1) assert len(atomtype_metadata.tag_names) == 3 diff --git a/gmso/tests/test_bond.py b/gmso/tests/test_bond.py index f174e372a..87b38c51a 100644 --- a/gmso/tests/test_bond.py +++ b/gmso/tests/test_bond.py @@ -99,7 +99,7 @@ def test_bond_member_classes_types(self, typed_ethane): def test_bond_member_types(self, typed_ethane): bonds = typed_ethane.bonds - assert set(bonds[0].member_types) == set(["opls_135", "opls_140"]) + assert set(bonds[0].member_types) == {"opls_135", "opls_140"} def test_bond_member_classes_from_connection_members(self): atype1 = AtomType(atomclass="CT", name="t1") @@ -107,8 +107,8 @@ def test_bond_member_classes_from_connection_members(self): atype2 = AtomType(atomclass="CK", name="t2") bond = Bond(connection_members=[Atom(atom_type=atype1), Atom(atom_type=atype2)]) - assert set(bond.member_classes) == set(["CT", "CK"]) - assert set(bond.member_types) == set(["t1", "t2"]) + assert set(bond.member_classes) == {"CT", "CK"} + assert set(bond.member_types) == {"t1", "t2"} def test_bond_member_types_classes_from_bond_type(self): atom_type = AtomType() @@ -121,5 +121,5 @@ def test_bond_member_types_classes_from_bond_type(self): member_classes=["XE", "XE"], ) bond = Bond(connection_members=[atom1, atom2], bond_type=btype) - assert set(bond.member_classes) == set(["XE", "XE"]) - assert set(bond.member_types) == set(["at1", "at2"]) + assert set(bond.member_classes) == {"XE"} + assert set(bond.member_types) == {"at1", "at2"} diff --git a/gmso/tests/test_conversions.py b/gmso/tests/test_conversions.py index ad09861aa..730fc0519 100644 --- a/gmso/tests/test_conversions.py +++ b/gmso/tests/test_conversions.py @@ -28,7 +28,7 @@ def test_rescale_potentials(self, typed_ethane): template = template.set_expression( template.expression / 4 ) # use setter to not set in place - atype = list(typed_ethane.atom_types)[0] + atype = next(iter(typed_ethane.atom_types)) assert atype.expression == sympify("4*epsilon*((sigma/r)**12 - (sigma/r)**6)") typed_ethane.convert_potential_styles({"sites": template}) assert atype.expression == sympify("epsilon*((sigma/r)**12 - (sigma/r)**6)") @@ -68,8 +68,8 @@ def test_kcal_per_mol_to_kJ_per_mol(self): def test_input_not_unyt_units(self): with pytest.raises( - ValueError, - match=r"ERROR: The entered energy_input_unyt value is a , " + TypeError, + match=r"The entered energy_input_unyt value is a , " r"not a .", ): input_value = 2.0 @@ -80,8 +80,8 @@ def test_input_not_unyt_units(self): def test_kcal_per_mol_to_float_output(self): with pytest.raises( - ValueError, - match=r"ERROR: The entered energy_output_unyt_units_str value is a , " + TypeError, + match=r"The entered energy_output_unyt_units_str value is a , " r"not a .", ): input_value = 2 * u.kcal / u.mol * u.gram**2 @@ -131,7 +131,7 @@ def test_conversion_for_topology_dihedrals(self, typed_ethane): ) def test_conversion_for_topology_angles(self, typed_ethane): - expected_units_dim = dict(k="energy/angle**2", theta_eq="angle") + expected_units_dim = {"k": "energy/angle**2", "theta_eq": "angle"} base_units = u.UnitSystem("atomic", "Å", "mp", "fs", "nK", "rad") base_units["energy"] = "kcal/mol" potentials = _convert_potential_types( @@ -146,7 +146,7 @@ def test_conversion_for_topology_angles(self, typed_ethane): ) def test_conversion_for_topology_bonds(self, typed_ethane): - expected_units_dim = dict(k="energy/length**2", r_eq="length") + expected_units_dim = {"k": "energy/length**2", "r_eq": "length"} base_units = u.UnitSystem("atomic", "Å", "mp", "fs", "nK", "rad") base_units["energy"] = "kcal/mol" potentials = _convert_potential_types( @@ -161,7 +161,7 @@ def test_conversion_for_topology_bonds(self, typed_ethane): ) def test_conversion_for_topology_sites(self, typed_ethane): - expected_units_dim = dict(sigma="length", epsilon="energy") + expected_units_dim = {"sigma": "length", "epsilon": "energy"} base_units = u.UnitSystem("atomic", "Å", "mp", "fs", "nK", "rad") base_units["energy"] = "kcal/mol" potentials = _convert_potential_types( diff --git a/gmso/tests/test_convert_mbuild.py b/gmso/tests/test_convert_mbuild.py index 626b237b7..2953b95b2 100644 --- a/gmso/tests/test_convert_mbuild.py +++ b/gmso/tests/test_convert_mbuild.py @@ -145,7 +145,7 @@ def test_pass_box(self, mb_ethane): assert_allclose_units(top.box.lengths, [3, 3, 3] * u.nm, rtol=1e-5, atol=1e-8) def test_pass_failed_box(self, mb_ethane): - with pytest.raises(ValueError): + with pytest.raises(TypeError): from_mbuild(mb_ethane, box=[3, 3, 3], parse_label=True) def test_pass_box_bounding(self, mb_ethane): diff --git a/gmso/tests/test_convert_parmed.py b/gmso/tests/test_convert_parmed.py index 9a3d89b6a..31c74fc92 100644 --- a/gmso/tests/test_convert_parmed.py +++ b/gmso/tests/test_convert_parmed.py @@ -9,6 +9,7 @@ from unyt.testing import assert_allclose_units from gmso.core.views import PotentialFilters +from gmso.exceptions import GMSOError from gmso.external.convert_parmed import from_parmed, to_parmed from gmso.tests.base_test import BaseTest from gmso.utils.io import get_fn, has_parmed, import_ @@ -146,24 +147,31 @@ def test_to_parmed_full(self): assert struc_from_top.rb_torsions[i].type == struc.rb_torsions[i].type def test_to_parmed_incompatible_expression(self): + from copy import deepcopy + + import sympy + struc = pmd.load_file(get_fn("ethane.top"), xyz=get_fn("ethane.gro")) top = from_parmed(struc) + top0 = deepcopy(top) + top0.sites[0].atom_type.expression = sympy.sympify("sigma + epsilon/r") - with pytest.raises(Exception): - top.atom_types[0] = "sigma + epsilon" - to_parmed(top) + with pytest.raises(GMSOError): + to_parmed(top0) - with pytest.raises(Exception): - top.bond_types[0] = "k * r_eq" - to_parmed(top) + top0.bonds[0].bond_type.expression = "k * r_eq/r" + with pytest.raises(GMSOError): + to_parmed(top0) - with pytest.raises(Exception): - top.angle_types[0] = "k - theta_eq" - to_parmed(top) + top0 = deepcopy(top) + top0.angles[0].angle_type.expression = "k - theta_eq/theta" + with pytest.raises(GMSOError): + to_parmed(top0) - with pytest.raises(Exception): - top.dihedral_types[0] = "c0 - c1 + c2 - c3 + c4 - c5" - to_parmed(top) + top0 = deepcopy(top) + top0.dihedrals[0].dihedral_type.expression = "c0 - c1 + c2 - c3 + c4 - c5 + phi" + with pytest.raises(GMSOError): + to_parmed(top0) def test_to_parmed_loop( self, parmed_methylnitroaniline, parmed_chloroethanol, parmed_ethane @@ -334,13 +342,11 @@ def test_from_parmed_impropers(self): for gmso_improper, pmd_improper in zip( gmso_top.impropers, pmd_structure.dihedrals ): - pmd_member_names = list( + pmd_member_names = [ atom.name for atom in [getattr(pmd_improper, f"atom{j + 1}") for j in range(4)] - ) - gmso_member_names = list( - map(lambda a: a.name, gmso_improper.connection_members) - ) + ] + gmso_member_names = [a.name for a in gmso_improper.connection_members] assert pmd_member_names[0] == gmso_member_names[0] and set( pmd_member_names[1:] ) == set(gmso_member_names[1:]) @@ -369,7 +375,7 @@ def test_simple_pmd_dihedrals_no_types(self): for j in range(10): dih = pmd.Dihedral( *[struct.atoms[i] for i in range(j, j + 4)], - improper=True if j % 2 == 0 else False, + improper=j % 2 == 0, ) struct.dihedrals.append(dih) gmso_top = from_parmed(struct, refer_type=False) @@ -401,7 +407,7 @@ def test_simple_pmd_dihedrals_impropers(self): for j in range(10): dih = pmd.Dihedral( *[struct.atoms[i] for i in range(j, j + 4)], - improper=True if j % 2 == 0 else False, + improper=j % 2 == 0, ) struct.dihedrals.append(dih) dtype = pmd.DihedralType(random.random(), random.random(), random.random()) diff --git a/gmso/tests/test_equation_compare.py b/gmso/tests/test_equation_compare.py index 2689f45c8..f37c84a8d 100644 --- a/gmso/tests/test_equation_compare.py +++ b/gmso/tests/test_equation_compare.py @@ -112,16 +112,16 @@ def test_find_lj_mie_exp6_forms_and_scalars(self): ) [ - test_topology, - test_residues_applied_list, - test_electrostatics14Scale_dict, - test_nonBonded14Scale_dict, + _test_topology, + _test_residues_applied_list, + _test_electrostatics14Scale_dict, + _test_nonBonded14Scale_dict, test_atom_types_dict, - test_bond_types_dict, - test_angle_types_dict, - test_dihedral_types_dict, - test_improper_types_dict, - test_combining_rule_dict, + _test_bond_types_dict, + _test_angle_types_dict, + _test_dihedral_types_dict, + _test_improper_types_dict, + _test_combining_rule_dict, ] = specific_ff_to_residue( test_box, forcefield_selection={ @@ -176,16 +176,16 @@ def test_bad_eqn_for_find_lj_mie_exp6_forms_and_scalars(self): ) [ - test_topology, - test_residues_applied_list, - test_electrostatics14Scale_dict, - test_nonBonded14Scale_dict, + _test_topology, + _test_residues_applied_list, + _test_electrostatics14Scale_dict, + _test_nonBonded14Scale_dict, test_atom_types_dict, - test_bond_types_dict, - test_angle_types_dict, - test_dihedral_types_dict, - test_improper_types_dict, - test_combining_rule_dict, + _test_bond_types_dict, + _test_angle_types_dict, + _test_dihedral_types_dict, + _test_improper_types_dict, + _test_combining_rule_dict, ] = specific_ff_to_residue( test_box, forcefield_selection={ diff --git a/gmso/tests/test_expression.py b/gmso/tests/test_expression.py index 99bdb353d..a119b5983 100644 --- a/gmso/tests/test_expression.py +++ b/gmso/tests/test_expression.py @@ -17,8 +17,8 @@ def test_expression(self): ) assert expression.expression == sympy.sympify("a*x+b") - assert "a" in expression.parameters.keys() - assert "b" in expression.parameters.keys() + assert "a" in expression.parameters + assert "b" in expression.parameters assert expression.parameters["a"] == 1.0 * u.dimensionless assert expression.parameters["b"] == 2.0 * u.dimensionless diff --git a/gmso/tests/test_forcefield.py b/gmso/tests/test_forcefield.py index 4035d5b4a..e8e3e8967 100644 --- a/gmso/tests/test_forcefield.py +++ b/gmso/tests/test_forcefield.py @@ -523,7 +523,7 @@ def test_forcefield_get_parameters_virtual_type(self): params = ff.get_parameters("virtual_type", key=["Xe"]) assert allclose_units_mixed( - list([val.values() for val in params.values()][0]), + list(next(val.values() for val in params.values())), [ 12 * u.dimensionless, 6 * u.dimensionless, @@ -618,7 +618,7 @@ def test_write_xml(self, opls_ethane_foyer): reloaded_xml = ForceField("test_xml_writer.xml") def get_names(ff, param): - return [typed for typed in getattr(ff, param).keys()] + return [typed for typed in getattr(ff, param)] for param in [ "atom_types", diff --git a/gmso/tests/test_gro.py b/gmso/tests/test_gro.py index eec261131..5e62517e6 100644 --- a/gmso/tests/test_gro.py +++ b/gmso/tests/test_gro.py @@ -53,7 +53,7 @@ def test_write_gro_with_shift_coord(self): top.save("out.gro", shift_coord=True) read_top = Topology.load("out.gro") - assert np.all(list(map(lambda x: x.position >= 0, read_top.sites))) + assert np.all([x.position >= 0 for x in read_top.sites]) def test_write_gro_non_orthogonal(self): top = from_parmed(pmd.load_file(get_fn("ethane.gro"), structure=True)) @@ -114,7 +114,7 @@ def test_resid_for_mol(self): top.save("ethane_methane.gro") reread = Topology.load("ethane_methane.gro") - nums = set([site.molecule.number for site in reread.sites]) + nums = {site.molecule.number for site in reread.sites} assert nums == {0, 1, 2, 3} def test_no_mol_name(self): @@ -129,7 +129,7 @@ def test_no_mol_name(self): top.box = box top.save("temp_system.gro") reread = Topology.load("temp_system.gro") - nums = set([site.molecule.number for site in reread.sites]) + nums = {site.molecule.number for site in reread.sites} assert nums == {0} def test_res_naming(self): @@ -146,7 +146,7 @@ def test_res_naming(self): top.save("temp1.gro", overwrite=True) reread = Topology.load("temp1.gro") - nums = set([site.molecule.number for site in reread.sites]) + nums = {site.molecule.number for site in reread.sites} assert nums == {0, 1} top = Topology() @@ -168,7 +168,7 @@ def test_res_naming(self): top.save("temp2.gro", overwrite=True) reread = Topology.load("temp2.gro") - nums = set([site.molecule.number for site in reread.sites]) + nums = {site.molecule.number for site in reread.sites} assert nums == {0, 1, 2} top = Topology() @@ -190,7 +190,7 @@ def test_res_naming(self): top.save("temp3.gro", overwrite=True) reread = Topology.load("temp3.gro") - nums = set([site.molecule.number for site in reread.sites]) + nums = {site.molecule.number for site in reread.sites} assert nums == {0, 1, 2, 3} @pytest.mark.parametrize("fixture", ["benzene_ua_box", "benzene_aa_box"]) diff --git a/gmso/tests/test_gsd.py b/gmso/tests/test_gsd.py index 2e40194e0..f72ef76e4 100644 --- a/gmso/tests/test_gsd.py +++ b/gmso/tests/test_gsd.py @@ -23,10 +23,10 @@ def test_write_gsd_untyped(self): top.save("out.gsd") with gsd.hoomd.open("out.gsd") as traj: snap = traj[0] - assert all([i in snap.particles.types for i in ["C", "H"]]) - assert all([i in snap.bonds.types for i in ["C-C", "C-H"]]) - assert all([i in snap.angles.types for i in ["C-C-C", "C-C-H"]]) - assert all([i in snap.dihedrals.types for i in ["C-C-C-C", "C-C-C-H"]]) + assert all(i in snap.particles.types for i in ["C", "H"]) + assert all(i in snap.bonds.types for i in ["C-C", "C-H"]) + assert all(i in snap.angles.types for i in ["C-C-C", "C-C-H"]) + assert all(i in snap.dihedrals.types for i in ["C-C-C-C", "C-C-C-H"]) def test_write_gsd(self, hierarchical_compound): top = from_mbuild(hierarchical_compound) diff --git a/gmso/tests/test_hoomd.py b/gmso/tests/test_hoomd.py index 989d7875d..ce305d801 100644 --- a/gmso/tests/test_hoomd.py +++ b/gmso/tests/test_hoomd.py @@ -66,9 +66,9 @@ def test_rigid_bodies(self): rigid_ids = [site.molecule.number for site in top.sites] assert set(rigid_ids) == {0, 1} - snapshot, refs, rigid = to_gsd_snapshot(top) + snapshot, _, rigid = to_gsd_snapshot(top) snapshot.validate() - snapshot_no_rigid, refs = to_gsd_snapshot(top_no_rigid) + snapshot_no_rigid, _refs = to_gsd_snapshot(top_no_rigid) # Check that snapshot has rigid particles added assert "Ethane" in snapshot.particles.types assert "Ethane" not in snapshot_no_rigid.particles.types @@ -114,7 +114,7 @@ def test_multiple_rigid_bodies(self, gaff_forcefield): site.molecule.isrigid = True apply(top, gaff_forcefield, identify_connections=True) - snapshot, refs, rigid = to_gsd_snapshot(top) + snapshot, _refs, rigid = to_gsd_snapshot(top) snapshot.validate() for site in top.iter_sites_by_molecule("Ethane"): @@ -289,10 +289,10 @@ def test_diff_base_units(self): oplsaa = ForceField("oplsaa") top = apply(top, oplsaa, remove_untyped=True) - gmso_snapshot, snapshot_base_units = to_hoomd_snapshot( + _gmso_snapshot, _snapshot_base_units = to_hoomd_snapshot( top, base_units=base_units ) - gmso_forces, forces_base_units = to_hoomd_forcefield( + _gmso_forces, _forces_base_units = to_hoomd_forcefield( top, r_cut=1.4, base_units=base_units, @@ -313,8 +313,8 @@ def test_default_units(self): oplsaa = ForceField("oplsaa") top = apply(top, oplsaa, remove_untyped=True) - gmso_snapshot, snapshot_base_units = to_hoomd_snapshot(top) - gmso_forces, forces_base_units = to_hoomd_forcefield( + _gmso_snapshot, _snapshot_base_units = to_hoomd_snapshot(top) + _gmso_forces, _forces_base_units = to_hoomd_forcefield( top=top, r_cut=1.4, pppm_kwargs={"resolution": (64, 64, 64), "order": 7}, @@ -332,16 +332,15 @@ def test_ff_zero_parameter(self): "length": u.nm, "energy": u.kJ / u.mol, } - gmso_forces, forces_base_units = to_hoomd_forcefield( + gmso_forces, _forces_base_units = to_hoomd_forcefield( top, r_cut=1.4, base_units=base_units, pppm_kwargs={"resolution": (64, 64, 64), "order": 7}, ) - integrator_forces = list() - for cat in gmso_forces: - for force in gmso_forces[cat]: - integrator_forces.append(force) + integrator_forces = [ + item for sublist in gmso_forces.values() for item in sublist + ] for force in integrator_forces: if isinstance(force, hoomd.md.pair.LJ): keys = force.params.param_dict.keys() @@ -438,10 +437,10 @@ def test_special_pairs(self): ): assert force.nlist.exclusions == ["bond", "1-3", "1-4"] elif isinstance(force, hoomd.md.special_pair.Coulomb): - for key in force.params.keys(): + for key in force.params: assert force.params[key]["alpha"] == 0.25 elif isinstance(force, hoomd.md.special_pair.LJ): - for key in force.params.keys(): + for key in force.params: ljKey = tuple(key.split("-")) assert ( force.params[key]["epsilon"] @@ -496,7 +495,7 @@ def test_forces_connections_match(self): assert "CT-HC" in snapshot.bonds.types forces, _ = to_hoomd_forcefield(top=top, r_cut=1.4, base_units=base_units) - assert "CT-HC" in forces["bonds"][0].params.keys() + assert "CT-HC" in forces["bonds"][0].params def test_forces_wildcards(self): compound = mb.load("CCCC", smiles=True) @@ -543,7 +542,7 @@ def test_pass_nlist(self): top = apply(top, oplsaa, remove_untyped=True, identify_connections=True) nlist_nb, nlist_coul = get_cell_nlist(top, buffer=1) - gmso_forces, forces_base_units = to_hoomd_forcefield( + gmso_forces, _ = to_hoomd_forcefield( top, r_cut=1.4, base_units=base_units, @@ -576,7 +575,7 @@ def test_pass_nlist(self): top = com_box.to_gmso() top = apply(top, oplsaa, remove_untyped=True, identify_connections=True) nlist_nb, nlist_coul = get_cell_nlist(top, buffer=1) - gmso_forces, forces_base_units = to_hoomd_forcefield( + gmso_forces, _forces_base_units = to_hoomd_forcefield( top, r_cut=1.4, base_units=base_units, @@ -584,9 +583,7 @@ def test_pass_nlist(self): nlist=nlist_coul, ) for force in gmso_forces["nonbonded"]: - if isinstance(force, hoomd.md.pair.LJ) or isinstance( - force, hoomd.md.pair.Ewald - ): + if isinstance(force, (hoomd.md.pair.LJ, hoomd.md.pair.Ewald)): assert force.nlist == nlist_nb assert list(force.nlist.exclusions) == ["bond", "1-3", "1-4"] assert force.nlist.buffer == 1 @@ -745,9 +742,10 @@ def test_rigid_forces(self): assert "1-3" in force.nlist.exclusions assert "1-4" in force.nlist.exclusions for t in gmso_snapshot.particles.types: - assert force.params[("benzene", t)].to_base() == dict( - epsilon=0.0, sigma=0.0 - ) + assert force.params[("benzene", t)].to_base() == { + "epsilon": 0.0, + "sigma": 0.0, + } assert force.r_cut[("benzene", t)] == 1.4 assert rigid_info.body["benzene"]["constituent_types"].to_base() == [ @@ -783,9 +781,9 @@ def test_pairpotential_only_forces(self, dpd_pairpotential): assert len(dpd_force.params.keys()) == 3 expected_potentials = { - ("_A", "_A"): dict(A=40.0, gamma=8.0), - ("_A", "_B"): dict(A=20.0, gamma=1.0), - ("_B", "_B"): dict(A=20.0, gamma=1.0), + ("_A", "_A"): {"A": 40.0, "gamma": 8.0}, + ("_A", "_B"): {"A": 20.0, "gamma": 1.0}, + ("_B", "_B"): {"A": 20.0, "gamma": 1.0}, } for potential in dpd_pairpotential.pairpotential_types: pair = potential.member_types diff --git a/gmso/tests/test_itp.py b/gmso/tests/test_itp.py index dab836c7d..5ca896555 100644 --- a/gmso/tests/test_itp.py +++ b/gmso/tests/test_itp.py @@ -8,7 +8,7 @@ def test_itp_LIQ(self): top = read_itp(get_path("LIQ.itp")) assert top is not None assert len(top.atom_types) == 14 - assert len(set([atype.name for atype in top.atom_types])) == 4 + assert len({atype.name for atype in top.atom_types}) == 4 assert len(top.bond_types) == 13 # assert top.bonds[0].bond_type.parameters["k"] == 0.1529 empty_set = set() @@ -42,7 +42,7 @@ def test_itp_PNB(self): assert top is not None assert len(top.atom_types) == 1416 - assert len(set([atype.name for atype in top.atom_types])) == 9 + assert len({atype.name for atype in top.atom_types}) == 9 assert len(top.bond_types) == 1445 # assert top.bonds[0].bond_type.parameters["k"] == 0.1529 diff --git a/gmso/tests/test_lammps.py b/gmso/tests/test_lammps.py index b2da82a80..421b042c7 100644 --- a/gmso/tests/test_lammps.py +++ b/gmso/tests/test_lammps.py @@ -19,12 +19,14 @@ pfilter = PotentialFilters.UNIQUE_SORTED_NAMES -def compare_lammps_files(line1, line2, skip_linesList=[], offsets=None): +def compare_lammps_files(line1, line2, skip_linesList=None, offsets=None): """Check for line by line equality between lammps files, by any values. offsets = [file1: [(start, step)], file2: [(start, step)] """ + if skip_linesList is None: + skip_linesList = [] length1 = len(line1) length2 = len(line2) line_counter1 = 0 @@ -89,10 +91,12 @@ def test_water_lammps(self, typed_water_system, are_equivalent_topologies): read_top = Topology.load("water.lammps") assert are_equivalent_topologies(read_top, typed_water_system) - def test_read_lammps(self, filename=get_path("data.lammps")): + def test_read_lammps(self): + filename = get_path("data.lammps") gmso.Topology.load(filename) - def test_read_box(self, filename=get_path("data.lammps")): + def test_read_box(self): + filename = get_path("data.lammps") read = gmso.Topology.load(filename) assert read.box == Box(lengths=[1, 1, 1]) @@ -101,7 +105,8 @@ def test_read_n_sites(self, typed_ar_system): read = gmso.Topology.load("ar.lammps") assert read.n_sites == 100 - def test_read_mass(self, filename=get_path("data.lammps")): + def test_read_mass(self): + filename = get_path("data.lammps") read = gmso.Topology.load(filename) masses = [i.mass for i in read.atom_types] @@ -109,23 +114,26 @@ def test_read_mass(self, filename=get_path("data.lammps")): masses, u.unyt_array(1.0079, u.g / u.mol), rtol=1e-5, atol=1e-8 ) - def test_read_charge(self, filename=get_path("data.lammps")): + def test_read_charge(self): + filename = get_path("data.lammps") read = gmso.Topology.load(filename) charge = [i.charge for i in read.atom_types] assert_allclose_units(charge, u.unyt_array(0, u.C), rtol=1e-5, atol=1e-8) - def test_read_sigma(self, filename=get_path("data.lammps")): + def test_read_sigma(self): + filename = get_path("data.lammps") read = gmso.Topology.load(filename) - lj = [i.parameters for i in read.atom_types][0] + lj = next(i.parameters for i in read.atom_types) assert_allclose_units( lj["sigma"], u.unyt_array(3, u.angstrom), rtol=1e-5, atol=1e-8 ) - def test_read_epsilon(self, filename=get_path("data.lammps")): + def test_read_epsilon(self): + filename = get_path("data.lammps") read = gmso.Topology.load(filename) - lj = [i.parameters for i in read.atom_types][0] + lj = next(i.parameters for i in read.atom_types) assert_allclose_units( lj["epsilon"], @@ -333,7 +341,8 @@ def test_lammps_default_conversions( skip_linesList=[0], offsets=[[[0, 1], [17, 1]], []], ) - out_lammps = open("gmso.lammps", "r").readlines() + with open("gmso.lammps", "r") as f: + out_lammps = f.readlines() found_impropers = False for i, line in enumerate(out_lammps): if "Improper Coeffs" in line: @@ -417,16 +426,9 @@ def test_lammps_units(self, typed_ethane, unit_style): assert real_top.sites[0].charge.units == charge_unit if unit_style == "lj": largest_eps = max( - list( - map( - lambda x: x.parameters["epsilon"], - typed_ethane.atom_types, - ) - ) - ) - largest_sig = max( - list(map(lambda x: x.parameters["sigma"], typed_ethane.atom_types)) + [x.parameters["epsilon"] for x in typed_ethane.atom_types] ) + largest_sig = max([x.parameters["sigma"] for x in typed_ethane.atom_types]) assert_allclose_units( real_top.dihedrals[0].dihedral_type.parameters["k1"], ( @@ -519,14 +521,7 @@ def test_atom_style_printing(self, typed_ethane): assert styleLine[-1] == stylesDict[styleLine[0]] def test_lj_passed_units(self, typed_ethane): - largest_eps = max( - list( - map( - lambda x: x.parameters["epsilon"], - typed_ethane.atom_types, - ) - ) - ) + largest_eps = max([x.parameters["epsilon"] for x in typed_ethane.atom_types]) typed_ethane.save( "ethane.lammps", unit_style="lj", @@ -543,7 +538,7 @@ def test_lj_passed_units(self, typed_ethane): end = i break largest_eps_written = max( - [obj for obj in map(lambda x: float(x.split()[1]), lines[start + 2 : end])] + [obj for obj in (float(x.split()[1]) for x in lines[start + 2 : end])] ) assert largest_eps_written == 0.5 diff --git a/gmso/tests/test_mcf.py b/gmso/tests/test_mcf.py index 463606cc8..64347adf4 100644 --- a/gmso/tests/test_mcf.py +++ b/gmso/tests/test_mcf.py @@ -31,27 +31,20 @@ def parse_mcf(filename): mcf_data.append(line.strip().split()) for idx, line in enumerate(mcf_data): - if len(line) > 1: - if line[1] == "Atom_Info": - mcf_idx["Atom_Info"] = idx - if len(line) > 1: - if line[1] == "Bond_Info": - mcf_idx["Bond_Info"] = idx - if len(line) > 1: - if line[1] == "Angle_Info": - mcf_idx["Angle_Info"] = idx - if len(line) > 1: - if line[1] == "Dihedral_Info": - mcf_idx["Dihedral_Info"] = idx - if len(line) > 1: - if line[1] == "Fragment_Info": - mcf_idx["Fragment_Info"] = idx - if len(line) > 1: - if line[1] == "Fragment_Connectivity": - mcf_idx["Fragment_Connectivity"] = idx - if len(line) > 1: - if line[1] == "Intra_Scaling": - mcf_idx["Intra_Scaling"] = idx + if len(line) > 1 and line[1] == "Atom_Info": + mcf_idx["Atom_Info"] = idx + if len(line) > 1 and line[1] == "Bond_Info": + mcf_idx["Bond_Info"] = idx + if len(line) > 1 and line[1] == "Angle_Info": + mcf_idx["Angle_Info"] = idx + if len(line) > 1 and line[1] == "Dihedral_Info": + mcf_idx["Dihedral_Info"] = idx + if len(line) > 1 and line[1] == "Fragment_Info": + mcf_idx["Fragment_Info"] = idx + if len(line) > 1 and line[1] == "Fragment_Connectivity": + mcf_idx["Fragment_Connectivity"] = idx + if len(line) > 1 and line[1] == "Intra_Scaling": + mcf_idx["Intra_Scaling"] = idx return mcf_data, mcf_idx @@ -392,7 +385,7 @@ def test_in_cassandra(self, typed_ethane): seeds=[12356, 64321], ) - py, fraglib_setup, cassandra = detect_cassandra_binaries() + _py, _fraglib_setup, cassandra = detect_cassandra_binaries() # TODO: not sure why the cassandra MCF writer of mBuild # outputs a different intramolecular exclusions relative @@ -443,7 +436,7 @@ def test_in_cassandra(self, typed_ethane): f.writelines(lines) # Run the simulation with the GMSO MCF file - code, out, err = run_cassandra(cassandra, inp_file) + code, out, _err = run_cassandra(cassandra, inp_file) assert code == 0 assert "complete" in out @@ -479,8 +472,8 @@ def test_parmed_vs_gmso(self, parmed_ethane): top = from_parmed(parmed_ethane) write_mcf(top, "gmso-ethane.mcf") - mcf_data_pmd, mcf_idx_pmd = parse_mcf(get_path("parmed-ethane.mcf")) - mcf_data_gmso, mcf_idx_gmso = parse_mcf("gmso-ethane.mcf") + mcf_data_pmd, _mcf_idx_pmd = parse_mcf(get_path("parmed-ethane.mcf")) + mcf_data_gmso, _mcf_idx_gmso = parse_mcf("gmso-ethane.mcf") skip_lines = [3] float_pattern = r"[+-]?[0-9]*[.][0-9]*" for i, (line_pmd, line_gmso) in enumerate(zip(mcf_data_pmd, mcf_data_gmso)): @@ -523,4 +516,4 @@ def test_top_with_ring(self, typed_benzene_ua_system): assert mcf_data[mcf_idx["Fragment_Info"] + 1][0] == "1" frag_atoms = mcf_data[mcf_idx["Fragment_Info"] + 2][1:] - assert set(frag_atoms) == set([str(i) for i in range(1, 7)]) + assert set(frag_atoms) == {str(i) for i in range(1, 7)} diff --git a/gmso/tests/test_mol2.py b/gmso/tests/test_mol2.py index ce0970b93..52cc6ed38 100644 --- a/gmso/tests/test_mol2.py +++ b/gmso/tests/test_mol2.py @@ -22,9 +22,9 @@ def test_read_mol2(self, caplog): rtol=1e-5, atol=1e-8, ) - assert list(top.sites)[0].element.name == "carbon" + assert next(iter(top.sites)).element.name == "carbon" assert_allclose_units( - list(top.sites)[0].element.mass, + next(iter(top.sites)).element.mass, np.array(1.9944733e-26) * u.kg, rtol=1e-5, atol=1e-8, @@ -60,7 +60,7 @@ def test_read_mol2(self, caplog): with caplog.at_level(logging.INFO, logger="gmso"): top = Topology.load(get_fn("ethane.mol2"), verbose=True) assert match in caplog.text - assert list(top.sites)[0].charge is None + assert next(iter(top.sites)).charge is None def test_residue(self): top = Topology.load(get_fn("ethanol_aa.mol2")) diff --git a/gmso/tests/test_networkx.py b/gmso/tests/test_networkx.py index ab270e50b..061f53e5e 100644 --- a/gmso/tests/test_networkx.py +++ b/gmso/tests/test_networkx.py @@ -37,8 +37,8 @@ @pytest.mark.skipif(not has_matplotlib, reason="Matplotlib is not installed") class TestNetworkx(BaseTest): def test_highlight_networkx_edges(self, typed_ethane): - list(typed_ethane.angles)[0].angle_type = None - list(typed_ethane.dihedrals)[0].dihedral_type = None + next(iter(typed_ethane.angles)).angle_type = None + next(iter(typed_ethane.dihedrals)).dihedral_type = None graph = to_networkx(typed_ethane) list_edges = list(graph.edges)[0:3] test_edge_weights, test_edge_colors = highlight_networkx_edges( @@ -78,33 +78,33 @@ def test_select_params_on_networkx(self, typed_ethane, capsys): def test_select_params_on_networkx_output(self, typed_ethane, capsys): graph = to_networkx(typed_ethane) select_params_on_networkx(graph, [None, None, None]) - captured, err = capsys.readouterr() + captured, _ = capsys.readouterr() assert captured.startswith("All angles") select_params_on_networkx(graph, [None, None, None, None]) - captured, err = capsys.readouterr() + captured, _ = capsys.readouterr() assert captured.startswith("All dihedrals") - for node, angles in graph.nodes(data="angles"): + for _, angles in graph.nodes(data="angles"): if angles[0]: angles[0].angle_type = None select_params_on_networkx(graph, [None, None, None]) - captured, err = capsys.readouterr() + captured, _ = capsys.readouterr() assert captured.startswith("Since no sites are input, angles") - for node, dihedrals in graph.nodes(data="dihedrals"): + for _, dihedrals in graph.nodes(data="dihedrals"): if dihedrals[0]: dihedrals[0].dihedral_type = None select_params_on_networkx(graph, [None, None, None, None]) - captured, err = capsys.readouterr() + captured, _ = capsys.readouterr() assert captured.startswith("Since no sites are input, dihedrals") nx.set_node_attributes(graph, None, name="angles") select_params_on_networkx(graph, [None, None, None]) - captured, err = capsys.readouterr() + captured, _ = capsys.readouterr() assert captured.startswith("No angle") nx.set_node_attributes(graph, None, name="dihedrals") select_params_on_networkx(graph, [None, None, None, None]) - captured, err = capsys.readouterr() + captured, _ = capsys.readouterr() assert captured.startswith("No dihedral") select_params_on_networkx(graph, [None, None]) - captured, err = capsys.readouterr() + captured, _ = capsys.readouterr() assert captured.startswith("invalid") def test__get_formatted_atom_types_names_for(self, typed_ethane): @@ -139,18 +139,18 @@ def test_select_dihedrals_from_sites(self, typed_ethane, capsys): graph = to_networkx(typed_ethane) select_dihedrals_from_sites(graph, typed_ethane) select_dihedrals_from_sites(graph, "C", "C", "H", "H") - captured, err = capsys.readouterr() + _captured, err = capsys.readouterr() assert isinstance(err, str) def test_select_dihedrals_without_sites(self, typed_ethane, capsys): graph = to_networkx(typed_ethane) select_dihedrals_from_sites(graph, typed_ethane) - captured, err = capsys.readouterr() + _captured, err = capsys.readouterr() assert isinstance(err, str) def test_plot_networkx_nodes(self, typed_ethane): graph = to_networkx(typed_ethane) - fig, ax = plt.subplots(1, 1) + _fig, ax = plt.subplots(1, 1) plot_networkx_nodes(graph, ax, edge_weights={1: 5}, edge_colors={1: "r"}) def test_plot_networkx_params(self, typed_ethane): @@ -171,7 +171,7 @@ def test_select_edges_on_networkx(self, typed_ethane, capsys): graph = to_networkx(typed_ethane) edges = select_params_on_networkx(graph, ["C", "C", "H"]) select_edges_on_networkx(graph, typed_ethane, edges[0][1]) - captured, err = capsys.readouterr() + _captured, err = capsys.readouterr() assert isinstance(err, str) def test_report_parameter_expression(self, typed_ethane, capsys): @@ -181,7 +181,7 @@ def test_report_parameter_expression(self, typed_ethane, capsys): report_parameter_expression( typed_ethane, list(typed_ethane.angles[0].connection_members) ) - captured, err = capsys.readouterr() + _captured, err = capsys.readouterr() assert isinstance(err, str) def test_get_edges(self, typed_ethane): @@ -192,7 +192,7 @@ def test_get_edges(self, typed_ethane): def test_report_bond_parameters(self, typed_ethane, capsys): report_bond_parameters(typed_ethane, [typed_ethane.bonds[0].connection_members]) - captured, err = capsys.readouterr() + _captured, err = capsys.readouterr() assert isinstance(err, str) def test_return_labels_for_nodes(self, typed_ethane): @@ -206,13 +206,13 @@ def test_return_labels_for_nodes(self, typed_ethane): == 8 ) assert ( - list(return_labels_for_nodes(graph.nodes, ["atom_type.error"]).values())[0][ - -8: - ] + next( + iter(return_labels_for_nodes(graph.nodes, ["atom_type.error"]).values()) + )[-8:] == "NoneType" ) assert ( - list(return_labels_for_nodes(graph.nodes, ["error"]).values())[0][-8:] + next(iter(return_labels_for_nodes(graph.nodes, ["error"]).values()))[-8:] == "NoneType" ) @@ -220,7 +220,7 @@ def test_select_angles_from_sites(self, typed_ethane, capsys): graph = to_networkx(typed_ethane) select_angles_from_sites(graph, typed_ethane, Atom1="C", Atom2="H", Atom3="C") select_angles_from_sites(graph, typed_ethane, Atom1="O", Atom2="H", Atom3="C") - captured, err = capsys.readouterr() + _captured, err = capsys.readouterr() assert isinstance(err, str) def test_call_interactive_sites(self, typed_ethane): diff --git a/gmso/tests/test_potential.py b/gmso/tests/test_potential.py index 4ebb59314..a615b8515 100644 --- a/gmso/tests/test_potential.py +++ b/gmso/tests/test_potential.py @@ -69,9 +69,7 @@ def test_expression_consistency(self): symbol_x, symbol_y, symbol_z = sympy.symbols("x y z") correct_expr = sympy.sympify("x+y*z") - assert new_potential.expression.free_symbols == set( - [symbol_x, symbol_y, symbol_z] - ) + assert new_potential.expression.free_symbols == {symbol_x, symbol_y, symbol_z} assert correct_expr == new_potential.expression def test_equivalance(self): @@ -292,7 +290,7 @@ def test_sorting(self, parmed_benzene): "improper_types", ] for connection_type in labelsList: - conn = list(getattr(top, connection_type)())[0] + conn = next(iter(getattr(top, connection_type)())) assert sort_by_classes(conn) == sort_by_types(conn) def test_numpy_potential(self): diff --git a/gmso/tests/test_reference_xmls.py b/gmso/tests/test_reference_xmls.py index d98957d1f..59107ca56 100644 --- a/gmso/tests/test_reference_xmls.py +++ b/gmso/tests/test_reference_xmls.py @@ -302,7 +302,7 @@ def test_noble_mie_xml(self): assert len(ff.angle_types) == 0 assert len(ff.dihedral_types) == 0 - for name, atom_type in ff.atom_types.items(): + for atom_type in ff.atom_types.values(): assert sympy.simplify(atom_type.expression - ref_expr) == 0 assert_allclose_units( @@ -547,11 +547,9 @@ def test_ethylene_forcefield(self): ) def test_error_duplicated_types(self): - # Temporarily opt out, pending new forcefield-utilities release - # with pytest.raises(ValueError) as e: - with pytest.raises(Exception): + with pytest.raises(ValueError) as e: ForceField(get_path("ff-nonunique-dihedral.xml")) - # assert ( - # e - # == "Duplicate identifier found for DihedralTypes: ('CT', 'CT', 'CT', 'HC')" - # ) + assert ( + e + == "Duplicate identifier found for DihedralTypes: ('CT', 'CT', 'CT', 'HC')" + ) diff --git a/gmso/tests/test_specific_ff_to_residue.py b/gmso/tests/test_specific_ff_to_residue.py index 42cfdbb5f..25f12cfc9 100644 --- a/gmso/tests/test_specific_ff_to_residue.py +++ b/gmso/tests/test_specific_ff_to_residue.py @@ -233,16 +233,16 @@ def test_specific_ff_to_residue_ff_selection_run(self, ethane_gomc): ) [ - test_topology, + _test_topology, test_residues_applied_list, test_electrostatics14Scale_dict, test_nonBonded14Scale_dict, - test_atom_types_dict, - test_bond_types_dict, - test_angle_types_dict, - test_dihedral_types_dict, - test_improper_types_dict, - test_combining_rule_dict, + _test_atom_types_dict, + _test_bond_types_dict, + _test_angle_types_dict, + _test_dihedral_types_dict, + _test_improper_types_dict, + _test_combining_rule_dict, ] = specific_ff_to_residue( test_box_ethane_gomc, forcefield_selection={ @@ -328,12 +328,12 @@ def test_charmm_a_few_mbuild_layers(self, ethane_gomc, ethanol_gomc): test_residues_applied_list, test_electrostatics14Scale_dict, test_nonBonded14Scale_dict, - test_atom_types_dict, - test_bond_types_dict, - test_angle_types_dict, - test_dihedral_types_dict, - test_improper_types_dict, - test_combining_rule_dict, + _test_atom_types_dict, + _test_bond_types_dict, + _test_angle_types_dict, + _test_dihedral_types_dict, + _test_improper_types_dict, + _test_combining_rule_dict, ] = specific_ff_to_residue( box_reservior_3, forcefield_selection={ @@ -380,6 +380,9 @@ def test_charmm_all_residues_not_in_dict_boxes_for_simulation_1( def test_charmm_all_residues_not_in_dict_boxes_for_simulation_2( self, ethane_gomc, ethanol_gomc ): + box_reservior_0 = mb.fill_box( + compound=[ethane_gomc], box=[1, 1, 1], n_compounds=[1] + ) with pytest.warns( UserWarning, match=f"The {'ETO'} residues were not used from the forcefield_selection string or dictionary. " @@ -392,9 +395,6 @@ def test_charmm_all_residues_not_in_dict_boxes_for_simulation_2( f"NOTE: This warning will appear if you are using the CHARMM pdb and psf writers " f"2 boxes, and the boxes do not contain all the residues in each box.", ): - box_reservior_0 = mb.fill_box( - compound=[ethane_gomc], box=[1, 1, 1], n_compounds=[1] - ) specific_ff_to_residue( box_reservior_0, forcefield_selection={ @@ -511,10 +511,10 @@ def test_specific_ff_params_benzene_aa_grouped(self): test_electrostatics14Scale_dict, test_nonBonded14Scale_dict, test_atom_types_dict, - test_bond_types_dict, - test_angle_types_dict, - test_dihedral_types_dict, - test_improper_types_dict, + _test_bond_types_dict, + _test_angle_types_dict, + _test_dihedral_types_dict, + _test_improper_types_dict, test_combining_rule_dict, ] = specific_ff_to_residue( methane_box, diff --git a/gmso/tests/test_top.py b/gmso/tests/test_top.py index 30c927ee0..fb2f39cac 100644 --- a/gmso/tests/test_top.py +++ b/gmso/tests/test_top.py @@ -77,13 +77,13 @@ def test_modified_potentials(self, ar_system): top.update_topology() - list(top.atom_types)[0].set_expression("sigma + epsilon*r") + next(iter(top.atom_types)).set_expression("sigma + epsilon*r") with pytest.raises(EngineIncompatibilityError): top.save("out.top") alternate_lj = "4*epsilon*sigma**12/r**12 - 4*epsilon*sigma**6/r**6" - list(top.atom_types)[0].set_expression(alternate_lj) + next(iter(top.atom_types)).set_expression(alternate_lj) top.save("ar.top") @@ -217,8 +217,8 @@ def test_benzene_restraints(self, typed_benzene_ua_system): assert len(f_cont) == len(ref_cont) - ref_sections = dict() - sections = dict() + ref_sections = {} + sections = {} current_section = None for line, ref in zip(f_cont[1:], ref_cont[1:]): if line.startswith("["): @@ -237,8 +237,8 @@ def test_benzene_restraints(self, typed_benzene_ua_system): if "dihedral" in section: # Need to deal with these separately due to member's order issue # Each dict will have the keys be members and values be their parameters - members = dict() - ref_members = dict() + members = {} + ref_members = {} for line, ref in zip(sections[section], ref_sections[ref_section]): line = line.split() ref = ref.split() @@ -248,8 +248,8 @@ def test_benzene_restraints(self, typed_benzene_ua_system): ref_members["-".join(reversed(ref[:4]))] = ref[4:] assert members == ref_members - for member in members: - assert members[member] == ref_members[member] + for member, val in members.items(): + assert val == ref_members[member] else: assert sections[section] == ref_sections[ref_section] diff --git a/gmso/tests/test_topology.py b/gmso/tests/test_topology.py index 16d063883..01e6da0f1 100644 --- a/gmso/tests/test_topology.py +++ b/gmso/tests/test_topology.py @@ -121,8 +121,8 @@ def test_positions_dtype(self): atom1 = Atom(name="atom1", position=[0.0, 0.0, 0.0]) top.add_site(atom1) - assert set([type(site.position) for site in top.sites]) == {u.unyt_array} - assert set([site.position.units for site in top.sites]) == {u.nm} + assert {type(site.position) for site in top.sites} == {u.unyt_array} + assert {site.position.units for site in top.sites} == {u.nm} assert top.positions.dtype == float assert top.positions.units == u.nm @@ -613,7 +613,7 @@ def test_topology_get_index_dihedral_type(self, typed_chloroethanol): ) def test_topology_get_bonds_for(self, typed_methylnitroaniline): - site = list(typed_methylnitroaniline.sites)[0] + site = next(iter(typed_methylnitroaniline.sites)) converted_bonds_list = typed_methylnitroaniline._get_bonds_for(site) top_bonds_containing_site = [] for bond in typed_methylnitroaniline.bonds: @@ -623,7 +623,7 @@ def test_topology_get_bonds_for(self, typed_methylnitroaniline): assert len(top_bonds_containing_site) == len(converted_bonds_list) def test_topology_get_angles_for(self, typed_methylnitroaniline): - site = list(typed_methylnitroaniline.sites)[0] + site = next(iter(typed_methylnitroaniline.sites)) converted_angles_list = typed_methylnitroaniline._get_angles_for(site) top_angles_containing_site = [] for angle in typed_methylnitroaniline.angles: @@ -633,7 +633,7 @@ def test_topology_get_angles_for(self, typed_methylnitroaniline): assert len(top_angles_containing_site) == len(converted_angles_list) def test_topology_get_dihedrals_for(self, typed_methylnitroaniline): - site = list(typed_methylnitroaniline.sites)[0] + site = next(iter(typed_methylnitroaniline.sites)) converted_dihedrals_list = typed_methylnitroaniline._get_dihedrals_for(site) top_dihedrals_containing_site = [] for dihedral in typed_methylnitroaniline.dihedrals: diff --git a/gmso/tests/test_units.py b/gmso/tests/test_units.py index f021a5d0a..8d7f1c756 100644 --- a/gmso/tests/test_units.py +++ b/gmso/tests/test_units.py @@ -96,24 +96,22 @@ def test_dimensions_thermal(self, real_usys): def test_get_dimensions(self): usys = LAMMPS_UnitSystems("electron") - parametersList = list( - map( - lambda x: 1 * u.Unit(x, registry=usys.reg), - [ - "nm", - "kJ", - "kJ/mol", - "K", - "degree/angstrom", - "elementary_charge/mm", - "dimensionless", - "kg*m**2/s**2", - "coulomb", - "kcal/nm**2", - "K/nm", - ], - ) - ) + parametersList = [ + 1 * u.Unit(x, registry=usys.reg) + for x in [ + "nm", + "kJ", + "kJ/mol", + "K", + "degree/angstrom", + "elementary_charge/mm", + "dimensionless", + "kg*m**2/s**2", + "coulomb", + "kcal/nm**2", + "K/nm", + ] + ] output_dimensionsList = [ "length", diff --git a/gmso/tests/test_xml_handling.py b/gmso/tests/test_xml_handling.py index 1e5e92cca..c64bdbfc8 100644 --- a/gmso/tests/test_xml_handling.py +++ b/gmso/tests/test_xml_handling.py @@ -56,7 +56,7 @@ def test_write_xml(self, opls_ethane_foyer): reloaded_xml = ForceField("test_xml_writer.xml") def get_names(ff, param): - return [typed for typed in getattr(ff, param).keys()] + return [typed for typed in getattr(ff, param)] for param in [ "atom_types", diff --git a/gmso/tests/test_xyz.py b/gmso/tests/test_xyz.py index d73611b75..67f66ceb8 100644 --- a/gmso/tests/test_xyz.py +++ b/gmso/tests/test_xyz.py @@ -12,15 +12,15 @@ def test_read_xyz(self): top = Topology.load(get_fn("ethane.xyz")) assert top.n_sites == 8 assert top.n_connections == 0 - assert set([type(site.position) for site in top.sites]) == {u.unyt_array} - assert set([site.position.units for site in top.sites]) == {u.nm} + assert {type(site.position) for site in top.sites} == {u.unyt_array} + assert {site.position.units for site in top.sites} == {u.nm} top = Topology.load(get_fn("cu_block.xyz")) assert top.n_sites == 108 assert top.n_connections == 0 - assert set([type(site.position) for site in top.sites]) == {u.unyt_array} - assert set([site.position.units for site in top.sites]) == {u.nm} + assert {type(site.position) for site in top.sites} == {u.unyt_array} + assert {site.position.units for site in top.sites} == {u.nm} def test_wrong_n_atoms(self): with pytest.raises(ValueError): diff --git a/gmso/utils/compatibility.py b/gmso/utils/compatibility.py index 343787734..19697b350 100644 --- a/gmso/utils/compatibility.py +++ b/gmso/utils/compatibility.py @@ -54,7 +54,7 @@ def check_compatibility( scheme for the potentials in the topology. """ - potential_forms_dict = dict() + potential_forms_dict = {} for atom_type in topology.atom_types( filter_by=site_pfilter ): # skip empty atomtypes diff --git a/gmso/utils/connectivity.py b/gmso/utils/connectivity.py index c24c7fd2b..fa443ea89 100644 --- a/gmso/utils/connectivity.py +++ b/gmso/utils/connectivity.py @@ -111,7 +111,7 @@ def _add_connections(top, matches, conn_type): """Add connections to the topology.""" for sorted_conn in matches: cmembers = [top.sites[idx] for idx in sorted_conn] - bonds = list() + bonds = [] for i, j in CONNS[conn_type].connectivity: bond = (cmembers[i], cmembers[j]) key = frozenset([bond, tuple(reversed(bond))]) @@ -272,14 +272,14 @@ def generate_pairs_lists( graph = to_networkx(top, parse_angles=False, parse_dihedrals=False) - pairs_dict = dict() + pairs_dict = {} if refer_from_scaling_factor: for i in range(3): if nb_scalings[i] or coulombic_scalings[i]: - pairs_dict[f"pairs1{i + 2}"] = list() + pairs_dict[f"pairs1{i + 2}"] = [] else: for i in range(3): - pairs_dict = {f"pairs1{i + 2}": list() for i in range(3)} + pairs_dict = {f"pairs1{i + 2}": [] for i in range(3)} if molecule is None: bonds, angles, dihedrals = top.bonds, top.angles, top.dihedrals @@ -384,7 +384,7 @@ def _get_graph_isomorphism_matches(g1, g2, match_by="identifier"): graph_matcher = nx.algorithms.isomorphism.GraphMatcher( g1, g2, node_match=node_match ) - acceptedMaps = dict() + acceptedMaps = {} for mapping in graph_matcher.subgraph_isomorphisms_iter(): possibleMap = {g1id: g2id for g1id, g2id in mapping.items()} acceptedMaps[frozenset(possibleMap.keys())] = possibleMap diff --git a/gmso/utils/conversions.py b/gmso/utils/conversions.py index 3de3f1e0d..f14c7f582 100644 --- a/gmso/utils/conversions.py +++ b/gmso/utils/conversions.py @@ -31,7 +31,7 @@ def _constant_multiplier(pot1, pot2): if eq_term.is_symbol: key = str(eq_term) return {key: pot1.parameters[key] * float(constant)} - except Exception: + except (ValueError, TypeError): # return nothing if the sympy conversion errors out pass return None @@ -117,7 +117,7 @@ def _conversion_from_template_obj( current_expression.parameters.update(modified_connection_parametersDict) -def convert_topology_expressions(top, expressionMap={}): +def convert_topology_expressions(top, expressionMap=None): """Convert from one parameter form to another. Parameters @@ -144,6 +144,8 @@ def convert_topology_expressions(top, expressionMap={}): """ # Apply from predefined conversions or easy sympy conversions # handler for various keys passed to expressionMap for conversion + if expressionMap is None: + expressionMap = {} for connStr, conv in expressionMap.items(): possible_connections = ["bond", "angle", "dihedral", "improper"] if connStr.lower() in [ @@ -170,7 +172,7 @@ def convert_topology_expressions(top, expressionMap={}): elif isinstance(conv, PotentialTemplate): _conversion_from_template_obj(top, connStr, conn_typeStr, conv) else: - connType = list(getattr(top, conn_typeStr))[0] + connType = next(iter(getattr(top, conn_typeStr))) errormsg = f""" Failed to convert {top} for {connStr} components, with conversion of {connType.name}: Attempted to convert {connType} with style {conv}, which is a {type(conv)}. @@ -198,14 +200,13 @@ def convert_opls_to_ryckaert(opls_connection_type): if ( opls_connection_type.independent_variables == opls_torsion_potential.independent_variables + ) and ( + sympy.simplify( + opls_connection_type.expression - opls_torsion_potential.expression + ) + == 0 ): - if ( - sympy.simplify( - opls_connection_type.expression - opls_torsion_potential.expression - ) - == 0 - ): - valid_connection_type = True + valid_connection_type = True if not valid_connection_type: raise GMSOError( "Cannot use convert_opls_to_ryckaert " @@ -293,15 +294,14 @@ def convert_ryckaert_to_fourier(ryckaert_connection_type): if ( ryckaert_connection_type.independent_variables == ryckaert_bellemans_torsion_potential.independent_variables + ) and ( + sympy.simplify( + ryckaert_connection_type.expression + - ryckaert_bellemans_torsion_potential.expression + ) + == 0 ): - if ( - sympy.simplify( - ryckaert_connection_type.expression - - ryckaert_bellemans_torsion_potential.expression - ) - == 0 - ): - valid_connection_type = True + valid_connection_type = True if not valid_connection_type: raise GMSOError( "Cannot use convert_ryckaert_to_fourier " @@ -383,14 +383,14 @@ def convert_kelvin_to_energy_units( f"ERROR: The entered energy_input_unyt value is a {type(energy_input_unyt)}, " f"not a {type(u.Kelvin)}." ) - raise ValueError(print_error_message) + raise TypeError(print_error_message) if not isinstance(energy_output_unyt_units_str, str): print_error_message = ( - f"ERROR: The entered energy_output_unyt_units_str value is a {type(energy_output_unyt_units_str)}, " + f"The entered energy_output_unyt_units_str value is a {type(energy_output_unyt_units_str)}, " f"not a {str}." ) - raise ValueError(print_error_message) + raise TypeError(print_error_message) # check for K energy units and convert them to normal energy units; # otherwise, just pass thru the original unyt units @@ -428,9 +428,9 @@ def convert_kelvin_to_energy_units( def convert_params_units(potentials, expected_units_dim, base_units, ref_values): """Convert parameters' units in the potential to that specified in the base_units.""" - converted_potentials = list() + converted_potentials = [] for potential in potentials: - converted_params = dict() + converted_params = {} for parameter in potential.parameters: unit_dim = expected_units_dim[parameter] ind_units = re.sub("[^a-zA-Z]+", " ", unit_dim).split() diff --git a/gmso/utils/equation_compare.py b/gmso/utils/equation_compare.py index b96edd0f7..76ff6f630 100644 --- a/gmso/utils/equation_compare.py +++ b/gmso/utils/equation_compare.py @@ -31,9 +31,9 @@ def evaluate_nonbonded_lj_format_with_scaler(new_lj_form, base_lj_form): try: ( eqn_ratio, - epsilon, + _epsilon, sigma, - r, + _r, Rmin, two, ) = sympy.symbols("eqn_ratio epsilon sigma r Rmin two") @@ -46,7 +46,7 @@ def evaluate_nonbonded_lj_format_with_scaler(new_lj_form, base_lj_form): [eqn_ratio, Rmin, two], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "LJ" except (UnsolvableFactorError, TypeError): @@ -83,10 +83,10 @@ def evaluate_nonbonded_mie_format_with_scaler(new_mie_form, base_mie_form): try: ( eqn_ratio, - epsilon, - sigma, - r, - n, + _epsilon, + _sigma, + _r, + _n, ) = sympy.symbols("eqn_ratio epsilon sigma r n") values = sympy.nonlinsolve( [ @@ -95,7 +95,7 @@ def evaluate_nonbonded_mie_format_with_scaler(new_mie_form, base_mie_form): [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "Mie" except (UnsolvableFactorError, TypeError): @@ -132,11 +132,11 @@ def evaluate_nonbonded_exp6_format_with_scaler(new_exp6_form, base_exp6_form): try: ( eqn_ratio, - epsilon, - sigma, - r, - Rmin, - alpha, + _epsilon, + _sigma, + _r, + _Rmin, + _alpha, ) = sympy.symbols("eqn_ratio epsilon sigma r Rmin alpha") values = sympy.nonlinsolve( [ @@ -146,7 +146,7 @@ def evaluate_nonbonded_exp6_format_with_scaler(new_exp6_form, base_exp6_form): [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "Exp6" except (UnsolvableFactorError, TypeError): @@ -213,7 +213,7 @@ def get_atom_type_expressions_and_scalars(atom_types_dict): } atomtypes_data_expression_data_dict = {} - for res_i in atom_types_dict.keys(): + for res_i in atom_types_dict: for atom_type_m in atom_types_dict[res_i]["atom_types"]: modified_atom_type_iter = f"{res_i}_{atom_type_m.name}" atomtypes_data_dict_iter = { @@ -345,7 +345,7 @@ def evaluate_harmonic_bond_format_with_scaler(new_bond_form, base_bond_form): None, if the new_bond_form variable is not a harmonic bond. """ try: - eqn_ratio, k, r, r_eq = sympy.symbols("eqn_ratio k r r_eq") + eqn_ratio, _k, _r, _r_eq = sympy.symbols("eqn_ratio k r r_eq") values = sympy.nonlinsolve( [ eqn_ratio @@ -354,7 +354,7 @@ def evaluate_harmonic_bond_format_with_scaler(new_bond_form, base_bond_form): [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "HarmonicBondPotential" except (UnsolvableFactorError, TypeError): @@ -389,7 +389,7 @@ def evaluate_harmonic_angle_format_with_scaler(new_angle_form, base_angle_form): None, if the new_angle_form variable is not a harmonic. """ try: - eqn_ratio, k, theta, theta_eq = sympy.symbols("eqn_ratio k theta theta_eq") + eqn_ratio, _k, _theta, _theta_eq = sympy.symbols("eqn_ratio k theta theta_eq") values = sympy.nonlinsolve( [ eqn_ratio @@ -398,7 +398,7 @@ def evaluate_harmonic_angle_format_with_scaler(new_angle_form, base_angle_form): [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "HarmonicAnglePotential" except (UnsolvableFactorError, TypeError): @@ -433,7 +433,7 @@ def evaluate_harmonic_torsion_format_with_scaler(new_torsion_form, base_torsion_ None, if the new_torsion_form variable is not a harmonic torsion. """ try: - eqn_ratio, k, phi, phi_eq = sympy.symbols("eqn_ratio k phi phi_eq") + eqn_ratio, _k, _phi, _phi_eq = sympy.symbols("eqn_ratio k phi phi_eq") values = sympy.nonlinsolve( [ eqn_ratio @@ -442,7 +442,7 @@ def evaluate_harmonic_torsion_format_with_scaler(new_torsion_form, base_torsion_ [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "HarmonicTorsionPotential" except (UnsolvableFactorError, TypeError): @@ -477,7 +477,7 @@ def evaluate_OPLS_torsion_format_with_scaler(new_torsion_form, base_torsion_form None, if the new_torsion_form variable is not an OPLS torsion. """ try: - eqn_ratio, k0, k1, k2, k3, k4, phi = sympy.symbols( + eqn_ratio, _k0, _k1, _k2, _k3, _k4, _phi = sympy.symbols( "eqn_ratio k0 k1 k2 k3 k4 phi" ) values = sympy.nonlinsolve( @@ -489,7 +489,7 @@ def evaluate_OPLS_torsion_format_with_scaler(new_torsion_form, base_torsion_form [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "OPLSTorsionPotential" except (UnsolvableFactorError, TypeError): @@ -524,7 +524,7 @@ def evaluate_periodic_torsion_format_with_scaler(new_torsion_form, base_torsion_ None, if the new_torsion_form variable is not a periodic torsion. """ try: - eqn_ratio, k, n, phi, phi_eq = sympy.symbols("eqn_ratio k n phi phi_eq") + eqn_ratio, _k, _n, _phi, _phi_eq = sympy.symbols("eqn_ratio k n phi phi_eq") values = sympy.nonlinsolve( [ eqn_ratio @@ -533,7 +533,7 @@ def evaluate_periodic_torsion_format_with_scaler(new_torsion_form, base_torsion_ [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "PeriodicTorsionPotential" except (UnsolvableFactorError, TypeError): @@ -568,7 +568,7 @@ def evaluate_RB_torsion_format_with_scaler(new_torsion_form, base_torsion_form): None, if the new_torsion_form variable is not an RB torsion. """ try: - eqn_ratio, c0, c1, c2, c3, c4, c5, psi = sympy.symbols( + eqn_ratio, _c0, _c1, _c2, _c3, _c4, _c5, _psi = sympy.symbols( "eqn_ratio c0 c1 c2 c3 c4 c5 psi" ) values = sympy.nonlinsolve( @@ -579,7 +579,7 @@ def evaluate_RB_torsion_format_with_scaler(new_torsion_form, base_torsion_form): [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "RyckaertBellemansTorsionPotential" except (UnsolvableFactorError, TypeError): @@ -616,7 +616,7 @@ def evaluate_harmonic_improper_format_with_scaler( None, if the new_improper_form variable is not a harmonic improper. """ try: - eqn_ratio, k, phi, phi_eq = sympy.symbols("eqn_ratio k phi phi_eq") + eqn_ratio, _k, _phi, _phi_eq = sympy.symbols("eqn_ratio k phi phi_eq") values = sympy.nonlinsolve( [ eqn_ratio @@ -625,7 +625,7 @@ def evaluate_harmonic_improper_format_with_scaler( [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "HarmonicImproperPotential" except (UnsolvableFactorError, TypeError): @@ -662,7 +662,7 @@ def evaluate_periodic_improper_format_with_scaler( None, if the new_improper_form variable is not a periodic improper. """ try: - eqn_ratio, k, n, phi, phi_eq = sympy.symbols("eqn_ratio k n phi phi_eq") + eqn_ratio, _k, _n, _phi, _phi_eq = sympy.symbols("eqn_ratio k n phi phi_eq") values = sympy.nonlinsolve( [ eqn_ratio @@ -671,7 +671,7 @@ def evaluate_periodic_improper_format_with_scaler( [eqn_ratio], ) - form_scalar = float(list(values)[0][0]) + form_scalar = float(next(iter(values))[0]) form_output = "PeriodicImproperPotential" except (UnsolvableFactorError, TypeError): diff --git a/gmso/utils/ff_utils.py b/gmso/utils/ff_utils.py index a1239759d..bad9e4872 100644 --- a/gmso/utils/ff_utils.py +++ b/gmso/utils/ff_utils.py @@ -39,7 +39,7 @@ # Create a dictionary of units _unyt_dictionary = {} for name, item in vars(u).items(): - if isinstance(item, u.Unit) or isinstance(item, u.unyt_quantity): + if isinstance(item, (u.Unit, u.unyt_quantity)): _unyt_dictionary.update({name: item}) @@ -320,9 +320,9 @@ def _parse_scaling_factors(meta_tag): "electrostatics14Scale": meta_tag.get("electrostatics14Scale", 1.0), "nonBonded14Scale": meta_tag.get("nonBonded14Scale", 1.0), } - for key in scaling_factors: - if not isinstance(scaling_factors[key], float): - scaling_factors[key] = float(scaling_factors[key]) + for key, val in scaling_factors.items(): + if not isinstance(val, float): + scaling_factors[key] = float(val) return scaling_factors @@ -370,8 +370,8 @@ def parse_ff_atomtypes(atomtypes_el, ff_meta): if atom_types_expression: ctor_kwargs["expression"] = atom_types_expression - for kwarg in ctor_kwargs: - ctor_kwargs[kwarg] = atom_type.attrib.get(kwarg, ctor_kwargs[kwarg]) + for kwarg, val in ctor_kwargs.items(): + ctor_kwargs[kwarg] = atom_type.attrib.get(kwarg, val) tags = {"tags": {"element": ctor_kwargs.pop("element", "")}} @@ -382,9 +382,9 @@ def parse_ff_atomtypes(atomtypes_el, ff_meta): float(ctor_kwargs["mass"]), units_dict["mass"] ) if isinstance(ctor_kwargs["overrides"], str): - ctor_kwargs["overrides"] = set( + ctor_kwargs["overrides"] = { override.strip() for override in ctor_kwargs["overrides"].split(",") - ) + } if isinstance(ctor_kwargs["charge"], str): ctor_kwargs["charge"] = u.unyt_quantity( float(ctor_kwargs["charge"]), units_dict["charge"] @@ -392,7 +392,7 @@ def parse_ff_atomtypes(atomtypes_el, ff_meta): params_dict = _parse_params_values(atom_type, param_unit_dict, "AtomType") if not ctor_kwargs["parameters"] and params_dict: ctor_kwargs["parameters"] = params_dict - valued_param_vars = set(sympify(param) for param in params_dict.keys()) + valued_param_vars = {sympify(param) for param in params_dict} ctor_kwargs["independent_variables"] = ( sympify(atom_types_expression).free_symbols - valued_param_vars ) @@ -432,8 +432,8 @@ def parse_ff_connection_types(connectiontypes_el, child_tag="BondType"): if connectiontype_expression: ctor_kwargs["expression"] = connectiontype_expression - for kwarg in ctor_kwargs: - ctor_kwargs[kwarg] = connection_type.attrib.get(kwarg, ctor_kwargs[kwarg]) + for kwarg, val in ctor_kwargs.items(): + ctor_kwargs[kwarg] = connection_type.attrib.get(kwarg, val) ctor_kwargs["member_types"] = _get_member_types(connection_type) if not ctor_kwargs["member_types"]: @@ -447,9 +447,7 @@ def parse_ff_connection_types(connectiontypes_el, child_tag="BondType"): ctor_kwargs["expression"], ) - valued_param_vars = set( - sympify(param) for param in ctor_kwargs["parameters"].keys() - ) + valued_param_vars = {sympify(param) for param in ctor_kwargs["parameters"]} ctor_kwargs["independent_variables"] = ( sympify(connectiontype_expression).free_symbols - valued_param_vars ) @@ -464,10 +462,10 @@ def parse_ff_connection_types(connectiontypes_el, child_tag="BondType"): return connectiontypes_dict -def parse_ff_virtual_types( - virtualtypes_el, child_tag="VirtualSiteType", ff_meta=dict() -): +def parse_ff_virtual_types(virtualtypes_el, child_tag="VirtualSiteType", ff_meta=None): """Parse an XML etree Element rooted at VirtualSiteType to create topology.core.VirtualType.""" + if ff_meta is None: + ff_meta = {} virtualtypes_dict = {} units_dict = ff_meta.get("Units") @@ -492,8 +490,8 @@ def parse_ff_virtual_types( "member_classes": None, } - for kwarg in ctor_kwargs: # get directly from etree - ctor_kwargs[kwarg] = virtual_type.attrib.get(kwarg, ctor_kwargs[kwarg]) + for kwarg, val in ctor_kwargs.items(): # get directly from etree + ctor_kwargs[kwarg] = virtual_type.attrib.get(kwarg, val) for expressStr, virtualClass in zip( ("potential_", "position_"), (VirtualPotentialType, VirtualPositionType) @@ -508,9 +506,7 @@ def parse_ff_virtual_types( child_tag, kwargs["expression"], ) - valued_param_vars = set( - sympify(param) for param in kwargs["parameters"].keys() - ) + valued_param_vars = {sympify(param) for param in kwargs["parameters"]} kwargs["independent_variables"] = ( sympify(expressionDict[expressStr]).free_symbols - valued_param_vars ) @@ -555,10 +551,8 @@ def parse_ff_pairpotential_types(pairpotentialtypes_el): if pairpotentialtype_expression: ctor_kwargs["expression"] = pairpotentialtype_expression - for kwarg in ctor_kwargs: - ctor_kwargs[kwarg] = pairpotential_type.attrib.get( - kwarg, ctor_kwargs[kwarg] - ) + for kwarg, val in ctor_kwargs.items(): + ctor_kwargs[kwarg] = pairpotential_type.attrib.get(kwarg, val) ctor_kwargs["member_types"] = _get_member_types(pairpotential_type) if not ctor_kwargs["parameters"]: @@ -569,9 +563,7 @@ def parse_ff_pairpotential_types(pairpotentialtypes_el): ctor_kwargs["expression"], ) - valued_param_vars = set( - sympify(param) for param in ctor_kwargs["parameters"].keys() - ) + valued_param_vars = {sympify(param) for param in ctor_kwargs["parameters"]} ctor_kwargs["independent_variables"] = ( sympify(pairpotentialtype_expression).free_symbols - valued_param_vars ) diff --git a/gmso/utils/geometry.py b/gmso/utils/geometry.py index bcb57c3c1..2bd183f06 100644 --- a/gmso/utils/geometry.py +++ b/gmso/utils/geometry.py @@ -31,7 +31,7 @@ def coord_shift(xyz, box_lengths): return xyz -def moment_of_inertia(xyz, masses, center=np.zeros(3)): +def moment_of_inertia(xyz, masses, center=None): """Find the moment of inertia tensor given a set of particle coordinates and their corresponding masses. @@ -52,6 +52,8 @@ def moment_of_inertia(xyz, masses, center=np.zeros(3)): numpy.ndarray (3,) Diagonal components of the moment of inertia tensor. """ + if center is None: + center = np.zeros(3) xyz -= np.asarray(center) x = xyz[:, 0] y = xyz[:, 1] diff --git a/gmso/utils/io.py b/gmso/utils/io.py index 8ea102106..0fe1137b2 100644 --- a/gmso/utils/io.py +++ b/gmso/utils/io.py @@ -8,7 +8,7 @@ from importlib.resources import files from unittest import SkipTest -MESSAGES = dict() +MESSAGES = {} MESSAGES["matplotlib.pyplot"] = """ The code at {filename}:{line_number} requires the "matplotlib" package matplotlib can be installed using: @@ -89,15 +89,15 @@ def import_(module): + module + " package" ) - raise ImportError("No module named %s" % module) + raise ImportError(f"No module named {module}") ( - frame, + _frame, filename, line_number, - function_name, - lines, - index, + _function_name, + _lines, + _index, ) = inspect.getouterframes(inspect.currentframe())[1] m = message.format(filename=os.path.basename(filename), line_number=line_number) @@ -208,7 +208,7 @@ def import_(module): def run_from_ipython(): """Verify that the code is running in an ipython kernel.""" try: - __IPYTHON__ - return True + shell = get_ipython().__class__.__name__ + return shell == "ZMQInteractiveShell" except NameError: return False diff --git a/gmso/utils/nx_utils.py b/gmso/utils/nx_utils.py index 2c5e04d31..d5aa42271 100644 --- a/gmso/utils/nx_utils.py +++ b/gmso/utils/nx_utils.py @@ -92,7 +92,7 @@ def select_angles_from_sites(networkx_graph, top, Atom1, Atom2, Atom3): edges_widget = widgets.Dropdown( options=params_list, layout=widgets.Layout(width="60%"), - style=dict(description_width="initial"), + style={"description_width": "initial"}, description="Selected Edge", ) interact( @@ -116,7 +116,7 @@ def select_dihedrals_from_sites( edges_widget = widgets.Dropdown( options=params_list, layout=widgets.Layout(width="60%"), - style=dict(description_width="initial"), + style={"description_width": "initial"}, description="Selected Edge", ) interact( @@ -260,8 +260,8 @@ def select_params_on_networkx(networkx_graph, atoms): # turn the dict selectable list into a list of tuples. list_of_edges = [] - for key in selectable_list: - list_of_edges.append((key, selectable_list[key])) + for key, val in selectable_list.items(): + list_of_edges.append((key, val)) return list_of_edges @@ -339,10 +339,12 @@ def plot_networkx_nodes( edge_weights=None, edge_colors=None, node_sizes=None, - list_of_labels=["atom_type.name"], + list_of_labels=None, ): """Plot the nodes of the networkX graph.""" # Place nodes at 2D positions related to position in the topology + if list_of_labels is None: + list_of_labels = ["atom_type.name"] layout = nx.drawing.layout.kamada_kawai_layout(networkx_graph) # Use this dictionary to color specific atoms @@ -547,7 +549,7 @@ def create_dict_of_labels_for_edges(selectable_dict, edge): except AttributeError: print(f"An atomtype for {edge[1].label} is missing") label = label0 + " --- " + label1 - if label in selectable_dict.keys(): + if label in selectable_dict: selectable_dict[label].append(edge) else: selectable_dict[label] = [] @@ -559,10 +561,14 @@ def plot_networkx_bonds( networkx_graph, atom_name1=None, atom_name2=None, - list_of_labels=["atom_type.name"], - list_of_bonds=[], + list_of_labels=None, + list_of_bonds=None, ): """Plot the bonds of the networkX graph.""" + if list_of_bonds is None: + list_of_bonds = [] + if list_of_labels is None: + list_of_labels = ["atom_type.name"] fig, ax = plt.subplots(1, 1, figsize=(8, 8)) # Create dictionaries of edges that correspond red thick lines for the selected bonds @@ -604,9 +610,7 @@ def report_bond_parameters(topology, edge): print(f"The bond between {edge} is missing parameters") -def plot_networkx_atomtypes( - topology, atom_name=None, list_of_labels=["atom_type.name"] -): +def plot_networkx_atomtypes(topology, atom_name=None, list_of_labels=None): """Get a networkx plot showing the atom types in a topology object. Parameters @@ -630,6 +634,8 @@ def plot_networkx_atomtypes( shown using matplotlib.pyplot.show() """ + if list_of_labels is None: + list_of_labels = ["atom_type.name"] fig, ax = plt.subplots(1, 1, figsize=(8, 8)) networkx_graph = to_networkx(topology) diff --git a/gmso/utils/sorting.py b/gmso/utils/sorting.py index 93b6d4f52..00e03760f 100644 --- a/gmso/utils/sorting.py +++ b/gmso/utils/sorting.py @@ -219,9 +219,7 @@ def sort_connection_strings(namesList, improperBool=False): else: return tuple(namesList) elif len(namesList) == 4 and improperBool: - return tuple( - [namesList[0], *sorted(namesList[1:])], - ) + return (namesList[0], *sorted(namesList[1:])) elif len(namesList) == 4 and not improperBool: if namesList[1] > namesList[2] or ( namesList[1] == namesList[2] and namesList[0] > namesList[3] @@ -247,8 +245,8 @@ def reindex_molecules(top): unique_moleculesDict[molecule.name] = {molecule.number} offsetDict = {} - for molecule in unique_moleculesDict: - min_val = min(unique_moleculesDict[molecule]) + for molecule, val in unique_moleculesDict.items(): + min_val = min(val) offsetDict[molecule] = min_val for site in top.sites: diff --git a/gmso/utils/specific_ff_to_residue.py b/gmso/utils/specific_ff_to_residue.py index cfcb73919..cdacc6307 100644 --- a/gmso/utils/specific_ff_to_residue.py +++ b/gmso/utils/specific_ff_to_residue.py @@ -299,7 +299,6 @@ def specific_ff_to_residue( # identify the bonded atoms and hence the molecule, label the GMSO objects # and create the function outputs. - molecule_number = 0 # 0 sets the 1st molecule_number at 1 molecules_atom_number_dict = {} unique_topology_groups_list = [] unique_topologies_groups_dict = {} @@ -374,9 +373,8 @@ def specific_ff_to_residue( # create a molecule number to atom number dict # Example: {molecule_number_x: {atom_number_1, ..., atom_number_y}, ...} - for molecule in molecules_atom_number_list: + for molecule_number, molecule in enumerate(molecules_atom_number_list): molecules_atom_number_dict.update({molecule_number: molecule}) - molecule_number += 1 for site in new_gmso_topology.sites: site_atom_number_iter = new_gmso_topology.get_index(site) @@ -410,7 +408,7 @@ def specific_ff_to_residue( atom_types_dict.update( { unique_top_group_name_iter: { - "expression": list(atom_type_expression_set)[0], + "expression": next(iter(atom_type_expression_set)), "atom_types": unique_top_iter.atom_types( filter_by=PotentialFilters.UNIQUE_NAME_CLASS ), @@ -435,7 +433,7 @@ def specific_ff_to_residue( bond_types_dict.update( { unique_top_group_name_iter: { - "expression": list(bond_type_expression_set)[0], + "expression": next(iter(bond_type_expression_set)), "bond_types": unique_top_iter.bond_types( filter_by=PotentialFilters.UNIQUE_NAME_CLASS ), @@ -460,7 +458,7 @@ def specific_ff_to_residue( angle_types_dict.update( { unique_top_group_name_iter: { - "expression": list(angle_type_expression_set)[0], + "expression": next(iter(angle_type_expression_set)), "angle_types": unique_top_iter.angle_types( filter_by=PotentialFilters.UNIQUE_NAME_CLASS ), @@ -485,7 +483,7 @@ def specific_ff_to_residue( dihedral_types_dict.update( { unique_top_group_name_iter: { - "expression": list(dihedral_type_expression_set)[0], + "expression": next(iter(dihedral_type_expression_set)), "dihedral_types": unique_top_iter.dihedral_types( filter_by=PotentialFilters.UNIQUE_NAME_CLASS ), @@ -510,7 +508,7 @@ def specific_ff_to_residue( improper_types_dict.update( { unique_top_group_name_iter: { - "expression": list(improper_type_expression_set)[0], + "expression": next(iter(improper_type_expression_set)), "improper_types": unique_top_iter.improper_types( filter_by=PotentialFilters.UNIQUE_NAME_CLASS ), @@ -567,13 +565,13 @@ def _validate_structure(structure, residues): """Validate if input is an mb.Compound with initialized box or mb.Box.""" if isinstance(structure, (Compound, mb.Box)): error_msg = f"The structure, {mb.Compound} or {mb.Box}, needs to have have box lengths and angles." - if isinstance(structure, Compound): - if structure.box is None: - raise TypeError(error_msg) - - elif isinstance(structure, mb.Box): - if structure.lengths is None or structure.angles is None: - raise TypeError(error_msg) + if ( + isinstance(structure, Compound) + and structure.box is None + or isinstance(structure, mb.Box) + and (structure.lengths is None or structure.angles is None) + ): + raise TypeError(error_msg) else: error_msg = ( "The structure expected to be of type: " diff --git a/gmso/utils/units.py b/gmso/utils/units.py index bd864059a..4a3dd22fb 100644 --- a/gmso/utils/units.py +++ b/gmso/utils/units.py @@ -350,7 +350,7 @@ def _dimensions_to_energy(dims): return dims energySym = Symbol("(energy)") # create dummy symbol to replace in equation dim_info = dims.as_terms() - time_idx = np.where(list(map(lambda x: x.name == "(time)", dim_info[1])))[0][0] + time_idx = np.where([x.name == "(time)" for x in dim_info[1]])[0][0] energy_exp = ( dim_info[0][0][1][1][time_idx] // 2 ) # energy has 1/time**2 in it, so this is the hint of how many @@ -365,9 +365,7 @@ def _dimensions_to_charge(dims): return dims chargeSym = Symbol("(charge)") # create dummy symbol to replace in equation dim_info = dims.as_terms() - current_idx = np.where( - list(map(lambda x: x.name == "(current_mks)", dim_info[1])) - )[0][0] + current_idx = np.where([x.name == "(current_mks)" for x in dim_info[1]])[0][0] charge_exp = dim_info[0][0][1][1][ current_idx ] # charge has (current_mks) in it, so this is the hint of how many @@ -382,9 +380,7 @@ def _dimensions_from_thermal_to_energy(dims): return dims energySym = Symbol("(energy)") # create dummy symbol to replace in equation dim_info = dims.as_terms() - temp_idx = np.where( - list(map(lambda x: x.name == "(temperature)", dim_info[1])) - )[0][0] + temp_idx = np.where([x.name == "(temperature)" for x in dim_info[1]])[0][0] temp_exp = dim_info[0][0][1][1][ temp_idx ] # energy has 1/time**2 in it, so this is the hint of how many @@ -496,9 +492,9 @@ def convert_params_units( the input potentials converted into the base units given by base_units `dict`. """ - converted_potentials = list() + converted_potentials = [] for potential in potentials: - converted_params = dict() + converted_params = {} for parameter in potential.parameters: unit_dim = expected_units_dim[parameter] ind_units = re.sub("[^a-zA-Z]+", " ", unit_dim).split() From 99fc59ae220dfe914d1b75d3ec4060044ab3c486 Mon Sep 17 00:00:00 2001 From: CalCraven Date: Fri, 4 Sep 2026 14:57:17 -0500 Subject: [PATCH 6/9] Add AtomTypesView to handle virtual_types in iterator --- gmso/core/topology.py | 15 +++++-------- gmso/core/views.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/gmso/core/topology.py b/gmso/core/topology.py index ecea969dd..6385a559d 100644 --- a/gmso/core/topology.py +++ b/gmso/core/topology.py @@ -23,7 +23,7 @@ from gmso.core.improper import Improper from gmso.core.improper_type import ImproperType from gmso.core.pairpotential_type import PairPotentialType -from gmso.core.views import TopologyPotentialView +from gmso.core.views import AtomTypesView, TopologyPotentialView from gmso.exceptions import GMSOError from gmso.utils.connectivity import ( identify_connections as _identify_connections, @@ -340,12 +340,12 @@ def unique_site_labels(self, label_type="molecule", name_only=False): return unique_tags @property - def atom_types(self, include_virtual_types=False): + def atom_types(self): """Return all atom_types in the topology. Notes ----- - This returns a TopologyPotentialView object which can be used as + This returns a AtomTypesView object which can be used as an iterator. By default, this will return a view with all the atom_types in the topology (if multiple sites point to the same atom_type, only a single reference is returned/iterated upon). Use, different filters(builtin or custom) to suit your needs. @@ -380,16 +380,11 @@ def atom_types(self, include_virtual_types=False): Returns ------- - gmso.core.views.TopologyPotentialView + gmso.core.views.AtomTypesView An iterator of the atom_types in the system filtered according to the filter function supplied. """ - if include_virtual_types: - return TopologyPotentialView( - itertools.chain(self._sites, self._virtual_sites) - ) - else: - return TopologyPotentialView(self._sites) + return AtomTypesView(self) @property def connection_types(self): diff --git a/gmso/core/views.py b/gmso/core/views.py index 5cbfdffaa..1b6af23f5 100644 --- a/gmso/core/views.py +++ b/gmso/core/views.py @@ -1,3 +1,4 @@ +import itertools import uuid from collections import defaultdict @@ -212,3 +213,52 @@ def __repr__(self): def __len__(self): return len(list(self.yield_view())) # This will be costly? But How frequent? + + +class AtomTypesView(TopologyPotentialView): + """A view of a Topology's atom types, extending TopologyPotentialView.""" + + def __init__( + self, + topology, + filter_by=PotentialFilters.UNIQUE_ID, + include_virtual_types=False, + ): + self.topology = topology + self.include_virtual_types = include_virtual_types + iterator = self._build_iterator(topology, include_virtual_types) + super().__init__(iterator, filter_by=filter_by) + + @staticmethod + def _build_iterator(topology, include_virtual_types): + if include_virtual_types: + return itertools.chain(topology._sites, topology._virtual_sites) + return topology._sites + + def __call__( + self, + filter_by=PotentialFilters.UNIQUE_ID, + *, + include_virtual_types=False, + ): + """Return a view of the atom types, possibly with new options. + + Parameters + ---------- + filter_by : str, callable, or None, default=PotentialFilters.UNIQUE_ID + Positional-compatible with ``TopologyPotentialView.__call__``, + e.g. ``top.atom_types(PotentialFilters.UNIQUE_NAME_CLASS)``. + include_virtual_types : bool, keyword-only, default=False + If True, include atom types from virtual sites as well. + """ + if ( + filter_by == self.filter_by + and include_virtual_types == self.include_virtual_types + ): + return self + + return AtomTypesView( + self.topology, + filter_by=filter_by, + include_virtual_types=include_virtual_types, + ) From 62a1d176086bee02b6dade8bdf59d0acf342e850 Mon Sep 17 00:00:00 2001 From: CalCraven Date: Fri, 4 Sep 2026 15:05:11 -0500 Subject: [PATCH 7/9] Fix codecov security --- gmso/formats/lammpsdata.py | 57 +++++++++++++++++++------------------- gmso/tests/test_hoomd.py | 10 +++---- 2 files changed, 32 insertions(+), 35 deletions(-) diff --git a/gmso/formats/lammpsdata.py b/gmso/formats/lammpsdata.py index 96dedb109..6ab15c7ac 100644 --- a/gmso/formats/lammpsdata.py +++ b/gmso/formats/lammpsdata.py @@ -1062,36 +1062,35 @@ def _write_impropertypes(out_file, top, base_unyts, parser, cfactorsDict): base_msg = "{}\t" # handles index end_msg = "# {}\t{}\t{}\t{}\n" - if True: # one cvff set per improper layer - ndecimalsDict = {"k": 6, "n": 0, "phi_eq": 0} - idx = 0 - improper_typesList = [] - for improper_type, members in index_membersList: - parameter_termList, parameterStrList = parser(improper_type) - variable_msg = "{:8}\t" * len(parameterStrList) - full_msg = base_msg + variable_msg + end_msg - for parameter_terms in parameter_termList: # list of params on each line - out_file.write( - full_msg.format( - idx + 1, - *[ - base_unyts.convert_parameter( - convert_kelvin_to_energy_units(parameter, "kJ"), - cfactorsDict, - n_decimals=ndecimalsDict[parameterStr], - name=parameterStr, - ) - for parameter, parameterStr in zip( - parameter_terms, parameterStrList - ) - ], - *members, - ) + ndecimalsDict = {"k": 6, "n": 0, "phi_eq": 0} + idx = 0 + improper_typesList = [] + for improper_type, members in index_membersList: + parameter_termList, parameterStrList = parser(improper_type) + variable_msg = "{:8}\t" * len(parameterStrList) + full_msg = base_msg + variable_msg + end_msg + for parameter_terms in parameter_termList: # list of params on each line + out_file.write( + full_msg.format( + idx + 1, + *[ + base_unyts.convert_parameter( + convert_kelvin_to_energy_units(parameter, "kJ"), + cfactorsDict, + n_decimals=ndecimalsDict[parameterStr], + name=parameterStr, + ) + for parameter, parameterStr in zip( + parameter_terms, parameterStrList + ) + ], + *members, ) - improper_typesList.append( - improper_type - ) # add improper type multiple times if it is layered - idx += 1 + ) + improper_typesList.append( + improper_type + ) # add improper type multiple times if it is layered + idx += 1 return improper_typesList diff --git a/gmso/tests/test_hoomd.py b/gmso/tests/test_hoomd.py index ce305d801..691d512d5 100644 --- a/gmso/tests/test_hoomd.py +++ b/gmso/tests/test_hoomd.py @@ -289,10 +289,8 @@ def test_diff_base_units(self): oplsaa = ForceField("oplsaa") top = apply(top, oplsaa, remove_untyped=True) - _gmso_snapshot, _snapshot_base_units = to_hoomd_snapshot( - top, base_units=base_units - ) - _gmso_forces, _forces_base_units = to_hoomd_forcefield( + to_hoomd_snapshot(top, base_units=base_units) + to_hoomd_forcefield( top, r_cut=1.4, base_units=base_units, @@ -313,8 +311,8 @@ def test_default_units(self): oplsaa = ForceField("oplsaa") top = apply(top, oplsaa, remove_untyped=True) - _gmso_snapshot, _snapshot_base_units = to_hoomd_snapshot(top) - _gmso_forces, _forces_base_units = to_hoomd_forcefield( + to_hoomd_snapshot(top) + to_hoomd_forcefield( top=top, r_cut=1.4, pppm_kwargs={"resolution": (64, 64, 64), "order": 7}, From b0a0b976edd3adb7065991f70a6aac842702a151 Mon Sep 17 00:00:00 2001 From: CalCraven Date: Fri, 4 Sep 2026 15:18:50 -0500 Subject: [PATCH 8/9] Fixes to ruff linting --- gmso/formats/xyz.py | 2 +- gmso/lib/potential_templates.py | 6 ++---- .../parameterization/test_virtual_site_parameterization.py | 1 - gmso/tests/test_box.py | 2 +- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/gmso/formats/xyz.py b/gmso/formats/xyz.py index aa4de6b54..3b358ff8c 100644 --- a/gmso/formats/xyz.py +++ b/gmso/formats/xyz.py @@ -86,7 +86,7 @@ def write_xyz( with open(filename, "w") as out_file: out_file.write(f"{top.n_sites:d}\n") out_file.write( - f"{top.name} {filename} written by topology at {datetime.datetime.now()!s}\n" + f"{top.name} {filename} written by topology at {datetime.datetime.now(datetime.timezone.utc).astimezone()!s}\n" ) out_file.write(_prepare_particles(top, decimals)) diff --git a/gmso/lib/potential_templates.py b/gmso/lib/potential_templates.py index 167bbd071..e55551179 100644 --- a/gmso/lib/potential_templates.py +++ b/gmso/lib/potential_templates.py @@ -131,7 +131,7 @@ def assert_can_parameterize_with( for param_name, param_value in parameters.items(): quantity = param_value if not (isinstance(param_value, u.unyt_array)): - raise ValueError(f"Parameter {param_name} lacks a unit.") + raise TypeError(f"Parameter {param_name} lacks a unit.") if param_name not in self.expected_parameters_dimensions: raise UnknownParameterError( @@ -157,9 +157,7 @@ class PotentialTemplateLibrary(Singleton): """A singleton collection of all the potential templates.""" def __init__(self): - try: - self.json_refs - except AttributeError: + if getattr(self, "json_refs", None) is None: self.json_refs = POTENTIAL_JSONS potential_names = [pot_json.name for pot_json in POTENTIAL_JSONS] self._ref_dict = { diff --git a/gmso/tests/parameterization/test_virtual_site_parameterization.py b/gmso/tests/parameterization/test_virtual_site_parameterization.py index 575432976..31dd14171 100644 --- a/gmso/tests/parameterization/test_virtual_site_parameterization.py +++ b/gmso/tests/parameterization/test_virtual_site_parameterization.py @@ -25,7 +25,6 @@ def test_tip4p_files(self): speedup_by_molgraph=False, identify_connections=True, ) - gmso_top.virtual_sites assert len(gmso_top.virtual_sites) == 1 vtype = gmso_top.virtual_sites[0].virtual_type assert ("HW", "OW", "HW") == vtype.member_classes diff --git a/gmso/tests/test_box.py b/gmso/tests/test_box.py index a05a941ad..77424cd3e 100644 --- a/gmso/tests/test_box.py +++ b/gmso/tests/test_box.py @@ -96,7 +96,7 @@ def test_scaled_vectors(self): assert vectors.units == u.nm def test_eq(self, box): - assert box == box + assert box == Box(lengths=u.nm * np.ones(3)) def test_eq_bad_lengths(self, box): diff_lengths = deepcopy(box) From 2f8fed48ae08d0878fd45e94b6dd5ebe346aee35 Mon Sep 17 00:00:00 2001 From: CalCraven Date: Fri, 4 Sep 2026 15:33:16 -0500 Subject: [PATCH 9/9] Change ValueError to TypeError to match convention --- gmso/tests/test_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gmso/tests/test_template.py b/gmso/tests/test_template.py index f5141097e..c87af163a 100644 --- a/gmso/tests/test_template.py +++ b/gmso/tests/test_template.py @@ -119,7 +119,7 @@ def test_non_unyt_error(self): }, ) - with pytest.raises(ValueError): + with pytest.raises(TypeError): template.assert_can_parameterize_with({"a": 1.0, "b": 2.0}) def test_dimensionless_errors(self):