Skip to content

Commit d0a0412

Browse files
committedFeb 6, 2025
initial files
1 parent a1499d5 commit d0a0412

7 files changed

+400
-1
lines changed
 

‎.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__pycache__/
33
*.py[cod]
44
*$py.class
5-
5+
venv/
66
# C extensions
77
*.so
88

‎main.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from sun_position import SunPosition
2+
from solar_panel import SolarPanel
3+
from weather_service import WeatherService
4+
from solar_optimizer import SolarOptimizer
5+
from datetime import datetime
6+
import os
7+
8+
def main():
9+
# Configuration
10+
latitude = 37.7749 # San Francisco
11+
longitude = -122.4194
12+
api_key = os.getenv('WEATHER_API_KEY') # Get API key from environment variable
13+
14+
# Initialize components
15+
sun_calculator = SunPosition(latitude, longitude)
16+
weather_service = WeatherService(api_key) if api_key else None
17+
optimizer = SolarOptimizer(sun_calculator, weather_service)
18+
19+
# Set date for optimization
20+
date = datetime(2024, 6, 21) # Summer solstice
21+
22+
# Find optimal placement
23+
optimal_tilt, optimal_orientation, max_energy = optimizer.optimize_placement(date)
24+
25+
print(f"Optimal tilt: {optimal_tilt}°")
26+
print(f"Optimal orientation: {optimal_orientation}°")
27+
print(f"Maximum daily energy: {max_energy/1000:.2f} kWh")
28+
29+
# Simulate with optimal placement
30+
optimal_panel = SolarPanel(optimal_tilt, optimal_orientation)
31+
daily_output = optimizer.simulate_day(optimal_panel, date)
32+
33+
# Visualize results
34+
optimizer.visualize_results(daily_output)
35+
36+
if __name__ == "__main__":
37+
main()

‎solar_optimizer.py

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import numpy as np
2+
from datetime import datetime, timedelta
3+
import matplotlib.pyplot as plt
4+
from mpl_toolkits.mplot3d import Axes3D
5+
import pandas as pd
6+
from solar_panel import SolarPanel
7+
8+
9+
class SolarOptimizer:
10+
def __init__(self, sun_position, weather_service=None):
11+
"""
12+
Initialize optimizer
13+
14+
Args:
15+
sun_position (SunPosition): SunPosition calculator instance
16+
weather_service (WeatherService): Optional WeatherService instance
17+
"""
18+
self.sun_calculator = sun_position
19+
self.weather_service = weather_service
20+
self.results = []
21+
22+
def simulate_day(self, panel, date, time_step_minutes=30):
23+
"""
24+
Simulate panel output over a day
25+
26+
Args:
27+
panel (SolarPanel): Solar panel object
28+
date (datetime): Date to simulate
29+
time_step_minutes (int): Time step for simulation
30+
31+
Returns:
32+
list: Hourly power outputs
33+
"""
34+
daily_output = []
35+
36+
# Simulate for each time step
37+
for hour in range(24):
38+
for minute in range(0, 60, time_step_minutes):
39+
time = date.replace(hour=hour, minute=minute)
40+
altitude, azimuth = self.sun_calculator.calculate_sun_position(time)
41+
42+
# Get weather data if available
43+
weather_data = None
44+
if self.weather_service:
45+
weather_data = self.weather_service.get_weather_data(
46+
np.degrees(self.sun_calculator.latitude),
47+
np.degrees(self.sun_calculator.longitude),
48+
time
49+
)
50+
51+
if altitude > 0: # Only calculate during daylight
52+
power = panel.calculate_power_output(altitude, azimuth, weather_data)
53+
daily_output.append({
54+
'time': time,
55+
'power': power,
56+
'altitude': altitude,
57+
'azimuth': azimuth,
58+
'weather': weather_data
59+
})
60+
61+
return daily_output
62+
63+
def optimize_placement(self, date, tilt_range=(0, 90), orientation_range=(0, 360)):
64+
"""
65+
Find optimal panel placement
66+
67+
Args:
68+
date (datetime): Date to optimize for
69+
tilt_range (tuple): Range of tilt angles to test
70+
orientation_range (tuple): Range of orientation angles to test
71+
72+
Returns:
73+
tuple: (optimal_tilt, optimal_orientation, max_daily_energy)
74+
"""
75+
max_energy = 0
76+
optimal_tilt = 0
77+
optimal_orientation = 0
78+
results = []
79+
80+
# Test different combinations
81+
for tilt in range(tilt_range[0], tilt_range[1], 5):
82+
for orientation in range(orientation_range[0], orientation_range[1], 5):
83+
panel = SolarPanel(tilt, orientation)
84+
daily_output = self.simulate_day(panel, date)
85+
86+
# Calculate total daily energy
87+
daily_energy = sum(record['power'] for record in daily_output)
88+
results.append({
89+
'tilt': tilt,
90+
'orientation': orientation,
91+
'energy': daily_energy
92+
})
93+
94+
if daily_energy > max_energy:
95+
max_energy = daily_energy
96+
optimal_tilt = tilt
97+
optimal_orientation = orientation
98+
99+
self.results = results
100+
return optimal_tilt, optimal_orientation, max_energy
101+
102+
def visualize_results(self, daily_output):
103+
"""
104+
Create visualization of daily power output
105+
106+
Args:
107+
daily_output (list): Simulation results
108+
"""
109+
# Daily power output plot
110+
plt.figure(figsize=(12, 6))
111+
times = [record['time'] for record in daily_output]
112+
powers = [record['power'] for record in daily_output]
113+
114+
plt.plot(times, powers)
115+
plt.title('Solar Panel Power Output Over Day')
116+
plt.xlabel('Time')
117+
plt.ylabel('Power Output (W)')
118+
plt.xticks(rotation=45)
119+
plt.grid(True)
120+
plt.tight_layout()
121+
plt.show()
122+
123+
# Optimization results heat map
124+
if self.results:
125+
df = pd.DataFrame(self.results)
126+
pivot_table = df.pivot_table(
127+
values='energy',
128+
index='tilt',
129+
columns='orientation',
130+
aggfunc='first'
131+
)
132+
133+
plt.figure(figsize=(12, 8))
134+
plt.imshow(pivot_table, cmap='viridis', aspect='auto')
135+
plt.colorbar(label='Daily Energy (Wh)')
136+
plt.title('Energy Production by Panel Orientation and Tilt')
137+
plt.xlabel('Orientation (degrees)')
138+
plt.ylabel('Tilt (degrees)')
139+
plt.tight_layout()
140+
plt.show()

‎solar_panel.py

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import numpy as np
2+
3+
class SolarPanel:
4+
def __init__(self, tilt, orientation, efficiency=0.2, area=1.6):
5+
"""
6+
Initialize solar panel properties
7+
8+
Args:
9+
tilt (float): Panel tilt angle in degrees
10+
orientation (float): Panel orientation (azimuth) in degrees
11+
efficiency (float): Panel efficiency (default 20%)
12+
area (float): Panel area in square meters
13+
"""
14+
self.tilt = np.radians(tilt)
15+
self.orientation = np.radians(orientation)
16+
self.efficiency = efficiency
17+
self.area = area
18+
19+
def calculate_power_output(self, sun_altitude, sun_azimuth, weather_data=None):
20+
"""
21+
Calculate power output based on sun position and weather conditions
22+
23+
Args:
24+
sun_altitude (float): Sun's altitude in degrees
25+
sun_azimuth (float): Sun's azimuth in degrees
26+
weather_data (dict): Optional weather data including cloud cover and temperature
27+
28+
Returns:
29+
float: Power output in watts
30+
"""
31+
# Base solar intensity (W/m²)
32+
solar_intensity = 1000
33+
34+
# Adjust for weather conditions if available
35+
if weather_data:
36+
cloud_factor = 1 - (weather_data.get('cloud_cover', 0) / 100)
37+
temp_coefficient = 1 - (0.004 * (weather_data.get('temperature', 25) - 25)) # Temperature coefficient
38+
solar_intensity *= cloud_factor * temp_coefficient
39+
40+
# Convert sun position to radians
41+
sun_altitude_rad = np.radians(sun_altitude)
42+
sun_azimuth_rad = np.radians(sun_azimuth)
43+
44+
# Calculate incidence angle using spherical trigonometry
45+
cos_incidence = (
46+
np.cos(sun_altitude_rad) * np.sin(self.tilt) *
47+
np.cos(sun_azimuth_rad - self.orientation) +
48+
np.sin(sun_altitude_rad) * np.cos(self.tilt)
49+
)
50+
51+
# Calculate power output
52+
if cos_incidence > 0:
53+
# Add air mass effect
54+
air_mass = 1 / (np.sin(sun_altitude_rad) + 0.50572 * (6.07995 + sun_altitude)**-1.6364)
55+
atmospheric_loss = 0.7 ** air_mass
56+
57+
power = solar_intensity * cos_incidence * self.efficiency * self.area * atmospheric_loss
58+
return power
59+
return 0

‎sun_position.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import numpy as np
2+
from datetime import datetime
3+
4+
class SunPosition:
5+
def __init__(self, latitude, longitude):
6+
"""
7+
Initialize with location coordinates
8+
9+
Args:
10+
latitude (float): Location latitude in degrees
11+
longitude (float): Location longitude in degrees
12+
"""
13+
self.latitude = np.radians(latitude)
14+
self.longitude = np.radians(longitude)
15+
16+
def calculate_sun_position(self, date_time):
17+
"""
18+
Calculate sun's altitude and azimuth for a given date and time
19+
20+
Args:
21+
date_time (datetime): Date and time for calculation
22+
23+
Returns:
24+
tuple: (altitude, azimuth) in degrees
25+
"""
26+
# Calculate day of year
27+
day_of_year = date_time.timetuple().tm_yday
28+
29+
# Calculate solar declination
30+
declination = np.radians(23.45 * np.sin(np.radians(360/365 * (day_of_year - 81))))
31+
32+
# Calculate hour angle
33+
hour_angle = np.radians(15 * (date_time.hour + date_time.minute/60 - 12))
34+
35+
# Calculate solar altitude
36+
altitude = np.arcsin(
37+
np.sin(self.latitude) * np.sin(declination) +
38+
np.cos(self.latitude) * np.cos(declination) * np.cos(hour_angle)
39+
)
40+
41+
# Calculate solar azimuth
42+
azimuth = np.arccos(
43+
(np.sin(declination) - np.sin(altitude) * np.sin(self.latitude)) /
44+
(np.cos(altitude) * np.cos(self.latitude))
45+
)
46+
47+
# Convert to degrees
48+
altitude_deg = np.degrees(altitude)
49+
azimuth_deg = np.degrees(azimuth)
50+
51+
# Adjust azimuth based on time of day
52+
if hour_angle > 0:
53+
azimuth_deg = 360 - azimuth_deg
54+
55+
return altitude_deg, azimuth_deg

‎test_api.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import requests
2+
import os
3+
4+
api_key = os.getenv('WEATHER_API_KEY')
5+
6+
# Test URL
7+
url = f"http://api.weatherapi.com/v1/current.json?key={"3ab2eda07aaf460086064219250502"}&q=London"
8+
9+
response = requests.get(url)
10+
if response.status_code == 200:
11+
print("API connection successful!")
12+
data = response.json()
13+
print(f"Current temperature in London: {data['current']['temp_c']}°C")
14+
else:
15+
print(f"Error: {response.status_code}")
16+
print(response.text)

‎weather_service.py

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import requests
2+
from datetime import datetime
3+
import json
4+
5+
class WeatherService:
6+
def __init__(self, api_key):
7+
"""
8+
Initialize weather service with API key
9+
10+
Args:
11+
api_key (str): API key for weather service
12+
"""
13+
self.api_key = api_key
14+
self.base_url = "http://api.weatherapi.com/v1"
15+
16+
def get_weather_data(self, latitude, longitude, date_time):
17+
"""
18+
Get weather data for specific location and time
19+
20+
Args:
21+
latitude (float): Location latitude
22+
longitude (float): Location longitude
23+
date_time (datetime): Date and time for weather data
24+
25+
Returns:
26+
dict: Weather data including temperature and cloud cover
27+
"""
28+
try:
29+
# Format date for API
30+
date_str = date_time.strftime("%Y-%m-%d")
31+
32+
# Make API request
33+
response = requests.get(
34+
f"{self.base_url}/history.json",
35+
params={
36+
"key": self.api_key,
37+
"q": f"{latitude},{longitude}",
38+
"dt": date_str
39+
}
40+
)
41+
42+
if response.status_code == 200:
43+
data = response.json()
44+
hour = date_time.hour
45+
46+
# Extract relevant weather data
47+
weather_data = {
48+
'temperature': data['forecast']['forecastday'][0]['hour'][hour]['temp_c'],
49+
'cloud_cover': data['forecast']['forecastday'][0]['hour'][hour]['cloud'],
50+
'humidity': data['forecast']['forecastday'][0]['hour'][hour]['humidity']
51+
}
52+
53+
return weather_data
54+
else:
55+
print(f"Error fetching weather data: {response.status_code}")
56+
return None
57+
58+
except Exception as e:
59+
print(f"Error: {str(e)}")
60+
return None
61+
62+
def get_forecast(self, latitude, longitude, days=1):
63+
"""
64+
Get weather forecast for future optimization
65+
66+
Args:
67+
latitude (float): Location latitude
68+
longitude (float): Location longitude
69+
days (int): Number of days to forecast
70+
71+
Returns:
72+
dict: Forecast data
73+
"""
74+
try:
75+
response = requests.get(
76+
f"{self.base_url}/forecast.json",
77+
params={
78+
"key": self.api_key,
79+
"q": f"{latitude},{longitude}",
80+
"days": days
81+
}
82+
)
83+
84+
if response.status_code == 200:
85+
return response.json()['forecast']['forecastday']
86+
else:
87+
print(f"Error fetching forecast: {response.status_code}")
88+
return None
89+
90+
except Exception as e:
91+
print(f"Error: {str(e)}")
92+
return None

0 commit comments

Comments
 (0)
Please sign in to comment.