diff --git a/pyproject.toml b/pyproject.toml
index 2da8bbffeb8040d17ef480e1d0aaed5c32f77fb6..7d1bfe74f99f70d57b16349a5fdf793b21b91765 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -29,3 +29,4 @@ dependencies = [
 ]
 [project.scripts]
 geomtools-push = "geomtools.sfx.report:push_geometry"
+geomtools-refine-sfx = "geomtools.sfx.optimiser:refine"
diff --git a/src/geomtools/detector/crystfel_frm.py b/src/geomtools/detector/crystfel_frm.py
index 64181129c27d2a9381603d3b76d783ca7e941a49..77d94d6504caf2e3db6a267fef76b2a759fee0c7 100644
--- a/src/geomtools/detector/crystfel_frm.py
+++ b/src/geomtools/detector/crystfel_frm.py
@@ -6,7 +6,7 @@ from natsort import natsorted
 
 
 HEADER_TEMPLATE = """\
-; {detector} geometry file written by geomtools-optimiser-sfx
+; {detector} geometry file written by geomtools
 ; You may need to edit this file to add:
 ; - data and mask locations in the file
 ; - mask_good & mask_bad values to interpret the mask
diff --git a/src/geomtools/sfx/optimiser.py b/src/geomtools/sfx/optimiser.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0d55c05dc204922976932c7856dd01a8f29010c
--- /dev/null
+++ b/src/geomtools/sfx/optimiser.py
@@ -0,0 +1,45 @@
+import argparse
+import pathlib
+import tempfile
+
+from .refine import refine_geometry
+from .crystfelio import extract_geometry, read_crystfel_streamfile
+from ..detector import read_crystfel_geom, write_crystfel_geom
+
+
+def refine():
+    parser = argparse.ArgumentParser(
+        description='Makes the detector geometry report')
+    parser.add_argument('-i', '--stream', type=pathlib.Path, required=True,
+                        help="Crystfel stream file")
+    parser.add_argument('-c', '--connected', default="modules",
+                        help="Collection of rigid groups to pose")
+    parser.add_argument('-o', '--output', type=pathlib.Path,
+                        default=pathlib.Path("refined.geom"),
+                        help="File to store refined geometry")
+
+    args = parser.parse_args()
+
+    stream_filename = args.stream.absolute()
+    geom_file = extract_geometry(stream_filename)
+
+    temp_geom_file = tempfile.NamedTemporaryFile("w+t", prefix='geomtools')
+    temp_geom_file.write(geom_file.read())
+    geom_file.seek(0)
+
+    panels, beam = read_crystfel_geom(
+        temp_geom_file.name, indexes={'modno': 1})
+
+    temp_geom_file.close()
+
+    fr, pe, la, re, ma = read_crystfel_streamfile(
+        stream_filename, panels, args.connected, disp=True)
+
+    panels_new, transform, clen = refine_geometry(ma, panels, 1.0)
+
+    with open(args.output.absolute(), "w") as f:
+        write_crystfel_geom(f, panels_new, beam, ["modules", "quads"])
+
+
+if __name__ == "__main__":
+    refine()