{"id":34833012,"url":"https://github.com/openforcefield/tmos","last_synced_at":"2026-05-25T04:34:43.890Z","repository":{"id":305027552,"uuid":"982412618","full_name":"openforcefield/tmos","owner":"openforcefield","description":"Python script to determine the oxidation state of a metal complex","archived":false,"fork":false,"pushed_at":"2026-04-16T19:52:27.000Z","size":773,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-16T21:33:45.314Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://tmos.readthedocs.io/en/latest/?badge=latest","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openforcefield.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-12T21:03:06.000Z","updated_at":"2026-04-16T19:52:31.000Z","dependencies_parsed_at":"2025-07-18T01:24:12.542Z","dependency_job_id":"d406889e-7a1c-4c3b-a5c4-4b7270070976","html_url":"https://github.com/openforcefield/tmos","commit_stats":null,"previous_names":["jaclark5/tmos","openforcefield/tmos"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/openforcefield/tmos","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openforcefield%2Ftmos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openforcefield%2Ftmos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openforcefield%2Ftmos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openforcefield%2Ftmos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openforcefield","download_url":"https://codeload.github.com/openforcefield/tmos/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openforcefield%2Ftmos/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33461090,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T02:24:28.008Z","status":"ssl_error","status_checked_at":"2026-05-25T02:23:23.339Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-25T15:57:19.335Z","updated_at":"2026-05-25T04:34:43.885Z","avatar_url":"https://github.com/openforcefield.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Transition Metal Oxidation State (TMOS)\n=======================================\n[//]: # (Badges)\n[![Documentation Status](https://readthedocs.org/projects/tmos/badge/?version=latest)](https://tmos.readthedocs.io/en/latest/?badge=latest)\n[![GitHub Actions Build Status](https://github.com/openforcefield/tmos/workflows/CI/badge.svg)](https://github.com/openforcefield/tmos/actions?query=workflow%3ACI)\n\n**This is a set of informal tools for internal use. We make no guarantees of versioning, functionality, or support.**\n\n`tmos` predicts the oxidation state, formal charge, and d-electron count of a\ntransition metal complex from its 3-D structure.  Starting from atomic symbols\nand Cartesian coordinates it builds an RDKit molecule with correct bond orders and scores all (ligand assignment, oxidation state) pairs and returns a ranked list of `ComplexState` objects\n\n## Quick start\n\n### Transition metal complex (from XYZ data)\n\n```python\nimport tmos\nimport tmos.build_rdmol\n\n# symbols : list[str]   — element symbols\n# geometry: np.ndarray  — coordinates in Bohr (QCElemental convention)\n# molecular_charge: int — net charge of the complex\nrdmol_draft = tmos.build_rdmol.xyz_to_rdkit(input[\"symbols\"], input[\"geometry\"])\nresults = tmos.sanitize_complex(rdmol_draft, target_charge=input[\"molecular_charge\"])\n\nbest = results[0]\nprint(best.metal.oxidation_state)   # e.g. 2\nprint(best.metal.electron_count)    # e.g. 16\nprint(best.ligands.summary)         # \"6 ligand(s), 4L/2X donors, total charge=-2\"\nprint(best.complex.smiles)          # canonical SMILES of the reassembled complex\n```\n\n`xyz_to_rdkit` expects coordinates in **Å**.\n\n`sanitize_complex` returns a list of `ComplexState` objects sorted by score\nascending (lower is better). Scores below 1000 indicate a chemically\nplausible state (valid oxidation state, consistent net charge).\nThe total score is a weighted sum of penalty terms available via\n`state.score_components`.\n\nEach `ComplexState` has five sub-objects:\n\n| Attribute | Type | Key fields |\n|---|---|---|\n| `state.score` | `int` | Lower is better; \u003c 1000 = plausible |\n| `state.metal` | `MetalInfo` | `oxidation_state`, `electron_count`, `charge` |\n| `state.ligands` | `LigandSummary` | `number_Ltype_connectors`, `number_Xtype_connectors`, `total_charge`, `ligand_info` |\n| `state.complex` | `ComplexInfo` | `rdmol`, `smiles`, `formula`, `geometry_type` |\n| `state.score_components` | `ScoreComponents` | Penalty breakdown |\n\n### Organic / non-metal molecule (from XYZ data)\n\nFor molecules without a transition metal, use `determine_bonds` directly after\nbuilding the connectivity graph:\n\n```python\nimport tmos.build_rdmol\n\nrdmol = tmos.build_rdmol.xyz_to_rdkit(\n    input[\"symbols\"], input[\"geometry\"]\n)\nrdmol = tmos.build_rdmol.determine_bonds(rdmol, charge=input[\"molecular_charge\"])\n```\n\n### Useful `sanitize_complex` options\n\n| Parameter | Default | Purpose |\n|---|---|---|\n| `target_charge` | `0` | Expected net charge of the complex |\n| `score_cutoff` | `1000` | Discard states with score ≥ this value (`None` keeps all) |\n| `n_results` | `5` | Maximum states returned |\n| `geometry_method` | `\"angles\"` | Geometry backend: `\"angles\"`, `\"posym\"`, `\"pymatgen\"`, `\"rylm\"` |\n| `add_hydrogens` | `False` | Add explicit H atoms before processing |\n\n## Installation\n\n### Requirements\n\nPython ≥ 3.10 is required. Core dependencies (`rdkit`, `openbabel`, `networkx`,\n`qcelemental`, `periodictable`, `numpy`, `loguru`) are declared in\n`pyproject.toml` and installed automatically by `pip`.\n\n\u003e **Note**: `rdkit` and `openbabel` have compiled C/C++ extensions. Installing\n\u003e via **conda/mamba is strongly recommended** to avoid build issues.\n\u003e Pure-`pip` installs may work on some platforms but are not guaranteed.\n\n### Option A — conda / micromamba (recommended)\n\nClone the repository and create the bundled environment (includes all optional\ndependencies):\n\n```bash\ngit clone https://github.com/openforcefield/tmos\ncd tmos\nmicromamba env create -f requirements.yaml   # or: conda env create -f requirements.yaml\nmicromamba activate tmos                     # or: conda activate tmos\npip install -e .\n```\n\n### Option B — pip only\n\n```bash\ngit clone https://github.com/openforcefield/tmos\ncd tmos\npip install .\n```\n\nAdd `-e` for an editable / development install.\n\n### Optional dependencies\n\nSeveral features require extra packages that are *not* installed by default:\n\n| Feature | Extra | Install command |\n|---|---|---|\n| Geometry (pymatgen, posym) | `geometry` | `pip install \"tmos[geometry]\"` |\n| 3-D visualization (py3Dmol) | `viz` | `pip install \"tmos[viz]\"` |\n| Documentation build | `docs` | `pip install \"tmos[docs]\"` |\n| Tests | `test` | `pip install \"tmos[test]\"` |\n\nInstall multiple extras at once: `pip install \"tmos[geometry,viz,test]\"`.\n\n**rylm** is required for `geometry_method=\"rylm\"` and is not on PyPI:\n\n```bash\npip install git+https://github.com/chrisiacovella/rylm.git\n```\n\nIf an optional dependency is absent, the relevant function raises a clear\n`ImportError` with the exact install command.\n\n### Developer setup\n\n```bash\npip install \"tmos[geometry,viz,test]\"\npip install git+https://github.com/chrisiacovella/rylm.git\npre-commit install\n```\n\n### Copyright\n\nCopyright (c) 2025, Jennifer A Clark\n\n#### Acknowledgements\n\nProject based on the\n[Computational Molecular Science Python Cookiecutter](https://github.com/molssi/cookiecutter-cms) version 1.10.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenforcefield%2Ftmos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenforcefield%2Ftmos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenforcefield%2Ftmos/lists"}