Repository: openforcefield/openff-toolkit

Issue:
Allow Smirnoff99Frosst force fields with lower case names 
**Is your feature request related to a problem? Please describe.**
When trying to run the smirnoff99Frosst forcefields via QCArchive the name is lowered somewhere within the internals of QCFractal/QCEngine which means the force field can not be loaded on Linux but this still works on mac as it ignores the capitalization when loading the file.

**Describe the solution you'd like**
It would be great if the toolkit could even out this behaviour by replacing any lower case names which reach the toolkit with the correct capitalization before loading the file. I think this could be done with the following code placed here https://github.com/openforcefield/openff-toolkit/blob/bb40b067484f968b39092b8a892bbc12f2d6d010/openff/toolkit/typing/engines/smirnoff/forcefield.py#L1087
```python
import re
source = re.sub("frosst", "Frosst", source)
```

**Describe alternatives you've considered**
I have tried changing this in other places in the chain but I think it makes the most sense here as this is the main call to load the forcefield file. 



Discussion:

Patch:
diff --git a/docs/releasehistory.rst b/docs/releasehistory.rst
index 9b561182..e196dcfa 100644
--- a/docs/releasehistory.rst
+++ b/docs/releasehistory.rst
@@ -45,6 +45,8 @@ Behavior changed
   slightly different energies as a result of this change.
 - `PR #839 <https://github.com/openforcefield/openforcefield/pull/839>`_: The average WBO will now be returned when
   multiple conformers are provided to ``assign_fractional_bond_orders`` using ``use_conformers``.
+- `PR #816 <https://github.com/openforcefield/openforcefield/pull/816>`_: Force field file paths
+  are now loaded in a case-insensitive manner.
 
 Improved documentation and warnings
 """""""""""""""""""""""""""""""""""
diff --git a/openff/toolkit/typing/engines/smirnoff/forcefield.py b/openff/toolkit/typing/engines/smirnoff/forcefield.py
index d22c4e89..1c8151ba 100644
--- a/openff/toolkit/typing/engines/smirnoff/forcefield.py
+++ b/openff/toolkit/typing/engines/smirnoff/forcefield.py
@@ -1094,11 +1094,13 @@ class ForceField:
 
             # Determine the actual path of the file.
             # TODO: What is desired toolkit behavior if two files with the desired name are available?
-            for dir_path in searched_dirs_paths:
-                file_path = os.path.join(dir_path, source)
-                if os.path.isfile(file_path):
-                    source = file_path
-                    break
+            dir_paths = [pathlib.Path(path) for path in searched_dirs_paths]
+            for dir_path in dir_paths:
+                for file_path in dir_path.glob("**/*.offxml"):
+                    # Cannot compare filenames alone, source can be test_forcefield/...
+                    if str(file_path).lower().endswith(source.lower()):
+                        source = str(file_path.absolute())
+                        break
 
         # Process all SMIRNOFF definition files or objects
         # QUESTION: Allow users to specify force field URLs so they can pull force field definitions from the web too?


Test patch:
diff --git a/openff/toolkit/tests/test_forcefield.py b/openff/toolkit/tests/test_forcefield.py
index 20e515d1..36cb4197 100644
--- a/openff/toolkit/tests/test_forcefield.py
+++ b/openff/toolkit/tests/test_forcefield.py
@@ -797,6 +797,13 @@ class TestForceField:
         for ff in expected_force_fields:
             assert ff in available_force_fields
 
+    def test_force_field_case(self):
+        """Ensure forcefield paths are loaded in a case-insensitive manner"""
+        default_case = ForceField("smirnoff99Frosst-1.1.0.offxml")
+        lower_case = ForceField("smirnoff99frosst-1.1.0.offxml")
+
+        assert hash(default_case) == hash(lower_case)
+
     @pytest.mark.parametrize("full_path", [(True, False)])
     @pytest.mark.parametrize("force_field_file", [*get_available_force_fields()])
     def test_get_available_force_fields_loadable(self, full_path, force_field_file):