voxcity 0.3.2__py3-none-any.whl → 0.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of voxcity might be problematic. Click here for more details.
- voxcity/download/eubucco.py +9 -17
- voxcity/download/gee.py +4 -3
- voxcity/download/mbfp.py +7 -7
- voxcity/download/oemj.py +22 -22
- voxcity/download/omt.py +10 -10
- voxcity/download/osm.py +23 -21
- voxcity/download/overture.py +7 -15
- voxcity/file/envimet.py +4 -4
- voxcity/file/geojson.py +83 -26
- voxcity/geo/draw.py +128 -22
- voxcity/geo/grid.py +9 -143
- voxcity/geo/utils.py +79 -66
- voxcity/sim/solar.py +187 -53
- voxcity/sim/view.py +183 -31
- voxcity/utils/weather.py +7 -7
- {voxcity-0.3.2.dist-info → voxcity-0.3.4.dist-info}/METADATA +61 -5
- voxcity-0.3.4.dist-info/RECORD +34 -0
- {voxcity-0.3.2.dist-info → voxcity-0.3.4.dist-info}/WHEEL +1 -1
- voxcity-0.3.2.dist-info/RECORD +0 -34
- {voxcity-0.3.2.dist-info → voxcity-0.3.4.dist-info}/AUTHORS.rst +0 -0
- {voxcity-0.3.2.dist-info → voxcity-0.3.4.dist-info}/LICENSE +0 -0
- {voxcity-0.3.2.dist-info → voxcity-0.3.4.dist-info}/top_level.txt +0 -0
voxcity/sim/solar.py
CHANGED
|
@@ -16,10 +16,16 @@ def compute_direct_solar_irradiance_map_binary(voxel_data, sun_direction, view_p
|
|
|
16
16
|
"""
|
|
17
17
|
Compute a map of direct solar irradiation accounting for tree transmittance.
|
|
18
18
|
|
|
19
|
+
The function:
|
|
20
|
+
1. Places observers at valid locations (empty voxels above ground)
|
|
21
|
+
2. Casts rays from each observer in the sun direction
|
|
22
|
+
3. Computes transmittance through trees using Beer-Lambert law
|
|
23
|
+
4. Returns a 2D map of transmittance values
|
|
24
|
+
|
|
19
25
|
Args:
|
|
20
26
|
voxel_data (ndarray): 3D array of voxel values.
|
|
21
27
|
sun_direction (tuple): Direction vector of the sun.
|
|
22
|
-
|
|
28
|
+
view_point_height (float): Observer height in meters.
|
|
23
29
|
hit_values (tuple): Values considered non-obstacles if inclusion_mode=False.
|
|
24
30
|
meshsize (float): Size of each voxel in meters.
|
|
25
31
|
tree_k (float): Tree extinction coefficient.
|
|
@@ -27,7 +33,7 @@ def compute_direct_solar_irradiance_map_binary(voxel_data, sun_direction, view_p
|
|
|
27
33
|
inclusion_mode (bool): False here, meaning any voxel not in hit_values is an obstacle.
|
|
28
34
|
|
|
29
35
|
Returns:
|
|
30
|
-
ndarray: 2D array of transmittance values (0.0-1.0), NaN = invalid observer.
|
|
36
|
+
ndarray: 2D array of transmittance values (0.0-1.0), NaN = invalid observer position.
|
|
31
37
|
"""
|
|
32
38
|
|
|
33
39
|
view_height_voxel = int(view_point_height / meshsize)
|
|
@@ -35,18 +41,22 @@ def compute_direct_solar_irradiance_map_binary(voxel_data, sun_direction, view_p
|
|
|
35
41
|
nx, ny, nz = voxel_data.shape
|
|
36
42
|
irradiance_map = np.full((nx, ny), np.nan, dtype=np.float64)
|
|
37
43
|
|
|
38
|
-
# Normalize sun direction
|
|
44
|
+
# Normalize sun direction vector for ray tracing
|
|
39
45
|
sd = np.array(sun_direction, dtype=np.float64)
|
|
40
46
|
sd_len = np.sqrt(sd[0]**2 + sd[1]**2 + sd[2]**2)
|
|
41
47
|
if sd_len == 0.0:
|
|
42
48
|
return np.flipud(irradiance_map)
|
|
43
49
|
sd /= sd_len
|
|
44
50
|
|
|
51
|
+
# Process each x,y position in parallel
|
|
45
52
|
for x in prange(nx):
|
|
46
53
|
for y in range(ny):
|
|
47
54
|
found_observer = False
|
|
55
|
+
# Search upward for valid observer position
|
|
48
56
|
for z in range(1, nz):
|
|
57
|
+
# Check if current voxel is empty/tree and voxel below is solid
|
|
49
58
|
if voxel_data[x, y, z] in (0, -2) and voxel_data[x, y, z - 1] not in (0, -2):
|
|
59
|
+
# Skip if standing on building/vegetation/water
|
|
50
60
|
if voxel_data[x, y, z - 1] in (-30, -3, -2):
|
|
51
61
|
irradiance_map[x, y] = np.nan
|
|
52
62
|
found_observer = True
|
|
@@ -62,12 +72,43 @@ def compute_direct_solar_irradiance_map_binary(voxel_data, sun_direction, view_p
|
|
|
62
72
|
if not found_observer:
|
|
63
73
|
irradiance_map[x, y] = np.nan
|
|
64
74
|
|
|
75
|
+
# Flip map vertically to match visualization conventions
|
|
65
76
|
return np.flipud(irradiance_map)
|
|
66
77
|
|
|
67
78
|
def get_direct_solar_irradiance_map(voxel_data, meshsize, azimuth_degrees_ori, elevation_degrees,
|
|
68
79
|
direct_normal_irradiance, show_plot=False, **kwargs):
|
|
69
80
|
"""
|
|
70
81
|
Compute direct solar irradiance map with tree transmittance.
|
|
82
|
+
|
|
83
|
+
The function:
|
|
84
|
+
1. Converts sun angles to direction vector
|
|
85
|
+
2. Computes binary transmittance map
|
|
86
|
+
3. Scales by direct normal irradiance and sun elevation
|
|
87
|
+
4. Optionally visualizes and exports results
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
voxel_data (ndarray): 3D array of voxel values.
|
|
91
|
+
meshsize (float): Size of each voxel in meters.
|
|
92
|
+
azimuth_degrees_ori (float): Sun azimuth angle in degrees (0° = North, 90° = East).
|
|
93
|
+
elevation_degrees (float): Sun elevation angle in degrees above horizon.
|
|
94
|
+
direct_normal_irradiance (float): Direct normal irradiance in W/m².
|
|
95
|
+
show_plot (bool): Whether to display visualization.
|
|
96
|
+
**kwargs: Additional arguments including:
|
|
97
|
+
- view_point_height (float): Observer height in meters (default: 1.5)
|
|
98
|
+
- colormap (str): Matplotlib colormap name (default: 'magma')
|
|
99
|
+
- vmin (float): Minimum value for colormap
|
|
100
|
+
- vmax (float): Maximum value for colormap
|
|
101
|
+
- tree_k (float): Tree extinction coefficient (default: 0.6)
|
|
102
|
+
- tree_lad (float): Leaf area density in m^-1 (default: 1.0)
|
|
103
|
+
- obj_export (bool): Whether to export as OBJ file
|
|
104
|
+
- output_directory (str): Directory for OBJ export
|
|
105
|
+
- output_file_name (str): Filename for OBJ export
|
|
106
|
+
- dem_grid (ndarray): DEM grid for OBJ export
|
|
107
|
+
- num_colors (int): Number of colors for OBJ export
|
|
108
|
+
- alpha (float): Alpha value for OBJ export
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
ndarray: 2D array of direct solar irradiance values (W/m²).
|
|
71
112
|
"""
|
|
72
113
|
view_point_height = kwargs.get("view_point_height", 1.5)
|
|
73
114
|
colormap = kwargs.get("colormap", 'magma')
|
|
@@ -78,7 +119,8 @@ def get_direct_solar_irradiance_map(voxel_data, meshsize, azimuth_degrees_ori, e
|
|
|
78
119
|
tree_k = kwargs.get("tree_k", 0.6)
|
|
79
120
|
tree_lad = kwargs.get("tree_lad", 1.0)
|
|
80
121
|
|
|
81
|
-
# Convert angles to direction
|
|
122
|
+
# Convert sun angles to direction vector
|
|
123
|
+
# Note: azimuth is adjusted by 180° to match coordinate system
|
|
82
124
|
azimuth_degrees = 180 - azimuth_degrees_ori
|
|
83
125
|
azimuth_radians = np.deg2rad(azimuth_degrees)
|
|
84
126
|
elevation_radians = np.deg2rad(elevation_degrees)
|
|
@@ -91,14 +133,17 @@ def get_direct_solar_irradiance_map(voxel_data, meshsize, azimuth_degrees_ori, e
|
|
|
91
133
|
hit_values = (0,)
|
|
92
134
|
inclusion_mode = False
|
|
93
135
|
|
|
136
|
+
# Compute transmittance map
|
|
94
137
|
transmittance_map = compute_direct_solar_irradiance_map_binary(
|
|
95
138
|
voxel_data, sun_direction, view_point_height, hit_values,
|
|
96
139
|
meshsize, tree_k, tree_lad, inclusion_mode
|
|
97
140
|
)
|
|
98
141
|
|
|
142
|
+
# Scale by direct normal irradiance and sun elevation
|
|
99
143
|
sin_elev = dz
|
|
100
144
|
direct_map = transmittance_map * direct_normal_irradiance * sin_elev
|
|
101
145
|
|
|
146
|
+
# Optional visualization
|
|
102
147
|
if show_plot:
|
|
103
148
|
cmap = plt.cm.get_cmap(colormap).copy()
|
|
104
149
|
cmap.set_bad(color='lightgray')
|
|
@@ -135,6 +180,33 @@ def get_direct_solar_irradiance_map(voxel_data, meshsize, azimuth_degrees_ori, e
|
|
|
135
180
|
def get_diffuse_solar_irradiance_map(voxel_data, meshsize, diffuse_irradiance=1.0, show_plot=False, **kwargs):
|
|
136
181
|
"""
|
|
137
182
|
Compute diffuse solar irradiance map using the Sky View Factor (SVF) with tree transmittance.
|
|
183
|
+
|
|
184
|
+
The function:
|
|
185
|
+
1. Computes SVF map accounting for tree transmittance
|
|
186
|
+
2. Scales SVF by diffuse horizontal irradiance
|
|
187
|
+
3. Optionally visualizes and exports results
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
voxel_data (ndarray): 3D array of voxel values.
|
|
191
|
+
meshsize (float): Size of each voxel in meters.
|
|
192
|
+
diffuse_irradiance (float): Diffuse horizontal irradiance in W/m².
|
|
193
|
+
show_plot (bool): Whether to display visualization.
|
|
194
|
+
**kwargs: Additional arguments including:
|
|
195
|
+
- view_point_height (float): Observer height in meters (default: 1.5)
|
|
196
|
+
- colormap (str): Matplotlib colormap name (default: 'magma')
|
|
197
|
+
- vmin (float): Minimum value for colormap
|
|
198
|
+
- vmax (float): Maximum value for colormap
|
|
199
|
+
- tree_k (float): Tree extinction coefficient
|
|
200
|
+
- tree_lad (float): Leaf area density in m^-1
|
|
201
|
+
- obj_export (bool): Whether to export as OBJ file
|
|
202
|
+
- output_directory (str): Directory for OBJ export
|
|
203
|
+
- output_file_name (str): Filename for OBJ export
|
|
204
|
+
- dem_grid (ndarray): DEM grid for OBJ export
|
|
205
|
+
- num_colors (int): Number of colors for OBJ export
|
|
206
|
+
- alpha (float): Alpha value for OBJ export
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
ndarray: 2D array of diffuse solar irradiance values (W/m²).
|
|
138
210
|
"""
|
|
139
211
|
|
|
140
212
|
view_point_height = kwargs.get("view_point_height", 1.5)
|
|
@@ -152,6 +224,7 @@ def get_diffuse_solar_irradiance_map(voxel_data, meshsize, diffuse_irradiance=1.
|
|
|
152
224
|
SVF_map = get_sky_view_factor_map(voxel_data, meshsize, **svf_kwargs)
|
|
153
225
|
diffuse_map = SVF_map * diffuse_irradiance
|
|
154
226
|
|
|
227
|
+
# Optional visualization
|
|
155
228
|
if show_plot:
|
|
156
229
|
vmin = kwargs.get("vmin", 0.0)
|
|
157
230
|
vmax = kwargs.get("vmax", diffuse_irradiance)
|
|
@@ -201,18 +274,35 @@ def get_global_solar_irradiance_map(
|
|
|
201
274
|
"""
|
|
202
275
|
Compute global solar irradiance (direct + diffuse) on a horizontal plane at each valid observer location.
|
|
203
276
|
|
|
204
|
-
|
|
277
|
+
The function:
|
|
278
|
+
1. Computes direct solar irradiance map
|
|
279
|
+
2. Computes diffuse solar irradiance map
|
|
280
|
+
3. Combines maps and optionally visualizes/exports results
|
|
205
281
|
|
|
206
282
|
Args:
|
|
207
283
|
voxel_data (ndarray): 3D voxel array.
|
|
208
284
|
meshsize (float): Voxel size in meters.
|
|
209
|
-
azimuth_degrees (float): Sun azimuth angle in degrees.
|
|
210
|
-
elevation_degrees (float): Sun elevation angle in degrees.
|
|
211
|
-
direct_normal_irradiance (float):
|
|
212
|
-
diffuse_irradiance (float): Diffuse irradiance in W/m².
|
|
285
|
+
azimuth_degrees (float): Sun azimuth angle in degrees (0° = North, 90° = East).
|
|
286
|
+
elevation_degrees (float): Sun elevation angle in degrees above horizon.
|
|
287
|
+
direct_normal_irradiance (float): Direct normal irradiance in W/m².
|
|
288
|
+
diffuse_irradiance (float): Diffuse horizontal irradiance in W/m².
|
|
289
|
+
show_plot (bool): Whether to display visualization.
|
|
290
|
+
**kwargs: Additional arguments including:
|
|
291
|
+
- view_point_height (float): Observer height in meters (default: 1.5)
|
|
292
|
+
- colormap (str): Matplotlib colormap name (default: 'magma')
|
|
293
|
+
- vmin (float): Minimum value for colormap
|
|
294
|
+
- vmax (float): Maximum value for colormap
|
|
295
|
+
- tree_k (float): Tree extinction coefficient
|
|
296
|
+
- tree_lad (float): Leaf area density in m^-1
|
|
297
|
+
- obj_export (bool): Whether to export as OBJ file
|
|
298
|
+
- output_directory (str): Directory for OBJ export
|
|
299
|
+
- output_file_name (str): Filename for OBJ export
|
|
300
|
+
- dem_grid (ndarray): DEM grid for OBJ export
|
|
301
|
+
- num_colors (int): Number of colors for OBJ export
|
|
302
|
+
- alpha (float): Alpha value for OBJ export
|
|
213
303
|
|
|
214
304
|
Returns:
|
|
215
|
-
ndarray: 2D array of global solar irradiance (W/m²).
|
|
305
|
+
ndarray: 2D array of global solar irradiance values (W/m²).
|
|
216
306
|
"""
|
|
217
307
|
|
|
218
308
|
colormap = kwargs.get("colormap", 'magma')
|
|
@@ -242,12 +332,13 @@ def get_global_solar_irradiance_map(
|
|
|
242
332
|
**direct_diffuse_kwargs
|
|
243
333
|
)
|
|
244
334
|
|
|
245
|
-
# Sum the two
|
|
335
|
+
# Sum the two components
|
|
246
336
|
global_map = direct_map + diffuse_map
|
|
247
337
|
|
|
248
338
|
vmin = kwargs.get("vmin", np.nanmin(global_map))
|
|
249
339
|
vmax = kwargs.get("vmax", np.nanmax(global_map))
|
|
250
340
|
|
|
341
|
+
# Optional visualization
|
|
251
342
|
if show_plot:
|
|
252
343
|
cmap = plt.cm.get_cmap(colormap).copy()
|
|
253
344
|
cmap.set_bad(color='lightgray')
|
|
@@ -283,10 +374,22 @@ def get_global_solar_irradiance_map(
|
|
|
283
374
|
|
|
284
375
|
return global_map
|
|
285
376
|
|
|
286
|
-
def get_solar_positions_astral(times,
|
|
377
|
+
def get_solar_positions_astral(times, lon, lat):
|
|
287
378
|
"""
|
|
288
379
|
Compute solar azimuth and elevation using Astral for given times and location.
|
|
289
|
-
|
|
380
|
+
|
|
381
|
+
The function:
|
|
382
|
+
1. Creates an Astral observer at the specified location
|
|
383
|
+
2. Computes sun position for each timestamp
|
|
384
|
+
3. Returns DataFrame with azimuth and elevation angles
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
times (DatetimeIndex): Array of timezone-aware datetime objects.
|
|
388
|
+
lon (float): Longitude in degrees.
|
|
389
|
+
lat (float): Latitude in degrees.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
DataFrame: DataFrame with columns 'azimuth' and 'elevation' containing solar positions.
|
|
290
393
|
"""
|
|
291
394
|
observer = Observer(latitude=lat, longitude=lon)
|
|
292
395
|
df_pos = pd.DataFrame(index=times, columns=['azimuth', 'elevation'], dtype=float)
|
|
@@ -303,33 +406,49 @@ def get_solar_positions_astral(times, lat, lon):
|
|
|
303
406
|
def get_cumulative_global_solar_irradiance(
|
|
304
407
|
voxel_data,
|
|
305
408
|
meshsize,
|
|
306
|
-
df,
|
|
409
|
+
df, lon, lat, tz,
|
|
307
410
|
direct_normal_irradiance_scaling=1.0,
|
|
308
411
|
diffuse_irradiance_scaling=1.0,
|
|
309
412
|
**kwargs
|
|
310
413
|
):
|
|
311
414
|
"""
|
|
312
|
-
Compute cumulative global solar irradiance over a specified period using data from an EPW file
|
|
313
|
-
|
|
415
|
+
Compute cumulative global solar irradiance over a specified period using data from an EPW file.
|
|
416
|
+
|
|
417
|
+
The function:
|
|
418
|
+
1. Filters EPW data for specified time period
|
|
419
|
+
2. Computes sun positions for each timestep
|
|
420
|
+
3. Calculates and accumulates global irradiance maps
|
|
421
|
+
4. Handles tree transmittance and visualization
|
|
314
422
|
|
|
315
423
|
Args:
|
|
316
424
|
voxel_data (ndarray): 3D array of voxel values.
|
|
317
425
|
meshsize (float): Size of each voxel in meters.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
426
|
+
df (DataFrame): EPW weather data.
|
|
427
|
+
lon (float): Longitude in degrees.
|
|
428
|
+
lat (float): Latitude in degrees.
|
|
429
|
+
tz (float): Timezone offset in hours.
|
|
430
|
+
direct_normal_irradiance_scaling (float): Scaling factor for direct normal irradiance.
|
|
431
|
+
diffuse_irradiance_scaling (float): Scaling factor for diffuse horizontal irradiance.
|
|
322
432
|
**kwargs: Additional arguments including:
|
|
323
|
-
- view_point_height (float): Observer height in meters
|
|
324
|
-
-
|
|
325
|
-
-
|
|
326
|
-
-
|
|
327
|
-
-
|
|
433
|
+
- view_point_height (float): Observer height in meters (default: 1.5)
|
|
434
|
+
- start_time (str): Start time in format 'MM-DD HH:MM:SS'
|
|
435
|
+
- end_time (str): End time in format 'MM-DD HH:MM:SS'
|
|
436
|
+
- tree_k (float): Tree extinction coefficient
|
|
437
|
+
- tree_lad (float): Leaf area density in m^-1
|
|
328
438
|
- show_plot (bool): Whether to show final plot
|
|
329
439
|
- show_each_timestep (bool): Whether to show plots for each timestep
|
|
440
|
+
- colormap (str): Matplotlib colormap name
|
|
441
|
+
- vmin (float): Minimum value for colormap
|
|
442
|
+
- vmax (float): Maximum value for colormap
|
|
443
|
+
- obj_export (bool): Whether to export as OBJ file
|
|
444
|
+
- output_directory (str): Directory for OBJ export
|
|
445
|
+
- output_file_name (str): Filename for OBJ export
|
|
446
|
+
- dem_grid (ndarray): DEM grid for OBJ export
|
|
447
|
+
- num_colors (int): Number of colors for OBJ export
|
|
448
|
+
- alpha (float): Alpha value for OBJ export
|
|
330
449
|
|
|
331
450
|
Returns:
|
|
332
|
-
ndarray: 2D array of cumulative global solar irradiance (W/m²·hour).
|
|
451
|
+
ndarray: 2D array of cumulative global solar irradiance values (W/m²·hour).
|
|
333
452
|
"""
|
|
334
453
|
view_point_height = kwargs.get("view_point_height", 1.5)
|
|
335
454
|
colormap = kwargs.get("colormap", 'magma')
|
|
@@ -346,20 +465,23 @@ def get_cumulative_global_solar_irradiance(
|
|
|
346
465
|
except ValueError as ve:
|
|
347
466
|
raise ValueError("start_time and end_time must be in format 'MM-DD HH:MM:SS'") from ve
|
|
348
467
|
|
|
349
|
-
# Add hour of year column and filter data
|
|
468
|
+
# Add hour of year column and filter data
|
|
350
469
|
df['hour_of_year'] = (df.index.dayofyear - 1) * 24 + df.index.hour + 1
|
|
351
470
|
|
|
471
|
+
# Convert dates to day of year and hour
|
|
352
472
|
start_doy = datetime(2000, start_dt.month, start_dt.day).timetuple().tm_yday
|
|
353
473
|
end_doy = datetime(2000, end_dt.month, end_dt.day).timetuple().tm_yday
|
|
354
474
|
|
|
355
475
|
start_hour = (start_doy - 1) * 24 + start_dt.hour + 1
|
|
356
476
|
end_hour = (end_doy - 1) * 24 + end_dt.hour + 1
|
|
357
477
|
|
|
478
|
+
# Handle period crossing year boundary
|
|
358
479
|
if start_hour <= end_hour:
|
|
359
480
|
df_period = df[(df['hour_of_year'] >= start_hour) & (df['hour_of_year'] <= end_hour)]
|
|
360
481
|
else:
|
|
361
482
|
df_period = df[(df['hour_of_year'] >= start_hour) | (df['hour_of_year'] <= end_hour)]
|
|
362
483
|
|
|
484
|
+
# Filter by minutes within start/end hours
|
|
363
485
|
df_period = df_period[
|
|
364
486
|
((df_period.index.hour != start_dt.hour) | (df_period.index.minute >= start_dt.minute)) &
|
|
365
487
|
((df_period.index.hour != end_dt.hour) | (df_period.index.minute <= end_dt.minute))
|
|
@@ -368,15 +490,15 @@ def get_cumulative_global_solar_irradiance(
|
|
|
368
490
|
if df_period.empty:
|
|
369
491
|
raise ValueError("No EPW data in the specified period.")
|
|
370
492
|
|
|
371
|
-
#
|
|
493
|
+
# Handle timezone conversion
|
|
372
494
|
offset_minutes = int(tz * 60)
|
|
373
495
|
local_tz = pytz.FixedOffset(offset_minutes)
|
|
374
496
|
df_period_local = df_period.copy()
|
|
375
497
|
df_period_local.index = df_period_local.index.tz_localize(local_tz)
|
|
376
498
|
df_period_utc = df_period_local.tz_convert(pytz.UTC)
|
|
377
499
|
|
|
378
|
-
# Compute solar positions
|
|
379
|
-
solar_positions = get_solar_positions_astral(df_period_utc.index,
|
|
500
|
+
# Compute solar positions for period
|
|
501
|
+
solar_positions = get_solar_positions_astral(df_period_utc.index, lon, lat)
|
|
380
502
|
|
|
381
503
|
# Create kwargs for diffuse calculation
|
|
382
504
|
diffuse_kwargs = kwargs.copy()
|
|
@@ -393,7 +515,7 @@ def get_cumulative_global_solar_irradiance(
|
|
|
393
515
|
**diffuse_kwargs
|
|
394
516
|
)
|
|
395
517
|
|
|
396
|
-
# Initialize maps
|
|
518
|
+
# Initialize accumulation maps
|
|
397
519
|
cumulative_map = np.zeros((voxel_data.shape[0], voxel_data.shape[1]))
|
|
398
520
|
mask_map = np.ones((voxel_data.shape[0], voxel_data.shape[1]), dtype=bool)
|
|
399
521
|
|
|
@@ -405,13 +527,14 @@ def get_cumulative_global_solar_irradiance(
|
|
|
405
527
|
'obj_export': False
|
|
406
528
|
})
|
|
407
529
|
|
|
408
|
-
#
|
|
530
|
+
# Process each timestep
|
|
409
531
|
for idx, (time_utc, row) in enumerate(df_period_utc.iterrows()):
|
|
532
|
+
# Get scaled irradiance values
|
|
410
533
|
DNI = row['DNI'] * direct_normal_irradiance_scaling
|
|
411
534
|
DHI = row['DHI'] * diffuse_irradiance_scaling
|
|
412
535
|
time_local = df_period_local.index[idx]
|
|
413
536
|
|
|
414
|
-
# Get solar position
|
|
537
|
+
# Get solar position for timestep
|
|
415
538
|
solpos = solar_positions.loc[time_utc]
|
|
416
539
|
azimuth_degrees = solpos['azimuth']
|
|
417
540
|
elevation_degrees = solpos['elevation']
|
|
@@ -426,13 +549,13 @@ def get_cumulative_global_solar_irradiance(
|
|
|
426
549
|
**direct_kwargs
|
|
427
550
|
)
|
|
428
551
|
|
|
429
|
-
# Scale
|
|
552
|
+
# Scale base diffuse map by actual DHI
|
|
430
553
|
diffuse_map = base_diffuse_map * DHI
|
|
431
554
|
|
|
432
|
-
# Combine direct and diffuse
|
|
555
|
+
# Combine direct and diffuse components
|
|
433
556
|
global_map = direct_map + diffuse_map
|
|
434
557
|
|
|
435
|
-
# Update
|
|
558
|
+
# Update valid pixel mask
|
|
436
559
|
mask_map &= ~np.isnan(global_map)
|
|
437
560
|
|
|
438
561
|
# Replace NaN with 0 for accumulation
|
|
@@ -453,7 +576,7 @@ def get_cumulative_global_solar_irradiance(
|
|
|
453
576
|
plt.colorbar(label='Global Solar Irradiance (W/m²)')
|
|
454
577
|
plt.show()
|
|
455
578
|
|
|
456
|
-
# Apply mask
|
|
579
|
+
# Apply mask to final result
|
|
457
580
|
cumulative_map[~mask_map] = np.nan
|
|
458
581
|
|
|
459
582
|
# Final visualization
|
|
@@ -506,27 +629,38 @@ def get_global_solar_irradiance_using_epw(
|
|
|
506
629
|
**kwargs
|
|
507
630
|
):
|
|
508
631
|
"""
|
|
509
|
-
Compute
|
|
510
|
-
accounting for tree transmittance.
|
|
632
|
+
Compute global solar irradiance using EPW weather data, either for a single time or cumulatively over a period.
|
|
511
633
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
)
|
|
521
|
-
|
|
522
|
-
|
|
634
|
+
The function:
|
|
635
|
+
1. Optionally downloads and reads EPW weather data
|
|
636
|
+
2. Handles timezone conversions and solar position calculations
|
|
637
|
+
3. Computes either instantaneous or cumulative irradiance maps
|
|
638
|
+
4. Supports visualization and export options
|
|
639
|
+
|
|
640
|
+
Args:
|
|
641
|
+
voxel_data (ndarray): 3D array of voxel values.
|
|
642
|
+
meshsize (float): Size of each voxel in meters.
|
|
643
|
+
calc_type (str): 'instantaneous' or 'cumulative'.
|
|
644
|
+
direct_normal_irradiance_scaling (float): Scaling factor for direct normal irradiance.
|
|
645
|
+
diffuse_irradiance_scaling (float): Scaling factor for diffuse horizontal irradiance.
|
|
646
|
+
**kwargs: Additional arguments including:
|
|
523
647
|
- download_nearest_epw (bool): Whether to download nearest EPW file
|
|
524
648
|
- epw_file_path (str): Path to EPW file
|
|
525
|
-
-
|
|
526
|
-
-
|
|
649
|
+
- rectangle_vertices (list): List of (lat,lon) coordinates for EPW download
|
|
650
|
+
- output_dir (str): Directory for EPW download
|
|
651
|
+
- calc_time (str): Time for instantaneous calculation ('MM-DD HH:MM:SS')
|
|
652
|
+
- start_time (str): Start time for cumulative calculation
|
|
653
|
+
- end_time (str): End time for cumulative calculation
|
|
654
|
+
- view_point_height (float): Observer height in meters
|
|
655
|
+
- tree_k (float): Tree extinction coefficient
|
|
656
|
+
- tree_lad (float): Leaf area density in m^-1
|
|
657
|
+
- show_plot (bool): Whether to show visualization
|
|
658
|
+
- show_each_timestep (bool): Whether to show timestep plots
|
|
659
|
+
- colormap (str): Matplotlib colormap name
|
|
660
|
+
- obj_export (bool): Whether to export as OBJ file
|
|
527
661
|
|
|
528
662
|
Returns:
|
|
529
|
-
ndarray: 2D array of
|
|
663
|
+
ndarray: 2D array of solar irradiance values (W/m²).
|
|
530
664
|
"""
|
|
531
665
|
view_point_height = kwargs.get("view_point_height", 1.5)
|
|
532
666
|
colormap = kwargs.get("colormap", 'magma')
|