diff --git a/podpac/core/algorithm/algorithm.py b/podpac/core/algorithm/algorithm.py
index a2bb8e9b..b5940e40 100644
--- a/podpac/core/algorithm/algorithm.py
+++ b/podpac/core/algorithm/algorithm.py
@@ -18,6 +18,8 @@ from podpac.core.node import NodeException
 from podpac.core.node import COMMON_NODE_DOC
 from podpac.core.node import node_eval
 from podpac.core.utils import common_doc
+from podpac.core.settings import settings
+from podpac.core.managers.multi_threading import thread_manager
 
 COMMON_DOC = COMMON_NODE_DOC.copy()
 
@@ -67,8 +69,40 @@ class Algorithm(Node):
         self._requested_coordinates = coordinates
 
         inputs = {}
-        for key, node in self._inputs.items():
-            inputs[key] = node.eval(coordinates)
+
+        if settings["MULTITHREADING"]:
+            n_threads = thread_manager.request_n_threads(len(self._inputs))
+            if n_threads == 1:
+                thread_manager.release_n_threads(n_threads)
+        else:
+            n_threads = 0
+
+        if settings["MULTITHREADING"] and n_threads > 1:
+            # Create a function for each thread to execute asynchronously
+            def f(node):
+                return node.eval(coordinates)
+
+            # Create pool of size n_threads, note, this may be created from a sub-thread (i.e. not the main thread)
+            pool = thread_manager.get_thread_pool(processes=n_threads)
+
+            # Evaluate nodes in parallel/asynchronously
+            results = [pool.apply_async(f, [node]) for node in self._inputs.values()]
+
+            # Collect the results in dictionary
+            for key, res in zip(self._inputs.keys(), results):
+                inputs[key] = res.get()
+
+            # This prevents any more tasks from being submitted to the pool, and will close the workers one done
+            pool.close()
+
+            # Release these number of threads back to the thread pool
+            thread_manager.release_n_threads(n_threads)
+            self._multi_threaded = True
+        else:
+            # Evaluate nodes in serial
+            for key, node in self._inputs.items():
+                inputs[key] = node.eval(coordinates)
+            self._multi_threaded = False
 
         # accumulate output coordinates
         coords_list = [Coordinates.from_xarray(a.coords, crs=a.attrs.get("crs")) for a in inputs.values()]
diff --git a/podpac/core/algorithm/generic.py b/podpac/core/algorithm/generic.py
index 5c1c8d94..5d7a8de5 100644
--- a/podpac/core/algorithm/generic.py
+++ b/podpac/core/algorithm/generic.py
@@ -97,6 +97,8 @@ class Arithmetic(GenericInputs):
         f_locals = dict(zip(fields, res))
 
         try:
+            import numexpr.evaluate  # Needed for some systems to get around lazy_module issues
+
             result = ne.evaluate(eqn, f_locals)
         except (NotImplementedError, ImportError):
             result = eval(eqn, f_locals)
diff --git a/podpac/core/compositor.py b/podpac/core/compositor.py
index 04cea0ec..bae3ce9d 100644
--- a/podpac/core/compositor.py
+++ b/podpac/core/compositor.py
@@ -5,7 +5,6 @@ Compositor Summary
 
 from __future__ import division, unicode_literals, print_function, absolute_import
 
-from multiprocessing.pool import ThreadPool
 import numpy as np
 import traitlets as tl
 
@@ -20,6 +19,7 @@ from podpac.core.node import node_eval
 from podpac.core.data.datasource import COMMON_DATA_DOC
 from podpac.core.data.interpolation import interpolation_trait
 from podpac.core.utils import trait_is_defined
+from podpac.core.managers.multi_threading import thread_manager
 
 COMMON_COMPOSITOR_DOC = COMMON_DATA_DOC.copy()  # superset of COMMON_NODE_DOC
 
@@ -219,27 +219,45 @@ class Compositor(Node):
                 nc = merge_dims([Coordinates(np.atleast_1d(c), dims=[coords_dim]), self.shared_coordinates])
 
                 if trait_is_defined(s, "native_coordinates") is False:
-                    s.set_trait('native_coordinates', nc)
+                    s.set_trait("native_coordinates", nc)
 
         if settings["MULTITHREADING"]:
+            n_threads = thread_manager.request_n_threads(len(src_subset))
+            if n_threads == 1:
+                thread_manager.release_n_threads(n_threads)
+        else:
+            n_threads = 0
+
+        if settings["MULTITHREADING"] and n_threads > 1:
             # TODO pool of pre-allocated scratch space
             # TODO: docstring?
             def f(src):
                 return src.eval(coordinates)
 
-            pool = ThreadPool(processes=settings.get("N_THREADS", 10))
+            # Create pool of size n_threads, note, this may be created from a sub-thread (i.e. not the main thread)
+            pool = thread_manager.get_thread_pool(processes=n_threads)
+
+            # Evaluate nodes in parallel/asynchronously
             results = [pool.apply_async(f, [src]) for src in src_subset]
 
+            # Yield results as they are being requested, blocking when the thread is not finished
             for src, res in zip(src_subset, results):
                 yield res.get()
                 # src._output = None # free up memory
 
+            # This prevents any more tasks from being submitted to the pool, and will close the workers one done
+            pool.close()
+
+            # Release these number of threads back to the thread pool
+            thread_manager.release_n_threads(n_threads)
+            self._multi_threaded = True
         else:
             output = None  # scratch space
             for src in src_subset:
                 output = src.eval(coordinates, output)
                 yield output
                 # output[:] = np.nan
+            self._multi_threaded = False
 
     @node_eval
     @common_doc(COMMON_COMPOSITOR_DOC)
diff --git a/podpac/core/managers/multi_threading.py b/podpac/core/managers/multi_threading.py
new file mode 100644
index 00000000..d8b4a8b5
--- /dev/null
+++ b/podpac/core/managers/multi_threading.py
@@ -0,0 +1,86 @@
+"""
+Module for dealing with multi-threaded execution. 
+
+This is used to ensure that the total number of threads specified in the settings is not exceeded. 
+
+"""
+
+from __future__ import division, unicode_literals, print_function, absolute_import
+
+from multiprocessing import Lock
+from multiprocessing.pool import ThreadPool
+
+from podpac.core.settings import settings
+
+DEFAULT_N_THREADS = 10
+
+
+class ThreadManager(object):
+    """ This is a singleton class that keeps track of the total number of threads used in an application. 
+    """
+
+    _lock = Lock()
+    cache_lock = Lock()
+    _n_threads_used = 0
+    __instance = None
+
+    def __new__(cls):
+        if ThreadManager.__instance is None:
+            ThreadManager.__instance = object.__new__(cls)
+        return ThreadManager.__instance
+
+    def request_n_threads(self, n):
+        """ Returns the number of threads allowed for a pool taking into account all other threads application, as 
+        specified by podpac.settings["N_THREADS"].
+        
+        Parameters
+        -----------
+        n : int
+            Number of threads requested by operation
+            
+        Returns
+        --------
+        int 
+            Number of threads a pool may use. Note, this may be less than or equal to n, and may be 0. 
+        """
+        with self._lock:
+            available = max(0, settings.get("N_THREADS", DEFAULT_N_THREADS) - self._n_threads_used)
+            claimed = min(available, n)
+            self._n_threads_used += claimed
+            return claimed
+
+    def release_n_threads(self, n):
+        """ This releases the number of threads specified. 
+        
+        Parameters
+        ------------
+        n : int
+            Number of threads to be released
+            
+        Returns
+        --------
+        int
+            Number of threads available after releases 'n' threads
+        """
+        with self._lock:
+            self._n_threads_used = max(0, self._n_threads_used - n)
+            available = max(0, settings.get("N_THREADS", DEFAULT_N_THREADS) - self._n_threads_used)
+            return available
+
+    def get_thread_pool(self, processes):
+        """ Creates a threadpool that can be used to run jobs in parallel.
+        
+        Parameters
+        -----------
+        processes : int
+            The number of threads or workers that will be part of the pool
+            
+        Returns
+        --------
+        multiprocessing.ThreadPool
+            An instance of the ThreadPool class        
+        """
+        return ThreadPool(processes=processes)
+
+
+thread_manager = ThreadManager()
diff --git a/podpac/core/node.py b/podpac/core/node.py
index 940a9987..77b22106 100644
--- a/podpac/core/node.py
+++ b/podpac/core/node.py
@@ -25,6 +25,7 @@ from podpac.core.utils import _get_query_params_from_url, _get_from_url, _get_pa
 from podpac.core.coordinates import Coordinates
 from podpac.core.style import Style
 from podpac.core.cache import CacheCtrl, get_default_cache_ctrl, S3CacheStore, make_cache_ctrl
+from podpac.core.managers.multi_threading import thread_manager
 
 
 COMMON_NODE_DOC = {
@@ -136,6 +137,8 @@ class Node(tl.HasTraits):
     _requested_coordinates = tl.Instance(Coordinates, allow_none=True)
     _output = tl.Instance(UnitsDataArray, allow_none=True)
     _from_cache = tl.Bool(allow_none=True, default_value=None)
+    # Flag that is True if the Node was run multi-threaded, or None if the question doesn't apply
+    _multi_threaded = tl.Bool(allow_none=True, default_value=None)
 
     def __init__(self, **kwargs):
         """ Do not overwrite me """
@@ -478,11 +481,11 @@ class Node(tl.HasTraits):
         NodeException
             Cached data already exists (and overwrite is False)
         """
-
         if not overwrite and self.has_cache(key, coordinates=coordinates):
             raise NodeException("Cached data already exists for key '%s' and coordinates %s" % (key, coordinates))
 
-        self.cache_ctrl.put(self, data, key, coordinates=coordinates, update=overwrite)
+        with thread_manager.cache_lock:
+            self.cache_ctrl.put(self, data, key, coordinates=coordinates, update=overwrite)
 
     def has_cache(self, key, coordinates=None):
         """
@@ -500,7 +503,8 @@ class Node(tl.HasTraits):
         bool
             True if there is cached data for this node, key, and coordinates.
         """
-        return self.cache_ctrl.has(self, key, coordinates=coordinates)
+        with thread_manager.cache_lock:
+            return self.cache_ctrl.has(self, key, coordinates=coordinates)
 
     def rem_cache(self, key, coordinates=None, mode=None):
         """
@@ -807,6 +811,7 @@ def node_eval(fn):
             self._requested_coordinates = coordinates
         key = cache_key
         cache_coordinates = coordinates.transpose(*sorted(coordinates.dims))  # order agnostic caching
+
         if self.has_cache(key, cache_coordinates) and not self.cache_update:
             data = self.get_cache(key, cache_coordinates)
             if output is not None:
diff --git a/podpac/datalib/gfs.py b/podpac/datalib/gfs.py
index 1f7969da..9386d2e1 100644
--- a/podpac/datalib/gfs.py
+++ b/podpac/datalib/gfs.py
@@ -109,7 +109,7 @@ class GFS(DataSource):
         base_time = datetime.datetime.strptime("%s %s" % (self.date, self.hour), "%Y%m%d %H%M")
         forecast_times = [base_time + datetime.timedelta(hours=int(h)) for h in self.forecasts]
         tc = Coordinates([[dt.strftime("%Y-%m-%d %H:%M") for dt in forecast_times]], dims=["time"])
-        self.set_trait('native_coordinates', merge_dims([nc, tc]))
+        self.set_trait("native_coordinates", merge_dims([nc, tc]))
 
     def get_data(self, coordinates, coordinates_index):
         data = self.create_output_array(coordinates)
@@ -125,13 +125,13 @@ class GFSLatest(GFS):
         now = datetime.datetime.now()
 
         # date
-        self.set_trait('date', now.strftime("%Y%m%d"))
+        self.set_trait("date", now.strftime("%Y%m%d"))
 
         # hour
         prefix = "%s/%s/%s/" % (self.parameter, self.level, self.date)
         objs = bucket.objects.filter(Prefix=prefix)
         hours = set(obj.key.split("/")[3] for obj in objs)
         if hours:
-            self.set_trait('hour', max(hours))
+            self.set_trait("hour", max(hours))
 
         super(GFSLatest, self).init()

ENH: Add multithreading to Algorithm Node
**Description**
We want algorithms nodes to evaluate their inputs in parallel (multi-threaded) so that we don't have to wait for latency from S3 for all nodes in serial. 

**Describe the solution you'd like**

Use an implementation similar to the Compositor nodes. Need to wait for all evaluated nodes to return before continuing with the algorithm part... 

**Additional context**
This is critical to get the Drought Monitor working quickly.
diff --git a/podpac/core/algorithm/test/test_algorithm.py b/podpac/core/algorithm/test/test_algorithm.py
index 3647dc91..64641349 100644
--- a/podpac/core/algorithm/test/test_algorithm.py
+++ b/podpac/core/algorithm/test/test_algorithm.py
@@ -4,6 +4,7 @@ import warnings
 
 import pytest
 from collections import OrderedDict
+import numpy as np
 
 import podpac
 from podpac.core.algorithm.utility import Arange
@@ -40,3 +41,92 @@ class TestAlgorithm(object):
         assert "B" in d["inputs"]
 
         # TODO value of d['inputs']['A'], etc
+
+    def test_multi_threading(self):
+        coords = podpac.Coordinates([[1, 2, 3]], ["lat"])
+        node1 = Arithmetic(A=Arange(), B=Arange(), eqn="A+B")
+        node2 = Arithmetic(A=node1, B=Arange(), eqn="A+B")
+
+        with podpac.settings:
+            podpac.settings["MULTITHREADING"] = True
+            podpac.settings["N_THREADS"] = 8
+            podpac.settings["CACHE_OUTPUT_DEFAULT"] = False
+            podpac.settings["DEFAULT_CACHE"] = []
+            podpac.settings["RAM_CACHE_ENABLED"] = False
+            podpac.settings.set_unsafe_eval(True)
+
+            omt = node2.eval(coords)
+
+        with podpac.settings:
+            podpac.settings["MULTITHREADING"] = False
+            podpac.settings["CACHE_OUTPUT_DEFAULT"] = False
+            podpac.settings["DEFAULT_CACHE"] = []
+            podpac.settings["RAM_CACHE_ENABLED"] = False
+            podpac.settings.set_unsafe_eval(True)
+
+            ost = node2.eval(coords)
+
+        np.testing.assert_array_equal(omt, ost)
+
+    def test_multi_threading_cache_race(self):
+        coords = podpac.Coordinates([np.linspace(0, 1, 1024)], ["lat"])
+        with podpac.settings:
+            podpac.settings["MULTITHREADING"] = True
+            podpac.settings["N_THREADS"] = 3
+            podpac.settings["CACHE_OUTPUT_DEFAULT"] = True
+            podpac.settings["DEFAULT_CACHE"] = ["ram"]
+            podpac.settings["RAM_CACHE_ENABLED"] = True
+            podpac.settings.set_unsafe_eval(True)
+            A = Arithmetic(A=Arange(), eqn="A**2")
+            B = Arithmetic(A=Arange(), eqn="A**2")
+            C = Arithmetic(A=Arange(), eqn="A**2")
+            D = Arithmetic(A=Arange(), eqn="A**2")
+            E = Arithmetic(A=Arange(), eqn="A**2")
+            F = Arithmetic(A=Arange(), eqn="A**2")
+
+            node2 = Arithmetic(A=A, B=B, C=C, D=D, E=E, F=F, eqn="A+B+C+D+E+F")
+
+            om = node2.eval(coords)
+
+            from_cache = [n._from_cache for n in node2.inputs.values()]
+
+            assert sum(from_cache) > 0
+
+    def test_multi_threading_stress_nthreads(self):
+        coords = podpac.Coordinates([np.linspace(0, 1, 4)], ["lat"])
+
+        A = Arithmetic(A=Arange(), eqn="A**0")
+        B = Arithmetic(A=Arange(), eqn="A**1")
+        C = Arithmetic(A=Arange(), eqn="A**2")
+        D = Arithmetic(A=Arange(), eqn="A**3")
+        E = Arithmetic(A=Arange(), eqn="A**4")
+        F = Arithmetic(A=Arange(), eqn="A**5")
+
+        node2 = Arithmetic(A=A, B=B, C=C, D=D, E=E, F=F, eqn="A+B+C+D+E+F")
+        node3 = Arithmetic(A=A, B=B, C=C, D=D, E=E, F=F, G=node2, eqn="A+B+C+D+E+F+G")
+
+        with podpac.settings:
+            podpac.settings["MULTITHREADING"] = True
+            podpac.settings["N_THREADS"] = 8
+            podpac.settings["CACHE_OUTPUT_DEFAULT"] = False
+            podpac.settings["DEFAULT_CACHE"] = []
+            podpac.settings["RAM_CACHE_ENABLED"] = False
+            podpac.settings.set_unsafe_eval(True)
+
+            omt = node3.eval(coords)
+
+        assert node3._multi_threaded
+        assert not node2._multi_threaded
+
+        with podpac.settings:
+            podpac.settings["MULTITHREADING"] = True
+            podpac.settings["N_THREADS"] = 9  # 2 threads available after first 7
+            podpac.settings["CACHE_OUTPUT_DEFAULT"] = False
+            podpac.settings["DEFAULT_CACHE"] = []
+            podpac.settings["RAM_CACHE_ENABLED"] = False
+            podpac.settings.set_unsafe_eval(True)
+
+            omt = node3.eval(coords)
+
+        assert node3._multi_threaded
+        assert node2._multi_threaded

pip install -e .[devall]
parse_log_pytest
pytest
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y gdal-bin libgdal-dev
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
affine==2.4.0
aiofiles==22.1.0
aiosqlite==0.19.0
alabaster==0.7.13
anyio==3.7.1
appdirs==1.4.4
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.2.3
asciitree==0.3.3
astroid==2.15.8
attrs @ file:///croot/attrs_1668696182826/work
awscli==1.31.13
Babel==2.14.0
backcall==0.2.0
beautifulsoup4==4.13.3
black==23.3.0
bleach==6.0.0
boto3==1.33.13
botocore==1.33.13
branca==0.8.1
cached-property==1.5.2
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
cfgv==3.3.1
charset-normalizer==3.4.1
click==8.1.8
click-plugins==1.1.1
cligj==0.7.2
cloudpickle==2.2.1
colorama==0.4.4
comm==0.1.4
commonmark==0.9.1
coverage==6.5.0
coveralls==3.3.1
cycler==0.11.0
dask==2022.2.0
debugpy==1.7.0
decorator==5.1.1
defusedxml==0.7.1
dill==0.3.7
distlib==0.3.9
docopt==0.6.2
docutils==0.16
entrypoints==0.4
exceptiongroup==1.2.2
execnet==2.0.2
fasteners==0.19
fastjsonschema==2.21.1
filelock==3.12.2
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
fonttools==4.38.0
fqdn==1.5.1
fsspec==2023.1.0
h5py==3.8.0
identify==2.5.24
idna==3.10
imagesize==1.4.1
importlib-metadata==6.7.0
importlib-resources==5.12.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
intake==0.6.6
ipykernel==6.16.2
ipyleaflet==0.18.2
ipympl==0.9.3
ipython==7.34.0
ipython-genutils==0.2.0
ipywidgets==8.1.5
isoduration==20.11.0
isort==5.11.5
jedi==0.19.2
Jinja2==3.1.6
jmespath==1.0.1
json5==0.9.16
jsonpointer==3.0.0
jsonschema==4.17.3
jupyter-events==0.6.3
jupyter-server==1.24.0
jupyter-ydoc==0.2.5
jupyter_client==7.4.9
jupyter_core==4.12.0
jupyter_server_fileid==0.9.3
jupyter_server_ydoc==0.8.0
jupyterlab==3.6.8
jupyterlab-pygments==0.2.2
jupyterlab_server==2.24.0
jupyterlab_widgets==3.0.13
kiwisolver==1.4.5
lazy-import==0.2.2
lazy-object-proxy==1.9.0
livereload==2.7.1
locket==1.0.0
lxml==5.3.1
MarkupSafe==2.1.5
matplotlib==3.5.3
matplotlib-inline==0.1.6
mccabe==0.7.0
mistune==3.0.2
msgpack==1.0.5
mypy-extensions==1.0.0
nbclassic==1.2.0
nbclient==0.7.4
nbconvert==7.6.0
nbformat==5.8.0
nest-asyncio==1.6.0
nodeenv==1.9.1
nodejs==0.1.1
notebook==6.5.7
notebook_shim==0.2.4
numcodecs==0.10.2
numexpr==2.8.6
numpy==1.21.6
optional-django==0.1.0
packaging @ file:///croot/packaging_1671697413597/work
pandas==1.3.5
pandocfilters==1.5.1
parso==0.8.4
partd==1.4.1
pathspec==0.11.2
pexpect==4.9.0
pickleshare==0.7.5
Pillow==9.5.0
Pint==0.18
pkgutil_resolve_name==1.3.10
platformdirs==4.0.0
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
-e git+https://github.com/creare-com/podpac.git@3c52a2bedc5d3db33f56e1cf56895395958cd71b#egg=podpac
pre-commit==2.21.0
prometheus-client==0.17.1
prompt_toolkit==3.0.48
psutil==7.0.0
ptyprocess==0.7.0
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyasn1==0.5.1
pycparser==2.21
pydap==3.4.1
Pygments==2.17.2
pylint==2.17.7
pyparsing==3.1.4
pyproj==3.2.1
pyrsistent==0.19.3
pytest==7.1.2
pytest-asyncio==0.21.2
pytest-cov==4.1.0
pytest-html==3.2.0
pytest-metadata==3.0.0
pytest-mock==3.11.1
pytest-remotedata==0.4.1
pytest-xdist==3.5.0
python-dateutil==2.9.0.post0
python-json-logger==3.0.1
pytz==2025.2
PyYAML==6.0.1
pyzmq==26.2.1
rasterio==1.2.10
recommonmark==0.7.1
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rsa==4.7.2
s3fs==0.4.2
s3transfer==0.8.2
scipy==1.7.3
Send2Trash==1.8.3
six==1.17.0
sniffio==1.3.1
snowballstemmer==2.2.0
snuggs==1.4.7
soupsieve==2.4.1
Sphinx==5.3.0
sphinx-autobuild==2021.3.14
sphinx-rtd-theme==2.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
terminado==0.17.1
tinycss2==1.2.1
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
tomlkit==0.12.5
toolz==0.12.1
tornado==6.2
traitlets==5.9.0
traittypes==0.2.1
typed-ast==1.5.5
typing_extensions==4.7.1
uri-template==1.3.0
urllib3==1.26.20
virtualenv==20.26.6
wcwidth==0.2.13
webcolors==1.13
webencodings==0.5.1
WebOb==1.8.9
websocket-client==1.6.1
widgetsnbextension==4.0.13
wrapt==1.16.0
xarray==0.20.2
xyzservices==2023.2.0
y-py==0.6.2
ypy-websocket==0.8.4
zarr==2.12.0
zipp @ file:///croot/zipp_1672387121353/work

name: podpac
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - affine==2.4.0
      - aiofiles==22.1.0
      - aiosqlite==0.19.0
      - alabaster==0.7.13
      - anyio==3.7.1
      - appdirs==1.4.4
      - argon2-cffi==23.1.0
      - argon2-cffi-bindings==21.2.0
      - arrow==1.2.3
      - asciitree==0.3.3
      - astroid==2.15.8
      - awscli==1.31.13
      - babel==2.14.0
      - backcall==0.2.0
      - beautifulsoup4==4.13.3
      - black==23.3.0
      - bleach==6.0.0
      - boto3==1.33.13
      - botocore==1.33.13
      - branca==0.8.1
      - cached-property==1.5.2
      - cffi==1.15.1
      - cfgv==3.3.1
      - charset-normalizer==3.4.1
      - click==8.1.8
      - click-plugins==1.1.1
      - cligj==0.7.2
      - cloudpickle==2.2.1
      - colorama==0.4.4
      - comm==0.1.4
      - commonmark==0.9.1
      - coverage==6.5.0
      - coveralls==3.3.1
      - cycler==0.11.0
      - dask==2022.2.0
      - debugpy==1.7.0
      - decorator==5.1.1
      - defusedxml==0.7.1
      - dill==0.3.7
      - distlib==0.3.9
      - docopt==0.6.2
      - docutils==0.16
      - entrypoints==0.4
      - exceptiongroup==1.2.2
      - execnet==2.0.2
      - fasteners==0.19
      - fastjsonschema==2.21.1
      - filelock==3.12.2
      - fonttools==4.38.0
      - fqdn==1.5.1
      - fsspec==2023.1.0
      - h5py==3.8.0
      - identify==2.5.24
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==6.7.0
      - importlib-resources==5.12.0
      - intake==0.6.6
      - ipykernel==6.16.2
      - ipyleaflet==0.18.2
      - ipympl==0.9.3
      - ipython==7.34.0
      - ipython-genutils==0.2.0
      - ipywidgets==8.1.5
      - isoduration==20.11.0
      - isort==5.11.5
      - jedi==0.19.2
      - jinja2==3.1.6
      - jmespath==1.0.1
      - json5==0.9.16
      - jsonpointer==3.0.0
      - jsonschema==4.17.3
      - jupyter-client==7.4.9
      - jupyter-core==4.12.0
      - jupyter-events==0.6.3
      - jupyter-server==1.24.0
      - jupyter-server-fileid==0.9.3
      - jupyter-server-ydoc==0.8.0
      - jupyter-ydoc==0.2.5
      - jupyterlab==3.6.8
      - jupyterlab-pygments==0.2.2
      - jupyterlab-server==2.24.0
      - jupyterlab-widgets==3.0.13
      - kiwisolver==1.4.5
      - lazy-import==0.2.2
      - lazy-object-proxy==1.9.0
      - livereload==2.7.1
      - locket==1.0.0
      - lxml==5.3.1
      - markupsafe==2.1.5
      - matplotlib==3.5.3
      - matplotlib-inline==0.1.6
      - mccabe==0.7.0
      - mistune==3.0.2
      - msgpack==1.0.5
      - mypy-extensions==1.0.0
      - nbclassic==1.2.0
      - nbclient==0.7.4
      - nbconvert==7.6.0
      - nbformat==5.8.0
      - nest-asyncio==1.6.0
      - nodeenv==1.9.1
      - nodejs==0.1.1
      - notebook==6.5.7
      - notebook-shim==0.2.4
      - numcodecs==0.10.2
      - numexpr==2.8.6
      - numpy==1.21.6
      - optional-django==0.1.0
      - pandas==1.3.5
      - pandocfilters==1.5.1
      - parso==0.8.4
      - partd==1.4.1
      - pathspec==0.11.2
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pillow==9.5.0
      - pint==0.18
      - pkgutil-resolve-name==1.3.10
      - platformdirs==4.0.0
      - podpac==1.2.0+31.g3c52a2be
      - pre-commit==2.21.0
      - prometheus-client==0.17.1
      - prompt-toolkit==3.0.48
      - psutil==7.0.0
      - ptyprocess==0.7.0
      - pyasn1==0.5.1
      - pycparser==2.21
      - pydap==3.4.1
      - pygments==2.17.2
      - pylint==2.17.7
      - pyparsing==3.1.4
      - pyproj==3.2.1
      - pyrsistent==0.19.3
      - pytest-asyncio==0.21.2
      - pytest-cov==4.1.0
      - pytest-html==3.2.0
      - pytest-metadata==3.0.0
      - pytest-mock==3.11.1
      - pytest-remotedata==0.4.1
      - pytest-xdist==3.5.0
      - python-dateutil==2.9.0.post0
      - python-json-logger==3.0.1
      - pytz==2025.2
      - pyyaml==6.0.1
      - pyzmq==26.2.1
      - rasterio==1.2.10
      - recommonmark==0.7.1
      - requests==2.31.0
      - rfc3339-validator==0.1.4
      - rfc3986-validator==0.1.1
      - rsa==4.7.2
      - s3fs==0.4.2
      - s3transfer==0.8.2
      - scipy==1.7.3
      - send2trash==1.8.3
      - six==1.17.0
      - sniffio==1.3.1
      - snowballstemmer==2.2.0
      - snuggs==1.4.7
      - soupsieve==2.4.1
      - sphinx==5.3.0
      - sphinx-autobuild==2021.3.14
      - sphinx-rtd-theme==2.0.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - terminado==0.17.1
      - tinycss2==1.2.1
      - tomlkit==0.12.5
      - toolz==0.12.1
      - tornado==6.2
      - traitlets==5.9.0
      - traittypes==0.2.1
      - typed-ast==1.5.5
      - typing-extensions==4.7.1
      - uri-template==1.3.0
      - urllib3==1.26.20
      - virtualenv==20.26.6
      - wcwidth==0.2.13
      - webcolors==1.13
      - webencodings==0.5.1
      - webob==1.8.9
      - websocket-client==1.6.1
      - widgetsnbextension==4.0.13
      - wrapt==1.16.0
      - xarray==0.20.2
      - xyzservices==2023.2.0
      - y-py==0.6.2
      - ypy-websocket==0.8.4
      - zarr==2.12.0
prefix: /opt/conda/envs/podpac

podpac/core/algorithm/test/test_algorithm.py::TestAlgorithm::test_multi_threading_stress_nthreads
podpac/core/algorithm/test/test_algorithm.py::TestAlgorithm::test_not_implemented podpac/core/algorithm/test/test_algorithm.py::TestAlgorithm::test_base_definition podpac/core/algorithm/test/test_algorithm.py::TestAlgorithm::test_multi_threading podpac/core/algorithm/test/test_algorithm.py::TestAlgorithm::test_multi_threading_cache_race
podpac/core/algorithm/algorithm.py podpac/core/algorithm/generic.py podpac/core/compositor.py podpac/core/managers/multi_threading.py podpac/datalib/gfs.py podpac/core/node.py
podpac/core/algorithm/algorithm.py podpac/core/algorithm/generic.py podpac/core/compositor.py podpac/core/managers/multi_threading.py podpac/datalib/gfs.py podpac/core/node.py