From c7ffa99b678fe93a7ad16e389aab766a4e3d501f Mon Sep 17 00:00:00 2001 From: Claire Waters Date: Thu, 20 Aug 2026 13:08:52 -0500 Subject: [PATCH 1/3] REFACTOR: get kimunits.py from openkim-pipeline-excerpts add openkim-pipeline-excerpts as a submodule --- .gitmodules | 3 + docker/config/Dockerfile | 1 + docker/config/excerpts/kimunits.py | 164 ------------------------ docker/config/openkim-pipeline-excerpts | 1 + 4 files changed, 5 insertions(+), 164 deletions(-) create mode 100644 .gitmodules delete mode 100644 docker/config/excerpts/kimunits.py create mode 160000 docker/config/openkim-pipeline-excerpts diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e069dba --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "docker/config/openkim-pipeline-excerpts"] + path = docker/config/openkim-pipeline-excerpts + url = git@github.com:openkim/openkim-pipeline-excerpts.git diff --git a/docker/config/Dockerfile b/docker/config/Dockerfile index 18e7169..a0479a3 100644 --- a/docker/config/Dockerfile +++ b/docker/config/Dockerfile @@ -45,6 +45,7 @@ COPY instructions/README.txt /home/openkim/ RUN chmod 644 /home/openkim/README.txt COPY excerpts /pipeline/excerpts +COPY openkim-pipeline-excerpts /pipeline/excerpts COPY tools /pipeline/tools COPY utils/bashcompletion /pipeline/bashcompletion COPY utils/kimitems /usr/local/bin/ diff --git a/docker/config/excerpts/kimunits.py b/docker/config/excerpts/kimunits.py deleted file mode 100644 index 1d1d500..0000000 --- a/docker/config/excerpts/kimunits.py +++ /dev/null @@ -1,164 +0,0 @@ -""" -Simple wrapper for executable for converting arbitrary units to SI units - -Copyright (c) 2014-2022, Regents of the University of Minnesota. All rights -reserved. - -This software may be distributed as-is, without modification. -""" - -VERSION = 0.3 - -import re -import math -import subprocess -import warnings - -warnings.simplefilter("ignore") - - -class UnitConversion(Exception): - """Class for unit conversion errors""" - - -_units_output_expression = re.compile( - r"(?P(?:[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?))(?: (?P.+))?" -) - - -def linear_fit(x, y): - """ - Perform a linear fit between x,y, returning the average error for each data - point as well. This is written this way so as to not add a numpy dependency - """ - n = len(x) - xx = sum([x**2 for x in x]) - sum(x) ** 2 / n - xy = sum(map(lambda x, y: x * y, x, y)) - sum(x) * sum(y) / n - a, b = sum(y) / n - xy / xx * sum(x) / n, xy / xx - yhat = [a + b * x for x in x] - yerr = math.sqrt(sum(map(lambda y, yh: (y - yh) ** 2 / y**2, y, yhat)) / n) - return a, b, yerr - - -def islinear(unit, to_unit=None): - """ - Detect if the conversion from `unit` to `to_unit` is a linear map. Apparently - the units utility is float precision, so if error is less than 1e-7 we know - it is linear. - """ - x = [100 ** (1e-2 * (i - 50)) for i in range(20)] - y = convert_list(x, unit, to_unit=to_unit, dofit=False)[0] - a, b, err = linear_fit(x, y) - - a = convert_list(0, unit, to_unit=to_unit, dofit=False)[0] - b = convert_list(1, unit, to_unit=to_unit, dofit=False)[0] - a - return a, b, err < 1e-7 - - -def convert_units(from_value, from_unit, wanted_unit=None, suppress_unit=False): - """Works with 'units' utility""" - from_sign = from_value < 0 - from_value = str(abs(from_value)) - from_unit = str(from_unit) - - TEMPERATURE_FUNCTION_UNITS = ["degC", "tempC", "degF", "tempF"] - - if from_unit in TEMPERATURE_FUNCTION_UNITS: - args = [ - "units", - "-o", - "%1.15e", - "-qt1", - "".join((from_unit, "(", from_value, ")")), - ] - - else: - args = ["units", "-o", "%1.15e", "-qt1", " ".join((from_value, from_unit))] - - if wanted_unit: - args.append(wanted_unit) - - try: - output = subprocess.check_output(args).decode("utf-8") - except subprocess.CalledProcessError: - tag = wanted_unit if wanted_unit else "SI" - raise UnitConversion( - "Error in unit conversion of {} {} to {}".format(from_value, from_unit, tag) - ) - - matches = _units_output_expression.match(output).groupdict(None) - out = ((-1) ** from_sign * float(matches["value"]), matches["unit"] or wanted_unit) - - if suppress_unit: - return out[0] - return out - - -# Set default behavior -convert = convert_units - - -def convert_list(x, from_unit, to_unit=None, convert=convert, dofit=True): - """Thread conversion over a list, or list of lists""" - # Need a list for scoping reasons - - # Constant shortcut - if from_unit in (1, 1.0, "1"): - to_unit = "1" - - # get the SI unit if none provided - if to_unit is None: - _, to_unit = convert(1.0, from_unit) - - def convert_inner(x, fit=None): - if isinstance(x, (list, tuple)): - return type(x)(convert_inner(i, fit=fit) for i in x) - else: - if to_unit == "1": - return float(x) - else: - if fit is not None: - return fit[0] + fit[1] * x - return float(convert(x, from_unit, to_unit, suppress_unit=True)) - - # setup the linear fit if we are requested to simplify - fit = None - if dofit and isinstance(x, (list, tuple)) and len(x) > 20: - a, b, linear = islinear(from_unit, to_unit) - fit = (a, b) if linear else None - - output = convert_inner(x, fit=fit) - return output, to_unit - - -def add_si_units(doc, convert=convert): - """Given a document, add all of the appropriate si-units fields""" - if isinstance(doc, dict): - # check for a source-unit to defined a value with units - if "source-unit" in doc: - # we've found a place to add - assert "source-value" in doc, "Badly formed doc" - o_value = doc.get("source-value", None) - o_unit = doc.get("source-unit", None) - - if o_value is None: - raise UnitConversion("No source-value provided") - if o_unit is None: - raise UnitConversion("No source-unit provided") - - # convert the units and insert - value, unit = convert_list(o_value, o_unit, convert=convert) - si_dict = {"si-unit": unit, "si-value": value} - doc = doc.copy() - doc.update(si_dict) - return doc - else: - # recurse - return type(doc)( - (key, add_si_units(value)) for key, value in list(doc.items()) - ) - - elif isinstance(doc, (list, tuple)): - return type(doc)(add_si_units(x) for x in doc) - - return doc diff --git a/docker/config/openkim-pipeline-excerpts b/docker/config/openkim-pipeline-excerpts new file mode 160000 index 0000000..96691ec --- /dev/null +++ b/docker/config/openkim-pipeline-excerpts @@ -0,0 +1 @@ +Subproject commit 96691ec0aab6e786b49f962ee3e60a9626c06b14 From 703fcaffbbdfb21139835264e6800a8ea5bd8547 Mon Sep 17 00:00:00 2001 From: Claire Waters Date: Mon, 24 Aug 2026 14:40:41 -0500 Subject: [PATCH 2/3] MAINT: update openkim-pipeline-excerpts --- docker/config/openkim-pipeline-excerpts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/config/openkim-pipeline-excerpts b/docker/config/openkim-pipeline-excerpts index 96691ec..c6e977a 160000 --- a/docker/config/openkim-pipeline-excerpts +++ b/docker/config/openkim-pipeline-excerpts @@ -1 +1 @@ -Subproject commit 96691ec0aab6e786b49f962ee3e60a9626c06b14 +Subproject commit c6e977a6416ef9c04fa7eabc90448918f9993477 From 34bcd6a1d3422daedf93b12a5bbb787e7c339855 Mon Sep 17 00:00:00 2001 From: Claire Waters <39541408+ClaireWaters@users.noreply.github.com> Date: Mon, 24 Aug 2026 16:03:54 -0500 Subject: [PATCH 3/3] Update docker-publish-and-test.yml Recurse submodules in build-and-cache-minimal to ensure openkim-pipeline-excerpts is available. --- .github/workflows/docker-publish-and-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docker-publish-and-test.yml b/.github/workflows/docker-publish-and-test.yml index fe43d94..7fe9718 100644 --- a/.github/workflows/docker-publish-and-test.yml +++ b/.github/workflows/docker-publish-and-test.yml @@ -161,6 +161,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v2 + with: + submodules: recursive - name: Restore sys docker image cache uses: actions/cache@v3