Skip to content

Commit

Permalink
use dask.array functions instead of numpy (#367)
Browse files Browse the repository at this point in the history
* use dask.array functions instead of numpy

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
fneum and pre-commit-ci[bot] committed Sep 8, 2024
1 parent fec43a4 commit 74e961f
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 52 deletions.
6 changes: 4 additions & 2 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ Release Notes
#############


.. Upcoming Release
.. ================
Upcoming Release
================

* Use ``dask.array`` functions in favour of ``numpy`` functions.

Version 0.2.14
==============
Expand Down
23 changes: 12 additions & 11 deletions atlite/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import pandas as pd
import xarray as xr
from dask import compute, delayed
from dask.array import absolute, arccos, cos, maximum, mod, radians, sin, sqrt
from dask.diagnostics import ProgressBar
from numpy import pi
from scipy.sparse import csr_matrix

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -1005,23 +1007,23 @@ def convert_line_rating(
k = (
2.424e-2 + 7.477e-5 * (Tfilm - T0) - 4.407e-9 * (Tfilm - T0) ** 2
) # thermal conductivity
anglediff = ds["wnd_azimuth"] - np.deg2rad(psi)
Phi = np.abs(np.mod(anglediff + np.pi / 2, np.pi) - np.pi / 2)
anglediff = ds["wnd_azimuth"] - radians(psi)
Phi = absolute(mod(anglediff + pi / 2, pi) - pi / 2)
K = (
1.194 - np.cos(Phi) + 0.194 * np.cos(2 * Phi) + 0.368 * np.sin(2 * Phi)
1.194 - cos(Phi) + 0.194 * cos(2 * Phi) + 0.368 * sin(2 * Phi)
) # wind direction factor

Tdiff = Ts - Ta
qcf1 = K * (1.01 + 1.347 * reynold**0.52) * k * Tdiff # (3a) in [1]
qcf2 = K * 0.754 * reynold**0.6 * k * Tdiff # (3b) in [1]

qcf = np.maximum(qcf1, qcf2)
qcf = maximum(qcf1, qcf2)

# natural convection
qcn = 3.645 * np.sqrt(rho) * D**0.75 * Tdiff**1.25
qcn = 3.645 * sqrt(rho) * D**0.75 * Tdiff**1.25

# convection loss is the max between forced and natural
qc = np.maximum(qcf, qcn)
qc = maximum(qcf, qcn)

# 2. Radiated Loss
qr = 17.8 * D * epsilon * ((Ts / 100) ** 4 - (Ta / 100) ** 4)
Expand All @@ -1035,14 +1037,13 @@ def convert_line_rating(
solar_position = Position(ds["solar_altitude"], ds["solar_azimuth"])
else:
solar_position = SolarPosition(ds)
Phi_s = np.arccos(
np.cos(solar_position.altitude)
* np.cos((solar_position.azimuth) - np.deg2rad(psi))
Phi_s = arccos(
cos(solar_position.altitude) * cos((solar_position.azimuth) - radians(psi))
)

qs = alpha * Q * A * np.sin(Phi_s)
qs = alpha * Q * A * sin(Phi_s)

Imax = np.sqrt((qc + qr - qs) / R)
Imax = sqrt((qc + qr - qs) / R)
return Imax.min("spatial") if isinstance(Imax, xr.DataArray) else Imax


Expand Down
5 changes: 3 additions & 2 deletions atlite/csp.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import logging

import numpy as np
from dask.array import radians, sin

from atlite.pv.solar_position import SolarPosition

Expand Down Expand Up @@ -42,7 +43,7 @@ def calculate_dni(ds, solar_position=None, altitude_threshold=3.75):
solar_position = SolarPosition(ds)

# solar altitude expected in rad, convert degrees (easier to specifcy) to match
altitude_threshold = np.deg2rad(altitude_threshold)
altitude_threshold = radians(altitude_threshold)

# Sanitation of altitude values:
# Prevent high calculated DNI values during low solar altitudes (sunset / dawn)
Expand All @@ -53,6 +54,6 @@ def calculate_dni(ds, solar_position=None, altitude_threshold=3.75):

# Calculate DNI and remove NaNs introduced during altitude sanitation
# DNI is determined either by dividing by cos(azimuth) or sin(altitude)
dni = ds["influx_direct"] / np.sin(altitude)
dni = ds["influx_direct"] / sin(altitude)

return dni
5 changes: 3 additions & 2 deletions atlite/datasets/era5.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import numpy as np
import pandas as pd
import xarray as xr
from dask.array import arctan2, sqrt
from dask import compute, delayed
from numpy import atleast_1d

Expand Down Expand Up @@ -135,11 +136,11 @@ def get_data_wind(retrieval_params):
)
ds = _rename_and_clean_coords(ds)

ds["wnd100m"] = np.sqrt(ds["u100"] ** 2 + ds["v100"] ** 2).assign_attrs(
ds["wnd100m"] = sqrt(ds["u100"] ** 2 + ds["v100"] ** 2).assign_attrs(
units=ds["u100"].attrs["units"], long_name="100 metre wind speed"
)
# span the whole circle: 0 is north, π/2 is east, -π is south, 3π/2 is west
azimuth = np.arctan2(ds["u100"], ds["v100"])
azimuth = arctan2(ds["u100"], ds["v100"])
ds["wnd_azimuth"] = azimuth.where(azimuth >= 0, azimuth + 2 * np.pi)
ds = ds.drop_vars(["u100", "v100"])
ds = ds.rename({"fsr": "roughness"})
Expand Down
10 changes: 4 additions & 6 deletions atlite/pv/irradiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
import logging

import numpy as np
import pandas as pd
import xarray as xr
from numpy import cos, deg2rad, fmax, fmin, sin, sqrt
from dask.array import cos, fmax, fmin, radians, sin, sqrt

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -73,7 +71,7 @@ def DiffuseHorizontalIrrad(ds, solar_position, clearsky_model, influx):
)

# Set diffuse fraction to one when the sun isn't up
# fraction = fraction.where(sinaltitude >= sin(deg2rad(threshold))).fillna(1.0)
# fraction = fraction.where(sinaltitude >= sin(radians(threshold))).fillna(1.0)
# fraction = fraction.rename('fraction index')

return (influx * fraction).rename("diffuse horizontal")
Expand Down Expand Up @@ -110,7 +108,7 @@ def TiltedDiffuseIrrad(ds, solar_position, surface_orientation, direct, diffuse)
# fixup: clip all negative values (unclear why it gets negative)
# note: REatlas does not do the fixup
if logger.isEnabledFor(logging.WARNING):
if ((diffuse_t < 0.0) & (sinaltitude > sin(deg2rad(1.0)))).any():
if ((diffuse_t < 0.0) & (sinaltitude > sin(radians(1.0)))).any():
logger.warning(
"diffuse_t exhibits negative values above altitude threshold."
)
Expand Down Expand Up @@ -254,7 +252,7 @@ def clip(influx, influx_max):
# values, leading to big overall errors from the 1/sinaltitude factor.
# => Suppress irradiation below solar altitudes of 1 deg.

cap_alt = solar_position["altitude"] < deg2rad(altitude_threshold)
cap_alt = solar_position["altitude"] < radians(altitude_threshold)
result = result.where(~(cap_alt | (direct + diffuse <= 0.01)), 0)
result.attrs["units"] = "W m**-2"

Expand Down
33 changes: 17 additions & 16 deletions atlite/pv/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

import numpy as np
import xarray as xr
from numpy import cos, deg2rad, pi, sin
from dask.array import arccos, arcsin, arctan, cos, logical_and, radians, sin
from numpy import pi


def get_orientation(name, **params):
Expand Down Expand Up @@ -50,14 +51,14 @@ def make_latitude_optimal():
def latitude_optimal(lon, lat, solar_position):
slope = np.empty_like(lat.values)

below_25 = np.abs(lat.values) <= deg2rad(25)
below_50 = np.abs(lat.values) <= deg2rad(50)
below_25 = np.abs(lat.values) <= np.radians(25)
below_50 = np.abs(lat.values) <= np.radians(50)

slope[below_25] = 0.87 * np.abs(lat.values[below_25])
slope[~below_25 & below_50] = 0.76 * np.abs(
lat.values[~below_25 & below_50]
) + deg2rad(0.31)
slope[~below_50] = np.deg2rad(40.0)
) + np.radians(0.31)
slope[~below_50] = np.radians(40.0)

# South orientation for panels on northern hemisphere and vice versa
azimuth = np.where(lat.values < 0, 0, pi)
Expand All @@ -70,8 +71,8 @@ def latitude_optimal(lon, lat, solar_position):


def make_constant(slope, azimuth):
slope = deg2rad(slope)
azimuth = deg2rad(azimuth)
slope = radians(slope)
azimuth = radians(azimuth)

def constant(lon, lat, solar_position):
return dict(slope=slope, azimuth=azimuth)
Expand All @@ -80,7 +81,7 @@ def constant(lon, lat, solar_position):


def make_latitude(azimuth=180):
azimuth = deg2rad(azimuth)
azimuth = radians(azimuth)

def latitude(lon, lat, solar_position):
return dict(slope=lat, azimuth=azimuth)
Expand All @@ -100,8 +101,8 @@ def SurfaceOrientation(ds, solar_position, orientation, tracking=None):
tracking of one-axis trackers. No. NREL/TP-6A20-58891. National Renewable
Energy Lab.(NREL), Golden, CO (United States), 2013.
"""
lon = deg2rad(ds["lon"])
lat = deg2rad(ds["lat"])
lon = radians(ds["lon"])
lat = radians(ds["lat"])

orientation = orientation(lon, lat, solar_position)
surface_slope = orientation["slope"]
Expand All @@ -119,11 +120,11 @@ def SurfaceOrientation(ds, solar_position, orientation, tracking=None):
axis_azimuth = orientation[
"azimuth"
] # here orientation['azimuth'] refers to the azimuth of the tracker axis.
rotation = np.arctan(
rotation = arctan(
(cos(sun_altitude) / sin(sun_altitude)) * sin(sun_azimuth - axis_azimuth)
)
surface_slope = abs(rotation)
surface_azimuth = axis_azimuth + np.arcsin(
surface_azimuth = axis_azimuth + arcsin(
sin(rotation / sin(surface_slope))
) # the 2nd part yields +/-1 and determines if the panel is facing east or west
cosincidence = cos(surface_slope) * sin(sun_altitude) + sin(
Expand All @@ -135,15 +136,15 @@ def SurfaceOrientation(ds, solar_position, orientation, tracking=None):
"slope"
] # here orientation['slope'] refers to the tilt of the tracker axis.

rotation = np.arctan(
rotation = arctan(
(cos(sun_altitude) * sin(sun_azimuth - surface_azimuth))
/ (
cos(sun_altitude) * cos(sun_azimuth - surface_azimuth) * sin(axis_tilt)
+ sin(sun_altitude) * cos(axis_tilt)
)
)

surface_slope = np.arccos(cos(rotation) * cos(axis_tilt))
surface_slope = arccos(cos(rotation) * cos(axis_tilt))

azimuth_difference = sun_azimuth - surface_azimuth
azimuth_difference = np.where(
Expand All @@ -153,12 +154,12 @@ def SurfaceOrientation(ds, solar_position, orientation, tracking=None):
azimuth_difference < -pi, 2 * pi + azimuth_difference, azimuth_difference
)
rotation = np.where(
np.logical_and(rotation < 0, azimuth_difference > 0),
logical_and(rotation < 0, azimuth_difference > 0),
rotation + pi,
rotation,
)
rotation = np.where(
np.logical_and(rotation > 0, azimuth_difference < 0),
logical_and(rotation > 0, azimuth_difference < 0),
rotation - pi,
rotation,
)
Expand Down
19 changes: 8 additions & 11 deletions atlite/pv/solar_position.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

import pandas as pd
import xarray as xr
from numpy import arccos, arcsin, arctan2, cos, deg2rad, pi, sin
from dask.array import arccos, arcsin, arctan2, cos, radians, sin
from numpy import pi


def SolarPosition(ds, time_shift="0H"):
Expand Down Expand Up @@ -57,11 +58,7 @@ def SolarPosition(ds, time_shift="0H"):
}

if rvs.issubset(set(ds.data_vars)):
solar_position = ds[rvs]
solar_position = solar_position.rename(
{v: v.replace("solar_", "") for v in rvs}
)
return solar_position
return ds[rvs].rename({v: v.replace("solar_", "") for v in rvs})

warn(
"""The calculation method and handling of solar position variables will change.
Expand All @@ -86,20 +83,20 @@ def SolarPosition(ds, time_shift="0H"):
minute = minute.chunk(chunks)

L = 280.460 + 0.9856474 * n # mean longitude (deg)
g = deg2rad(357.528 + 0.9856003 * n) # mean anomaly (rad)
l = deg2rad(L + 1.915 * sin(g) + 0.020 * sin(2 * g)) # ecliptic long. (rad)
ep = deg2rad(23.439 - 4e-7 * n) # obliquity of the ecliptic (rad)
g = radians(357.528 + 0.9856003 * n) # mean anomaly (rad)
l = radians(L + 1.915 * sin(g) + 0.020 * sin(2 * g)) # ecliptic long. (rad)
ep = radians(23.439 - 4e-7 * n) # obliquity of the ecliptic (rad)

ra = arctan2(cos(ep) * sin(l), cos(l)) # right ascencion (rad)
lmst = (6.697375 + (hour + minute / 60.0) + 0.0657098242 * n) * 15.0 + ds[
"lon"
] # local mean sidereal time (deg)
h = (deg2rad(lmst) - ra + pi) % (2 * pi) - pi # hour angle (rad)
h = (radians(lmst) - ra + pi) % (2 * pi) - pi # hour angle (rad)

dec = arcsin(sin(ep) * sin(l)) # declination (rad)

# alt and az from [2]
lat = deg2rad(ds["lat"])
lat = radians(ds["lat"])
# Clip before arcsin to prevent values < -1. from rounding errors; can
# cause NaNs later
alt = arcsin(
Expand Down
5 changes: 3 additions & 2 deletions atlite/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import pkg_resources
import requests
import yaml
from dask.array import radians
from scipy.signal import fftconvolve

from atlite.utils import arrowdict
Expand Down Expand Up @@ -170,8 +171,8 @@ def get_cspinstallationconfig(installation):
da = da.rename({"azimuth": "azimuth [deg]", "altitude": "altitude [deg]"})
da = da.assign_coords(
{
"altitude": np.deg2rad(da["altitude [deg]"]),
"azimuth": np.deg2rad(da["azimuth [deg]"]),
"altitude": radians(da["altitude [deg]"]),
"azimuth": radians(da["azimuth [deg]"]),
}
)
da = da.swap_dims({"altitude [deg]": "altitude", "azimuth [deg]": "azimuth"})
Expand Down

0 comments on commit 74e961f

Please sign in to comment.