CalebBell / thermo

Thermodynamics and Phase Equilibrium component of Chemical Engineering Design Library (ChEDL)
MIT License
594 stars 114 forks source link

ImportError: cannot import name 'horner' from 'chemicals.utils' #135

Closed rjplevin closed 1 year ago

rjplevin commented 1 year ago

Some versions of thermo have an import in the file joback.py:

from chemicals.utils import to num, horner, exp

It appears that this is dependent on a specific version of chemicals.utils since recent versions don't define horner in horner/utils/init.py. Chemicals, in turn, imports horner from fluids.numerics. I see that the latest version of thermo in master uses fluids.numerics, too. But my requirements are tying me to an earlier version of thermo, which apparently requires an earlier version of chemicals.

Any advice on versions to get this working would be greatly appreciated!

CalebBell commented 1 year ago

Hi Richard,

The error you experienced should only have happened if incompatible versions of fluids and chemicals were installed. The setup.py file is configured to depend on the correct version of fluids; and pip should follow it although it can be flaky from time to time.

Regardless, I've updated the libraries and the error will go away if you upgrade to fluids 1.0.24 and chemicals 1.1.4 and thermo 0.2.25.

Sincerely, Caleb

rjplevin commented 1 year ago

Thanks for indicating the compatible versions. A possibly related problem (in that it wasn't present previously) is that I now have multiple pint unit registries. My app is using thermosteam, thermo, chemical, and fluids. Thermosteam creates its own unit registry (which IMO is incorrect behavior for a library). The other three pkgs do

from pint import _DEFAULT_REGISTRY as u

which also bypasses the application registry. This uses the initial, statically allocated registry, but if the app sets an application registry, these two will be incompatible. I think this would work better:

from pint import application_registry as u

which is an object that stores the registry via set_application_registry() and returns it via get_application_registry().

The pint docs suggest that libraries call pint.get_application_registry() so the app (e.g., my code) can create one and call pint.set_application_registry(x) which will then be used by all the libraries, ensuring that values will all be commensurable.

Maybe there's a better way, but this seems right to me.