"KeyError dimensionless" error message from finam-formind/examples/formind.py

The formind.py script in the examples folder from finam-formind has created the following error message on my system. However there is a workaround by adding "fm.UNITS.define("dimensionless = 1")" after def run(): Please find attached the changed formind.py code with the workaround after the error message.

(base) C:\Users\haeffner\finam-formind>python examples/formind.py
2024-11-26 12:26:24,386 INFO: init composition                     - FINAM
2024-11-26 12:26:24,391 INFO: validate composition                 - FINAM
2024-11-26 12:26:24,392 INFO: connect components                   - FINAM
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
INFO: Default value of the number of simulated plots per hectars is used (switches.Maxplot = 25).
INFO: Default value for the hectar size is used (switches.Hectare = 10000.0).
INFO: Default value for the side length of a hectare is used (switches.Hecside = 100.0).
2024-11-26 12:26:27,305 INFO: validate components                  - FINAM
2024-11-26 12:26:27,517 INFO: run composition                      - FINAM
Traceback (most recent call last):
  File "C:\Users\haeffner\finam-formind\examples\formind.py", line 125, in <module>
    run()
  File "C:\Users\haeffner\finam-formind\examples\formind.py", line 121, in run
    composition.run(end_time=datetime(2025, 1, 1))
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\finam\schedule.py", line 253, in run
    updated = self._update_recursive(to_update)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\finam\schedule.py", line 309, in _update_recursive
    module.update()
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\finam\sdk\component.py", line 159, in update
    self._update()
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\finam_plot\time_series.py", line 354, in _update
    units = f" [{units}]" if units else ""
                ^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\facets\plain\unit.py", line 67, in __format__
    return self._REGISTRY.formatter.format_unit(self, spec)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\delegates\formatter\full.py", line 137, in format_unit
    return self.get_formatter(uspec).format_unit(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\delegates\formatter\_to_register.py", line 104, in format_unit
    return func(units, registry=self._registry)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\finam\data\cf_units.py", line 38, in short_formatter
    s = f"{unit:~D}"
          ^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\facets\plain\unit.py", line 67, in __format__
    return self._REGISTRY.formatter.format_unit(self, spec)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\delegates\formatter\full.py", line 137, in format_unit
    return self.get_formatter(uspec).format_unit(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\delegates\formatter\plain.py", line 90, in format_unit
    numerator, denominator = prepare_compount_unit(
                             ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\delegates\formatter\_compound_unit_helpers.py", line 298, in prepare_compount_unit
    numerator = sort_func(numerator, registry)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\delegates\formatter\_compound_unit_helpers.py", line 190, in sort_by_unit_name
    return sorted(items, key=lambda el: el[2])
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\delegates\formatter\_compound_unit_helpers.py", line 161, in to_symbol_exponent_name
    return registry._get_symbol(el[0]), el[1], el[0]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\haeffner\AppData\Roaming\Python\Python312\site-packages\pint\facets\plain\registry.py", line 708, in _get_symbol
    return self._units[name].symbol
           ~~~~~~~~~~~^^^^^^
  File "C:\ProgramData\anaconda3\Lib\collections\__init__.py", line 1015, in __getitem__
    return self.__missing__(key)            # support subclasses that define __missing__
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\collections\__init__.py", line 1007, in __missing__
    raise KeyError(key)
KeyError: 'dimensionless'

Workaround formind.py

from datetime import datetime, timedelta

import finam as fm
import numpy as np
# pip install finam_plot
from finam_plot import ImagePlot, StepTimeSeriesPlot

from finam_formind import Formind


def run():
    fm.UNITS.define("dimensionless = 1")
    
    start = datetime(2000, 1, 1)

    sw_info = fm.Info(
        time=start,
        grid=fm.UniformGrid(
            dims=(4, 5), spacing=(1000, 1000), data_location=fm.Location.POINTS
        ),
        units=fm.UNITS.dimensionless,
    )

    def soil_water(t):
        dt = (t - start).days
        v = 40.0 if dt / 365 < 10 else 10.0
        return fm.data.full(v, sw_info)

    sw_comp = fm.modules.CallbackGenerator(
        callbacks={"soil_water": (soil_water, sw_info)},
        start=start,
        step=timedelta(days=1),
    )

    formind_comp = Formind(
        start_year=start.year,
        par_file="examples/formind_parameters/beech_forest.par",
        inventory="forest_test.inv",
        climate_input=False,
    )

    soil_props_gen = fm.modules.StaticCallbackGenerator(
        callbacks={
            "field_capacity": (
                lambda: 40.0,
                fm.Info(time=None, grid=fm.NoGrid(), units=""),
            ),
            "permanent_wilting_point": (
                lambda: 20.0,
                fm.Info(time=None, grid=fm.NoGrid(), units=""),
            ),
        },
    )

    soil_water_csv = fm.modules.CsvWriter(
        inputs=["SW"],
        path="soil_water.csv",
        start=start,
        step=timedelta(days=1),
    )

    formind_plot = StepTimeSeriesPlot(
        inputs=["LAI"],
        start=start,
        step=timedelta(days=365),
    )

    sw_plot = StepTimeSeriesPlot(
        inputs=["SW_in"],
        start=start,
        step=timedelta(days=365),
    )

    fm_viewer = ImagePlot(vmin=0.0, vmax=4.0)

    composition = fm.Composition(
        [
            sw_comp,
            soil_props_gen,
            formind_comp,
            soil_water_csv,
            formind_plot,
            sw_plot,
            fm_viewer,
        ]
    )
    composition.initialize()

    # Model coupling

    (sw_comp.outputs["soil_water"] >> formind_comp.inputs["soil_water"])
    (
        soil_props_gen.outputs["field_capacity"]
        >> fm.adapters.ValueToGrid(grid=None)
        >> formind_comp.inputs["field_capacity"]
    )
    (
        soil_props_gen.outputs["permanent_wilting_point"]
        >> fm.adapters.ValueToGrid(grid=None)
        >> formind_comp.inputs["permanent_wilting_point"]
    )

    (
        sw_comp.outputs["soil_water"]
        >> fm.adapters.GridToValue(func=np.ma.mean)
        >> soil_water_csv.inputs["SW"]
    )

    (
        sw_comp.outputs["soil_water"]
        >> fm.adapters.GridToValue(func=np.ma.mean)
        >> fm.adapters.AvgOverTime()
        >> sw_plot.inputs["SW_in"]
    )
    (
        formind_comp.outputs["LAI"]
        >> fm.adapters.GridToValue(func=np.ma.mean)
        >> fm.adapters.LinearTime()
        >> formind_plot.inputs["LAI"]
    )
    _ = formind_comp.outputs["LAI"] >> fm_viewer.inputs["Grid"]

    composition.connect(start_time=start)
    composition.run(end_time=datetime(2025, 1, 1))


if __name__ == "__main__":
    run()