Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ jobs:
# ubuntu-latest runner home is /home/runner; matches $HOME/hera_unittest_data
# which is both bootstrap_unittest_data.sh's default and conftest's default.
TEST_HERA: /home/runner/hera_unittest_data
# Silence jupyter_client's platformdirs migration DeprecationWarning in test output
JUPYTER_PLATFORM_DIRS: "1"

services:
mongodb:
Expand Down
22 changes: 17 additions & 5 deletions hera/measurements/GIS/raster/topography.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def _open_raster(fheight):
import xarray as xr
import geopandas as gpd
from shapely.geometry import Point
from pyproj import Transformer


WSG84 = 4326
Expand Down Expand Up @@ -253,25 +254,36 @@ def convertPointsCRS(self, points, inputCRS, outputCRS, **kwargs):

if isinstance(points, np.ndarray):
if points.ndim == 1:
gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy([points[0]], [points[1]]))
x_vals, y_vals = [points[0]], [points[1]]
else:
gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy(points[:, 0], points[:, 1]))
x_vals, y_vals = points[:, 0], points[:, 1]

elif isinstance(points, pd.DataFrame):
x_col = kwargs.get("x", "x")
y_col = kwargs.get("y", "y")
gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy(points[x_col], points[y_col]))
x_vals, y_vals = points[x_col], points[y_col]

elif isinstance(points, list):
if len(points) == 1:
gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy([points[0][0]], [points[0][1]]))
x_vals, y_vals = [points[0][0]], [points[0][1]]
else:
gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy([x[0] for x in points], [x[1] for x in points]))
x_vals, y_vals = [x[0] for x in points], [x[1] for x in points]

else:
raise ValueError(f"Unsupported type: {type(points)}")

gdf = gpd.GeoDataFrame(geometry=gpd.points_from_xy(x_vals, y_vals))
gdf.set_crs(inputCRS, inplace=True)

# A single-point GeoDataFrame.to_crs() triggers a spurious pyproj
# "ndim > 0 to a scalar" DeprecationWarning on the pinned geopandas/pyproj
# versions (the array-transform path mishandles length-1 arrays), so route
# the single-point case through a scalar pyproj transform instead.
if len(gdf) == 1:
transformer = Transformer.from_crs(inputCRS, outputCRS, always_xy=True)
x_out, y_out = transformer.transform(list(x_vals)[0], list(y_vals)[0])
return gpd.GeoDataFrame(geometry=[Point(x_out, y_out)], crs=outputCRS)

return gdf.to_crs(outputCRS)

def create_xarray(self, minx, miny, maxx, maxy, dxdy=30, inputCRS=WSG84):
Expand Down
2 changes: 1 addition & 1 deletion hera/riskassessment/agents/effects/Calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def __init__(self,tenbergeCoefficient,breathingRate=10*ureg.L/ureg.min,**kwargs)
self.n = tenbergeCoefficient

def calculate(self,concentrationField,field,breathingRate=10*ureg.L/ureg.min,time="datetime",inUnits=None):
"""
r"""
Calculates the toxic load from a concentration field.
\begin{equation}
D(T) = \int_0^T C^n dt
Expand Down
4 changes: 3 additions & 1 deletion hera/riskassessment/agents/effects/InjuryLevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def calculateContours(self, toxicLoads, time="datetime", x="x", y="y"):
The data returns in km because itm is in km.

"""
curBackend = plt.get_backend()
curBackend = plt.get_backend()
plt.close('all')
plt.switch_backend("pdf")

if time in toxicLoads.dims:
Expand Down Expand Up @@ -138,6 +139,7 @@ def calculateContours(self, toxicLoads, time="datetime", x="x", y="y"):
retList.append(ret)

ret = None if ret.empty else pandas.concat(retList,ignore_index=True)
plt.close('all')
plt.switch_backend(curBackend)

return ret
Expand Down
8 changes: 4 additions & 4 deletions hera/simulations/gaussian/Meteorology.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def getAirTemperature(self, height):
default unit m
:return:
air temperature at C.
"""
r"""
return self._temperature - 6.5e-3 * tonumber(height, ureg.m) * ureg.K

def getAirDensity(self, height):
Expand All @@ -213,7 +213,7 @@ def getAirDensity(self, height):
default m
:return:
air density in kg/m**3
"""
r"""
P = unumToPint(self.getAirPressure(height)).m_as(ureg.mmHg)
T = unumToPint(self.getAirTemperature(height)).m_as(ureg.degC)

Expand Down Expand Up @@ -243,7 +243,7 @@ def _setPvalues(self):

:return:
The coefficient (dimensionless).
"""
r"""
if (self.z0 is None or self.stability is None):
return
pstab = self._pvalues[self.stability]
Expand All @@ -262,7 +262,7 @@ def getWindVelocity(self, height):
default units [m]
:return:
The wind velocity at the requested height.
"""
r"""
height = tonumber(height, ureg.m)
refHeight = tonumber(self.refHeight, ureg.m)
height = numpy.min([numpy.max([height, 0]), 300])
Expand Down
2 changes: 1 addition & 1 deletion hera/simulations/gaussian/gasCloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ class Continuous(object):
_FullKernel = None

def __init__(self,dt,kernelsize,timetofinish=10*ureg.min):
"""
r"""
Time to finish.
the time (min) it take to reach 0.1.

Expand Down
2 changes: 1 addition & 1 deletion hera/simulations/hydrodynamics/nearWallFlow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import numpy

class functionG:
"""
r"""
The G(Lambda,D) is defined implicitly (Eqn. 17.60, page 537):

\begin{equation}
Expand Down
10 changes: 5 additions & 5 deletions hera/simulations/openFoam/CLI.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,11 +490,11 @@ def stochasticLagrangian_source_makeEscapedMassFile(args):
data = data.interpolate()
newstr = "/*--------------------------------*- C++ -*----------------------------------*\n" \
"| ========= | |\n" \
"| \ / F ield | OpenFOAM: The Open Source CFD Toolbox |\n" \
"| \ / O peration | Version: dev |\n" \
"| \ / A nd | Web: www.OpenFOAM.org |\n" \
"| \/ M anipulation | |\n" \
"\*---------------------------------------------------------------------------*/\n" \
"| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |\n" \
"| \\ / O peration | Version: dev |\n" \
"| \\ / A nd | Web: www.OpenFOAM.org |\n" \
"| \\/ M anipulation | |\n" \
"\\*---------------------------------------------------------------------------*/\n" \
"FoamFile\n{ version 2.0;\n format ascii;\n class scalarField;\n object kinematicCloudPositions;\n}\n" \
f"// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n\n{len(data)}\n(\n"
for time in timesteps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13001,15 +13001,15 @@ def areastatistics():
plt.plot(x1,y1,'*r')
plt.plot(x2,y2,'or')
plt.plot(x3,y3,'+r')
plt.title('$\lambda_f$')
plt.title(r'$\lambda_f$')
plt.subplot(1,3,2)
plt.imshow(zilp, origin='lower', interpolation='nearest',extent=[xmin,xmax,ymin,ymax]) # jet, Paired
plt.colorbar()
plt.grid()
plt.plot(x1,y1,'*r')
plt.plot(x2,y2,'or')
plt.plot(x3,y3,'+r')
plt.title('$\lambda_p$')
plt.title(r'$\lambda_p$')
plt.subplot(1,3,3)
plt.imshow(zilh, origin='lower', interpolation='nearest',extent=[xmin,xmax,ymin,ymax], norm=matplotlib.colors.LogNorm()) # jet, Paired
plt.colorbar()
Expand Down
10 changes: 5 additions & 5 deletions hera/simulations/openFoam/lagrangian/LSM/toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,11 @@ def makeSource(self, x, y, z, nParticles, type="Point", fileName="kinematicCloud
"""
string = "/*--------------------------------*- C++ -*----------------------------------*\n " \
"| ========= | |\n" \
"| \ / F ield | OpenFOAM: The Open Source CFD Toolbox |\n" \
"| \ / O peration | Version: dev |\n" \
"| \ / A nd | Web: www.OpenFOAM.org |\n" \
"| \/ M anipulation | |\n" \
"\*---------------------------------------------------------------------------*/\n" \
"| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |\n" \
"| \\ / O peration | Version: dev |\n" \
"| \\ / A nd | Web: www.OpenFOAM.org |\n" \
"| \\/ M anipulation | |\n" \
"\\*---------------------------------------------------------------------------*/\n" \
"FoamFile\n{ version 2.0;\n format ascii;\n class vectorField;\n" \
" object kinematicCloudPositions;\n}\n" \
"// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n\n" \
Expand Down
10 changes: 5 additions & 5 deletions hera/simulations/openFoam/lagrangian/abstractLagrangianSolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,11 +679,11 @@ def writeParticlePositionFile(self, x, y, z, nParticles, dispersionName, type="P
"""
string = "/*--------------------------------*- C++ -*----------------------------------*\n " \
"| ========= | |\n" \
"| \ / F ield | OpenFOAM: The Open Source CFD Toolbox |\n" \
"| \ / O peration | Version: dev |\n" \
"| \ / A nd | Web: www.OpenFOAM.org |\n" \
"| \/ M anipulation | |\n" \
"\*---------------------------------------------------------------------------*/\n" \
"| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |\n" \
"| \\ / O peration | Version: dev |\n" \
"| \\ / A nd | Web: www.OpenFOAM.org |\n" \
"| \\/ M anipulation | |\n" \
"\\*---------------------------------------------------------------------------*/\n" \
"FoamFile\n{ version 2.0;\n format ascii;\n class vectorField;\n" \
" object kinematicCloudPositions;\n}\n" \
"// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n\n" \
Expand Down
10 changes: 5 additions & 5 deletions hera/simulations/openFoam/toberewritten/wrf2of.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,11 @@ def regrid3(data, factorxy, factorz):
lines.append('// built with wrf2of.py for '+outputdir)
lines.append("/*--------------------------------*- C++ -*----------------------------------*\'")
lines.append('| ========= | |')
lines.append('| \ / F ield | OpenFOAM: The Open Source CFD Toolbox |')
lines.append('| \ / O peration | Version: 10.0 |')
lines.append('| \ / A nd | Web: www.OpenFOAM.org |')
lines.append('| \/ M anipulation | |')
lines.append('\*---------------------------------------------------------------------------*/')
lines.append('| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |')
lines.append('| \\ / O peration | Version: 10.0 |')
lines.append('| \\ / A nd | Web: www.OpenFOAM.org |')
lines.append('| \\/ M anipulation | |')
lines.append('\\*---------------------------------------------------------------------------*/')
lines.append('FoamFile')
lines.append('{')
lines.append(' version 10.0;')
Expand Down
18 changes: 18 additions & 0 deletions hera/tests/test_logging_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import warnings

from hera.utils.logging import helpers


class TestGetDefaultLoggingConfig:
def test_no_deprecation_warning_when_creating_default_config(self, tmp_path, monkeypatch):
"""Loading the packaged default config must not use deprecated importlib.resources APIs."""
log_dir = tmp_path / "log"
log_dir.mkdir()
monkeypatch.setattr(helpers, "HERA_DEFAULT_LOG_DIR", log_dir)

with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
config = helpers.get_default_logging_config()

assert isinstance(config, dict)
assert (log_dir / "heraLogging.config").is_file()
27 changes: 27 additions & 0 deletions hera/tests/test_no_invalid_escapes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pathlib
import warnings

import pytest

HERA_ROOT = pathlib.Path(__file__).resolve().parents[1]


def _python_sources():
return sorted(HERA_ROOT.rglob("*.py"))


@pytest.mark.parametrize("path", _python_sources(), ids=lambda p: str(p.relative_to(HERA_ROOT)))
def test_no_invalid_escape_sequences(path):
"""Compiling every hera source file must not emit invalid-escape warnings.

Python <=3.11 emits DeprecationWarning, >=3.12 emits SyntaxWarning."""
source = path.read_text(encoding="utf-8", errors="replace")
with warnings.catch_warnings():
warnings.simplefilter("error", SyntaxWarning)
warnings.simplefilter("error", DeprecationWarning)
try:
compile(source, str(path), "exec")
except SyntaxError as err:
# Legacy files that never compiled (dead code) are out of scope here;
# they cannot emit escape warnings because they cannot be imported at all.
pytest.skip(f"pre-existing SyntaxError, file is not importable: {err.msg} (line {err.lineno})")
21 changes: 21 additions & 0 deletions hera/tests/test_topography.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,27 @@ def test_basic(self, topo_toolkit):
converted = topo_toolkit.convertPointsCRS(points, inputCRS=4326, outputCRS=2039)
assert converted.shape[0] == 2

def test_single_point_no_deprecation_warning(self, topo_toolkit):
"""A single-point conversion must not trigger pyproj's array-to-scalar
DeprecationWarning (issue971) — geopandas.to_crs() on a 1-row
GeoDataFrame mishandles length-1 arrays on the pinned pyproj/geopandas."""
import warnings

with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
converted = topo_toolkit.convertPointsCRS([(35.1, 33.85)], inputCRS=4326, outputCRS=2039)

assert converted.shape[0] == 1

def test_single_point_matches_multi_point_conversion(self, topo_toolkit):
"""The single-point fast path must produce the same result as the
general (multi-point) to_crs path for the same coordinate."""
single = topo_toolkit.convertPointsCRS([(35.1, 33.85)], inputCRS=4326, outputCRS=2039)
multi = topo_toolkit.convertPointsCRS([(35.1, 33.85), (36.05, 33.9)], inputCRS=4326, outputCRS=2039)

assert single.geometry.iloc[0].x == pytest.approx(multi.geometry.iloc[0].x)
assert single.geometry.iloc[0].y == pytest.approx(multi.geometry.iloc[0].y)


class TestCreateElevationSTL:
def test_basic(self, topo_toolkit):
Expand Down
2 changes: 1 addition & 1 deletion hera/utils/latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


class bibItem:
"""
r"""
Represents a single bib item.

\bibitem{paper2}
Expand Down
4 changes: 2 additions & 2 deletions hera/utils/logging/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging.config
import os.path
import pathlib
from importlib.resources import read_text
from importlib.resources import files


HERA_DEFAULT_LOG_DIR = pathlib.Path.home() / ".pyhera" / "log"
Expand Down Expand Up @@ -31,7 +31,7 @@ def get_default_logging_config(*, disable_existing_loggers: bool = False) -> dic
"""Load the default Hera logging configuration, creating it on first use."""
defaultLocalConfig = os.path.join(HERA_DEFAULT_LOG_DIR,'heraLogging.config')
if not os.path.isfile(defaultLocalConfig):
defaultConfig = read_text('hera.utils.logging', 'heraLogging.config')
defaultConfig = files('hera.utils.logging').joinpath('heraLogging.config').read_text()
with open(defaultLocalConfig,'w') as localConfig:
localConfig.write(defaultConfig)

Expand Down
Loading