diff --git a/.gitignore b/.gitignore
index b4ecdf8c0..4d5e2aa3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,4 @@ venv
 build*/
 dist*/
 autodoc*
+switch_model/data/installed_version.txt
diff --git a/get_and_record_version.py b/get_and_record_version.py
new file mode 100644
index 000000000..9f407f51c
--- /dev/null
+++ b/get_and_record_version.py
@@ -0,0 +1,126 @@
+from __future__ import print_function
+import argparse
+import logging
+import os
+import subprocess
+
+"""
+Define a precise package version that includes any git digests for any commits
+made subsequently to a package release. The base version (2.0.4 in this
+example) is obtained from the last tag that starts with "2". version. Also use
+the git-standard "dirty" suffix instead of "localmod" for installations from
+code that hasn't been committed.
+
+Example: 
+1) 112 commits were made subsequent to an official release (possibly on
+a branch), plus some uncommitted modifications. The version would be:
+2.0.4+112+{gitsha}+dirty
+2) Same scenario, but no uncommitted modifications: 2.0.4+112+{gitsha}
+3) No commits since the last tagged release: 2.0.4
+
+These functions are encoded into a separate file from setup.py to support
+including precise versions in docker tags.
+"""
+
+def get_git_version():
+    """
+    Try to get git version like '{tag}+{gitsha}', with the added suffix
+    "+dirty" if the git repo has had any uncommitted modifications. 
+    The "+{gitsha}" suffix will be dropped if this is the tagged version.
+    Code adapted from setuptools_git_version which has an MIT license.
+        https://pypi.org/project/setuptools-git-version/
+    Note: Only look for tags that start with "2." to avoid tags of
+    non-released versions.
+    """
+    git_command = "git describe --all --long --match '2.*' --dirty --always"
+    fmt = '{base_v}+{count}+{gitsha}{dirty}'
+
+    git_version = subprocess.check_output(git_command, shell=True).decode('utf-8').strip()
+    # The prefix tags/ may not appear in every context, and should be ignored.
+    match = re.match("(tags/)?(.*)-([\d]+)-g([0-9a-f]+)(-dirty)?", git_version)
+    assert match, (
+        "Trouble parsing git version output. Got {}, expected 3 or 4 things "
+        "separated by dashes. This has been encountered when the local git repo "
+        "lacks tags, which can be solved by fetching from the main repo:\n"
+        "`git remote add main https://github.com/switch-model/switch.git && "
+        "git fetch --all`".format(git_version)
+    )
+    parts = match.groups()[1:]
+    if parts[-1] == '-dirty':
+        dirty = '+dirty'
+    else:
+        dirty = ''
+    base_v, count, sha = parts[:3]
+    if count == '0' and not dirty:
+        return base_v
+    return fmt.format(base_v=base_v, count=count, gitsha=sha, dirty=dirty)
+
+def get_and_record_version(repo_path):
+    """
+    Attempt to get an absolute version number that includes commits made since
+    the last release. If that succeeds, record the absolute version and use it
+    for the pip catalog. If that fails, fall back to something reasonable and
+    vague for the pip catalog, using the data from base_version.py.
+    """
+    pkg_dir = os.path.join(repo_path , 'switch_model' )
+    data_dir = os.path.join(pkg_dir, 'data' )
+    __version__ = None
+    try:
+        __version__ = get_git_version()
+        with open(os.path.join(data_dir, 'installed_version.txt'), 'w+') as f:
+            f.write(__version__)
+    except subprocess.CalledProcessError as e:
+        logging.warning(
+            "Could not call git as a subprocess to determine precise version."
+            "Falling back to using the static version from version.py")
+        logging.exception(e)
+    except AssertionError as e:
+        logging.warning("Trouble parsing git output.")
+        logging.exception(e)
+    except Exception as e:
+        logging.warning(
+            "Trouble getting precise version from git repository; "
+            "using base version from switch_model/version.py. "
+            "Error was: {}".format(e)
+        )
+    if __version__ is None:
+        module_dat = {}
+        with open(os.path.join(pkg_dir, 'version.py')) as fp:
+            exec(fp.read(), module_dat)
+        __version__ = module_dat['__version__']
+    return __version__
+
+def get_args():
+    parser = argparse.ArgumentParser(
+        description='Get a precise local version of this git repository',
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+    parser.add_argument(
+        '--verbose', '-v', dest='verbose', default=False, 
+        action='store_const', const=logging.WARNING,
+        help='Show information about model preparation and solution')
+    parser.add_argument(
+        '--very-verbose', '-vv', dest='verbose', default=False, 
+        action='store_const', const=logging.INFO,
+        help='Show more information about model preparation and solution')
+    parser.add_argument(
+        '--very-very-verbose', '-vvv', dest='verbose', default=False, 
+        action='store_const', const=logging.DEBUG,
+        help='Show debugging-level information about model preparation and solution')
+    parser.add_argument(
+        '--quiet', '-q', dest='verbose', action='store_false',
+        help="Don't show information about model preparation and solution "
+             "(cancels --verbose setting)")
+
+    args = parser.parse_args()
+    return args
+
+def main():
+    args = get_args()
+    if args.verbose:
+        logging.basicConfig(format='%(levelname)s:%(message)s', level=args.verbose)
+    repo_path = os.path.dirname(os.path.realpath(__file__))
+    __version__ = get_and_record_version(repo_path)
+    print(__version__)
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 30deab938..109647e2b 100644
--- a/setup.py
+++ b/setup.py
@@ -15,12 +15,10 @@
 import os
 from setuptools import setup, find_packages
 
-# Get the version number. Strategy #3 from https://packaging.python.org/single_source_version/
-version_path = os.path.join(os.path.dirname(__file__), 'switch_model', 'version.py')
-version = {}
-with open(version_path) as f:
-    exec(f.read(), version)
-__version__ = version['__version__']
+from get_and_record_version import get_and_record_version
+
+repo_path = os.path.dirname(os.path.realpath(__file__))
+__version__ = get_and_record_version(repo_path)
 
 def read(*rnames):
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
@@ -55,6 +53,9 @@ def read(*rnames):
         'Topic :: Software Development :: Libraries :: Python Modules'
     ],
     packages=find_packages(include=['switch_model', 'switch_model.*']),
+	package_data = {
+		'switch_model': ['data/*']
+	},
     keywords=[
         'renewable', 'power', 'energy', 'electricity',
         'production cost', 'capacity expansion',
@@ -65,6 +66,7 @@ def read(*rnames):
         'pint',         # needed by Pyomo when we run our tests, but not included
         'testfixtures', # used for standard tests
         'pandas',       # used for input upgrades and testing that functionality
+        'setuptools', # For parsing version numbers; it is part of almost all python distributions, but not guaranteed. 
     ],
     extras_require={
         # packages used for advanced demand response, progressive hedging
diff --git a/switch_model/data/__init__.py b/switch_model/data/__init__.py
new file mode 100644
index 000000000..2ae5248df
--- /dev/null
+++ b/switch_model/data/__init__.py
@@ -0,0 +1,3 @@
+"""This directory contains any necessary package data or default configuration
+files.
+"""
\ No newline at end of file
diff --git a/switch_model/utilities.py b/switch_model/utilities.py
index f97ce27c4..d61d8ab6d 100644
--- a/switch_model/utilities.py
+++ b/switch_model/utilities.py
@@ -12,6 +12,8 @@
 from pyomo.environ import *
 import pyomo.opt
 
+import switch_model
+
 # Define string_types (same as six.string_types). This is useful for
 # distinguishing between strings and other iterables.
 try:
@@ -263,6 +265,12 @@ def post_solve(instance, outputs_dir=None):
         if hasattr(module, 'post_solve'):
             module.post_solve(instance, outputs_dir)
 
+    # Save the precise version used to solve this problem.
+    version_path = os.path.join(outputs_dir, 'software_version.txt')
+    with open(version_path, 'w') as f:
+        f.write("This problem was solved with switch version {}.{}".format(
+            switch_model.__version__, os.linesep))
+
 
 def min_data_check(model, *mandatory_model_components):
     """
diff --git a/switch_model/version.py b/switch_model/version.py
index 813233d64..b98fbca11 100644
--- a/switch_model/version.py
+++ b/switch_model/version.py
@@ -5,4 +5,13 @@
 distribution because it needs to be executed before Switch (and its
 dependencies) are installed.
 """
-__version__='2.0.6-dev'
+import os
+
+base_version = '2.0.6-dev'
+
+try:
+    DATA_ROOT = os.path.join(os.path.dirname(__file__), 'data')
+    with open(os.path.join(DATA_ROOT, 'installed_version.txt'), 'r') as f:
+        __version__ = f.read().strip()
+except (IOError, NameError):
+    __version__ = base_version