Skip to content

Commit

Permalink
fix(helper): Add function to find smallest dtype
Browse files Browse the repository at this point in the history
* fix(helper): Add function to find smallest dtype

* fix(map): Add binary option in CLI command

* refactor(mtx): Use load matrix function
  • Loading branch information
mikkelkp authored Jul 26, 2024
1 parent bd31138 commit afd5af6
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 31 deletions.
97 changes: 94 additions & 3 deletions ladybug_comfort/cli/_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,100 @@ def thermal_map_csv(folder, temperature, condition, condition_intensity,
_data_to_csv(condition_intensity, result_file_dict['condition_intensity'])
else:
with open(result_file_dict['temperature'], 'wb') as fp:
np.save(fp, temperature)
np.save(fp, set_smallest_dtype(np.array(temperature)))
with open(result_file_dict['condition'], 'wb') as fp:
np.save(fp, np.array(condition))
np.save(fp, set_smallest_dtype(np.array(condition)))
with open(result_file_dict['condition_intensity'], 'wb') as fp:
np.save(fp, np.array(condition_intensity))
np.save(fp, set_smallest_dtype(np.array(condition_intensity)))
return result_file_dict


def smallest_integer_dtype(array: np.ndarray):
"""Return the smallest possible integer dtype.
Args:
array: NumPy array.
Returns:
A NumPy integer dtype.
"""
if np.all(array >= np.iinfo(np.int8).min) and \
np.all(array <= np.iinfo(np.int8).max):
return np.int8
elif np.all(array >= np.iinfo(np.int16).min) and \
np.all(array <= np.iinfo(np.int16).max):
return np.int16
elif np.all(array >= np.iinfo(np.int32).min) and \
np.all(array <= np.iinfo(np.int32).max):
return np.int32
elif np.all(array >= np.iinfo(np.int64).min) and \
np.all(array <= np.iinfo(np.int64).max):
return np.int64


def smallest_float_dtype(array: np.ndarray, rtol: float = 1e-5, atol: float = 1e-5):
"""Return the smallest possible float dtype.
The allclose function is used to check if a certain floating-point precision
can be used without losing accuracy.
Args:
array: NumPy array.
rtol: The relative tolerance parameter for `np.allclose`. The default
is 1e-5.
atol: The absolute tolerance parameter for `np.allclose`. The default
is 1e-5.
Returns:
A NumPy floating dtype.
"""
if np.all((array >= np.finfo(np.float16).min) & \
(array <= np.finfo(np.float16).max)):
if np.allclose(array, array.astype(np.float16), rtol=rtol, atol=atol):
return np.float16
if np.all((array >= np.finfo(np.float32).min) & \
(array <= np.finfo(np.float32).max)):
if np.allclose(array, array.astype(np.float32), rtol=rtol, atol=atol):
return np.float32
if np.all((array >= np.finfo(np.float64).min) & \
(array <= np.finfo(np.float64).max)):
if np.allclose(array, array.astype(np.float64), rtol=rtol, atol=atol):
return np.float64


def smallest_dtype(array: np.ndarray, rtol: float = 1e-5, atol: float = 1e-5):
"""Return the smallest possible dtype.
Args:
array: NumPy array.
rtol: The relative tolerance parameter for `np.allclose`. The default
is 1e-5. This is also used if the dtype of the array is np.floating.
atol: The absolute tolerance parameter for `np.allclose`. The default
is 1e-5. This is also used if the dtype of the array is np.floating.
Returns:
A NumPy dtype.
"""
if np.issubdtype(array.dtype, np.integer):
return smallest_integer_dtype(array)
elif np.issubdtype(array.dtype, np.floating):
return smallest_float_dtype(array, rtol=rtol, atol=atol)
else:
raise TypeError(f'Expected integer or floating dtype. Got {array.dtype}')


def set_smallest_dtype(array: np.ndarray, rtol: float = 1e-5, atol: float = 1e-5):
"""Return a NumPy array with the smallest possible dtype.
Args:
array: NumPy array.
rtol: The relative tolerance parameter for `np.allclose`. The default
is 1e-5. This is also used if the dtype of the array is np.floating.
atol: The absolute tolerance parameter for `np.allclose`. The default
is 1e-5. This is also used if the dtype of the array is np.floating.
Returns:
A new NumPy array with a smaller dtype.
"""
dtype = smallest_dtype(array, rtol=rtol, atol=atol)
return array.astype(dtype)
24 changes: 16 additions & 8 deletions ladybug_comfort/cli/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

from ._helper import load_values, load_analysis_period_str, \
load_pmv_par_str, load_adaptive_par_str, load_utci_par_str, \
load_solarcal_par_str, thermal_map_csv, _data_to_ill
load_solarcal_par_str, thermal_map_csv, _data_to_ill, set_smallest_dtype

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -590,7 +590,7 @@ def shortwave_mrt(
output_file.write('')
else:
with open(output_file.name, 'wb') as fp:
np.save(fp, d_mrt_temps)
np.save(fp, set_smallest_dtype(np.array(d_mrt_temps)))
except Exception as e:
_logger.exception('Failed to run Shortwave MRT Delta map.\n{}'.format(e))
sys.exit(1)
Expand Down Expand Up @@ -651,7 +651,7 @@ def longwave_mrt(result_sql, view_factors, modifiers, enclosure_info, epw_file,
output_file.write('\n')
else:
with open(output_file.name, 'wb') as fp:
np.save(fp, mrt_temps)
np.save(fp, set_smallest_dtype(np.array(mrt_temps)))
except Exception as e:
_logger.exception('Failed to run Longwave MRT map.\n{}'.format(e))
sys.exit(1)
Expand All @@ -677,8 +677,12 @@ def longwave_mrt(result_sql, view_factors, modifiers, enclosure_info, epw_file,
@click.option('--output-file', '-f', help='Optional file to output the CSV matrix '
'of values. By default this will be printed out to stdout',
type=click.File('w'), default='-', show_default=True)
def air_temperature(result_sql, enclosure_info, epw_file,
run_period, air_temperature, output_file):
@click.option('--plain-text/--binary', ' /-b', help='Flag to note whether the '
'output should be formatted as a plain text CSV or whether it '
'should be formatted as a binary numpy array.',
default=True, show_default=True)
def air_temperature(result_sql, enclosure_info, epw_file, run_period,
air_temperature, output_file, plain_text):
"""Get CSV files with maps of air temperatures or humidity from EnergyPlus results.
\b
Expand All @@ -701,9 +705,13 @@ def air_temperature(result_sql, enclosure_info, epw_file,
air_data = air_map(enclosure_info, result_sql, epw_file, run_period, humidity)

# write out the final results to CSV files
for air_d in air_data:
output_file.write(','.join(str(v) for v in air_d))
output_file.write('\n')
if plain_text:
for air_d in air_data:
output_file.write(','.join(str(v) for v in air_d))
output_file.write('\n')
else:
with open(output_file.name, 'wb') as fp:
np.save(fp, set_smallest_dtype(np.array(air_data)))
except Exception as e:
_logger.exception('Failed to run Air Temperature map.\n{}'.format(e))
sys.exit(1)
Expand Down
26 changes: 6 additions & 20 deletions ladybug_comfort/cli/mtx.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import logging
import json
import os
import numpy as np

from ladybug_comfort.pmv import predicted_mean_vote, predicted_mean_vote_no_set
from ladybug_comfort.adaptive import adaptive_comfort_ashrae55, \
adaptive_comfort_en15251, adaptive_comfort_conditioned_function, \
cooling_effect_ashrae55, cooling_effect_en16798, cooling_effect_en15251
from ladybug_comfort.utci import universal_thermal_climate_index

from ..map._helper import load_matrix
from ._helper import load_value_list, thermal_map_csv, csv_to_num_matrix, \
load_pmv_par_str, load_adaptive_par_str, load_utci_par_str

Expand Down Expand Up @@ -360,29 +360,15 @@ def utci_mtx(
"""
try:
# load up the matrices of values
air_temp = np.genfromtxt(temperature_mtx, delimiter=',').tolist()
rel_h = np.genfromtxt(rel_humidity_mtx, delimiter=',').tolist()
air_temp = load_matrix(temperature_mtx).tolist()
rel_h = load_matrix(rel_humidity_mtx).tolist()

if rad_temperature_mtx is not None:
with open(rad_temperature_mtx, 'rb') as inf:
first_char = inf.read(1)
second_char = inf.read(1)
is_text = True if first_char.isdigit() or second_char.isdigit() else False
if is_text:
rad_temp = np.genfromtxt(
rad_temperature_mtx, delimiter=',', encoding='utf-8').tolist()
else:
rad_temp = np.load(rad_temperature_mtx).tolist()
rad_temp = load_matrix(rad_temperature_mtx).tolist()
else:
rad_temp = air_temp
if rad_delta_mtx is not None and not os.path.getsize(rad_delta_mtx) == 0:
with open(rad_delta_mtx, 'rb') as inf:
first_char = inf.read(1)
second_char = inf.read(1)
is_text = True if first_char.isdigit() or second_char.isdigit() else False
if is_text:
d_rad_temp = np.genfromtxt(rad_delta_mtx, delimiter=',', encoding='utf-8')
else:
d_rad_temp = np.load(rad_delta_mtx).tolist()
d_rad_temp = load_matrix(rad_delta_mtx).tolist()
rad_temp = tuple(tuple(t + dt for t, dt in zip(t_pt, dt_pt))
for t_pt, dt_pt in zip(rad_temp, d_rad_temp))
mtx_len = len(air_temp[0])
Expand Down

0 comments on commit afd5af6

Please sign in to comment.