Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix PV potential calculation based on tilt angle #3763

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

martin-mosteiro
Copy link
Collaborator

@martin-mosteiro martin-mosteiro commented Jan 21, 2025

This PR fixes issue #3282. As discussed there (and in #3281), tilted PV panels were producing less electricity than flat ones, even at the optimal tilt angle. After investigating, I realized there was an inconsistent sign convention in the azimuth angle between the geographic coordinates and the ones used by the PV model (from Duffie and Beckman, 2013). After fixing this issue, you indeed get the expected improvement in PV performance as a function of tilt angle. In the process of fixing this issue, I have also found an incorrect constant (K=4 m^-1 from Duffie and Beckman was incorrectly coded as K=0.4 #m^-1) and one equation that inexplicably didn't match Duffie and Beckman (2013) (eq. 5.4.2 had a factor of 59.68 instead of 59.7), with marginal effect on the results. This PR fixes those as well. Finally, I have also changed the solar equations so an angle of 0° (flat placement) is possible.

Before fixing the issue, here are the results for different PV tilt angles for one case study building:

  • 0°: yearly 150.21 kWh/m2, peak 121.78 kW/m2
  • 10°: yearly 135.53 kWh/m2, peak 111.63 kW/m2
  • optimal (~34°): yearly 97.22 kWh/m2, peak 73.65 kW/m2

After fixing the issue, here are the results for the same PV tilt angles for the same case study building:

  • 0°: yearly 148.99 kWh/m2, peak 120.85 kW/m2
  • 10°: yearly 161.27 kWh/m2, peak 128.49 kW/m2
  • optimal (~34°): yearly 175.15 kWh/m2, peak 135.84 kW/m2

I have also fixed the azimuth issue on the PVT calculation. I am not sure why the calculation for PVT used a legacy function for angle of incidence (previously used for PV until @shanshanhsieh changed it in 2021), so I've changed it to pvlib.irradiance.aoi to match the PV calculation. After doing so, the PVT potential goes from 134.4 kWh/m2 at 10° and 101.22 kWh/m2 at 30° to 158.27 kWh/m2 at 10° and 171.2 kWh/m2 at 30°.

I have done the same for the angle of incidence for the SC calculation.

How to test this PR

  1. Use any case study building (or buildings) and run the radiation script (if necessary)
  2. Run the PV potential calculation for different tilt angles (e.g., a flat PV panel vs. the optimal angle) using the master branch; the electricity potential per m2 of PV panel should decrease instead of increasing when going from a small tilt angle (e.g., flat placement) compared to the optimal tilt angle.
  3. Repeat the procedure for the branch in this PR. The electricity potential per m2 should instead increase when going from a smaller to an optimal tilt angle.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced solar technology calculations with more precise angle and radiation computations.
    • Integrated pvlib library for improved solar panel performance modeling.
  • Bug Fixes

    • Corrected glazing extinction coefficient in solar constants.
    • Fixed function naming inconsistencies across solar technology modules.
  • Documentation

    • Added reference citations for solar technology parameters.
    • Improved code comments and clarified calculation methodologies.
  • Refactor

    • Updated solar angle calculation methods.
    • Streamlined solar panel property retrieval processes.

If we keep both there are chances that one is updated and the other isn't
the comment makes it seem like the calculation is hard coded for the northern hemisphere, but it actually doesn't matter for the calculation whether it's north or south (since calc_optimal_angle returns an absolute value)
also cleaning up unnecessary code and correcting equation 5.4.2
Copy link

coderabbitai bot commented Jan 21, 2025

Walkthrough

The pull request introduces significant modifications to solar technology-related modules within the City Energy Analyst (CEA) project. The changes primarily focus on updating solar calculations, including adjustments to constants, function signatures, and calculation methods. Key modifications involve renaming functions, updating import statements, integrating the pvlib library, and refining solar angle and radiation calculations across multiple files such as constants.py, photovoltaic.py, photovoltaic_thermal.py, solar_collector.py, and solar_equations.py.

Changes

File Change Summary
cea/technologies/solar/constants.py - Updated glazing extinction coefficient K from 0.4 to 4
cea/technologies/solar/photovoltaic.py - Renamed calc_properties_PV_db to get_properties_PV_db
- Removed latitude parameter from calc_pv_generation
cea/technologies/solar/photovoltaic_thermal.py - Updated import for photovoltaic properties retrieval
- Modified azimuth angle calculation
- Changed radiation type calculation method
- Replaced incident angle calculation method
cea/technologies/solar/solar_collector.py - Added pvlib library import
- Updated azimuth angle sign convention
- Modified incident angle calculation method
cea/utilities/solar_equations.py - Renamed cal_radiation_type to calc_radiation_type
- Updated optimal angle calculation for flat roofs
- Adjusted tilt angle category conditions

Poem

🌞 Solar Rays Dance Anew 🌞
With coefficients bright and true,
Angles shift, calculations gleam,
A rabbit's code, a solar dream!
PVLib joins our radiant team,
Efficiency rises, metrics supreme!

*~ CodeRabbit's Solar Sonnet* 🐰✨

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
cea/technologies/solar/photovoltaic_thermal.py (1)

183-185: Consider removing commented-out code for cleanliness.

The lines of code commented out (lines 183-185) appear to define variables that are no longer used. To improve code readability and maintainability, consider removing these lines if they are not needed.

cea/utilities/solar_equations.py (1)

634-638: Simplify conditional assignment with ternary operator.

The if-else block can be simplified using a ternary operator for brevity and clarity.

Apply this diff to simplify the code:

-    if B != 0:
-        teta_z = degrees(asin(xdir / sin(B))) # surface azimuth before adjusting for sign convention
-    else:
-        # for flat panels, surface azimuth doesn't matter
-        teta_z = 0
+    teta_z = degrees(asin(xdir / sin(B))) if B != 0 else 0  # surface azimuth before adjusting for sign convention
🧰 Tools
🪛 Ruff (0.8.2)

634-638: Use ternary operator teta_z = degrees(asin(xdir / sin(B))) if B != 0 else 0 instead of if-else-block

(SIM108)

cea/technologies/solar/photovoltaic.py (3)

167-169: LGTM! Fixed azimuth angle sign convention.

The adjustment correctly implements Duffie (2013)'s sign convention where south is 0°, east is negative, and west is positive.

Consider adding a reference to the specific page or equation number in Duffie (2013) in the comment for better traceability:

-# Adjust sign convention: in Duffie (2013), south is 0°, east is negative and west is positive (p. 13)
+# Adjust sign convention: in Duffie and Beckman (2013), south is 0°, east is negative and west is positive (Section 1.6, p. 13)

205-208: Remove commented-out code.

The old implementation is now replaced with pvlib, so these commented lines can be safely removed.

-        # teta_rad = pd.Series(index=radiation_Wperm2.index,
-        #                      data=np.vectorize(calc_angle_of_incidence)(g_rad, lat, ha_rad, tilt_rad, teta_z_rad),
-        #                      name='aoi')

393-395: LGTM! Using corrected glazing extinction coefficient.

The transmittance-absorptance calculation now uses the corrected glazing extinction coefficient (K=4 m^-1).

Consider adding a reference to the equation in Duffie and Beckman (2013):

-# transmittance-absorptance product at normal incidence
+# transmittance-absorptance product at normal incidence (Duffie and Beckman, 2013, Eq. 5.1.1)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07f7688 and 08524f8.

📒 Files selected for processing (5)
  • cea/technologies/solar/constants.py (2 hunks)
  • cea/technologies/solar/photovoltaic.py (12 hunks)
  • cea/technologies/solar/photovoltaic_thermal.py (4 hunks)
  • cea/technologies/solar/solar_collector.py (4 hunks)
  • cea/utilities/solar_equations.py (5 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
cea/technologies/solar/photovoltaic.py

200-200: Local variable teta_z_rad is assigned to but never used

Remove assignment to unused variable teta_z_rad

(F841)

cea/utilities/solar_equations.py

634-638: Use ternary operator teta_z = degrees(asin(xdir / sin(B))) if B != 0 else 0 instead of if-else-block

(SIM108)

🪛 GitHub Actions: Ruff
cea/technologies/solar/photovoltaic.py

[error] 8-8: Unused import: math.atan is imported but never used in the code

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: windows-latest
  • GitHub Check: macos-latest
  • GitHub Check: ubuntu-latest
  • GitHub Check: Analyze (python)
🔇 Additional comments (16)
cea/technologies/solar/photovoltaic_thermal.py (6)

16-16: LGTM: Import of pvlib module.

The addition of the pvlib import is appropriate and necessary for the updated solar calculations.


23-23: LGTM: Updated import statement to get_properties_PV_db.

The import statement correctly reflects the renaming of calc_properties_PV_db to get_properties_PV_db.


77-77: LGTM: Updated function call to get_properties_PV_db.

The function call correctly uses the updated get_properties_PV_db function to retrieve panel properties.


178-179: Ensure correct adjustment of azimuth angle sign convention.

The adjustment Az = solar_properties.Az - 180 modifies the azimuth angle to match the sign convention from Duffie (2013). Verify that this change is correctly integrated into all subsequent calculations that use Az, and that there are no inconsistencies in the sign conventions throughout the code.


227-227: LGTM: Updated function call to calc_radiation_type.

The function call correctly reflects the renaming from cal_radiation_type to calc_radiation_type.


231-232: Verify correctness of angle calculations using pvlib.irradiance.aoi.

Ensure that the parameters passed to pvlib.irradiance.aoi are in degrees and correctly represent the surface tilt angle (tilt_angle_deg), surface azimuth angle (teta_z_deg), solar zenith angle (solar_properties.Sz), and solar azimuth angle (Az). Confirm that the units and sign conventions are consistent, especially considering the adjustment made to Az.

cea/technologies/solar/constants.py (2)

4-7: LGTM: Addition of reference material to docstring.

Including the reference to Duffie and Beckman (2013) enhances the documentation and helps users understand the source of the constants.


22-22: Update of K value to correct constant in line with reference.

The glazing extinction coefficient K has been updated from 0.4 to 4, aligning with Duffie and Beckman (2013). Ensure that this change is correctly propagated through all calculations that depend on K.

cea/utilities/solar_equations.py (3)

401-402: Verify assumption of surface azimuth in optimal angle calculation.

The code assumes a surface azimuth of 0 degrees when calculating the optimal angle for flat roofs, implying the optimal angle will be the same for south- and north-facing panels. Confirm that this assumption is valid for your application and that it aligns with the intended behavior of the model.


529-529: LGTM: Updated tilt angle range to include zero degrees.

The condition now includes tilt angles where B equals 0 degrees, correctly categorizing perfectly flat roofs.


783-783: LGTM: Renamed function to calc_radiation_type.

The function name correction from cal_radiation_type to calc_radiation_type fixes a typographical error and improves code readability.

cea/technologies/solar/photovoltaic.py (2)

93-93: LGTM! Improved function naming.

The rename from calc_properties_PV_db to get_properties_PV_db better reflects the function's purpose of retrieving properties rather than calculating them.


203-204: LGTM! Using pvlib for incident angle calculation.

Using pvlib.irradiance.aoi improves reliability by leveraging a well-tested library implementation.

cea/technologies/solar/solar_collector.py (3)

764-766: LGTM! Consistent azimuth angle sign convention.

The adjustment maintains consistency with photovoltaic.py in implementing Duffie (2013)'s sign convention.


773-774: LGTM! Consistent use of pvlib for incident angle calculation.

Using pvlib.irradiance.aoi maintains consistency with photovoltaic.py and improves reliability.


Line range hint 1-1: Verify the impact of the fixes.

The changes address all three objectives from the PR description. Let's verify the impact:

✅ Verification successful

All fixes are properly implemented and consistent

  • Azimuth angle sign convention matches Duffie (2013) across all solar technology files
  • Glazing extinction coefficient K is correctly set to 4
  • Angle of incidence calculation is centralized and validated against Sandia pvlib
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the changes improve PV performance at optimal tilt angles

# Test 1: Check that the azimuth angle adjustment is consistent across files
echo "Checking azimuth angle adjustment consistency..."
rg -A 1 "Adjust sign convention.*Duffie" 

# Test 2: Verify K constant is set to 4
echo "Checking glazing extinction coefficient..."
rg "K = " cea/technologies/solar/constants.py

# Test 3: Look for any remaining references to the old equation
echo "Checking for any remaining references to the old equation..."
rg "calc_angle_of_incidence"

Length of output: 1543


Script:

#!/bin/bash
# Check the implementation of calc_angle_of_incidence
echo "Checking angle of incidence implementation..."
rg -A 10 "def calc_angle_of_incidence" cea/utilities/solar_equations.py

# Verify no other angle calculations exist
echo "Checking for other angle calculations..."
ast-grep --pattern 'def calc_angle_of_incidence'

Length of output: 800


Script:

#!/bin/bash
# Get the full implementation including the return statement
echo "Full implementation of calc_angle_of_incidence..."
rg -A 20 "def calc_angle_of_incidence" cea/utilities/solar_equations.py

Length of output: 998

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
cea/technologies/solar/photovoltaic.py (1)

200-200: Remove unused variable.

The variable teta_z_rad is assigned but never used.

-    teta_z_rad = radians(teta_z_deg)  # surface azimuth
🧰 Tools
🪛 Ruff (0.8.2)

200-200: Local variable teta_z_rad is assigned to but never used

Remove assignment to unused variable teta_z_rad

(F841)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08524f8 and c066da0.

📒 Files selected for processing (1)
  • cea/technologies/solar/photovoltaic.py (13 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
cea/technologies/solar/photovoltaic.py

200-200: Local variable teta_z_rad is assigned to but never used

Remove assignment to unused variable teta_z_rad

(F841)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: windows-latest
  • GitHub Check: macos-latest
  • GitHub Check: ubuntu-latest
🔇 Additional comments (6)
cea/technologies/solar/photovoltaic.py (6)

167-168: LGTM! Correct implementation of azimuth angle adjustment.

The adjustment Az = solar_properties.Az - 180 properly aligns with Duffie (2013) convention where south is 0°, east is negative, and west is positive.


203-207: Good improvement using pvlib for angle calculations.

Replacing the manual angle of incidence calculation with pvlib.irradiance.aoi is a good choice as it uses a well-tested implementation.


323-324: LGTM! Correct implementation of diffuse radiation equations.

The equations for teta_ed and teta_eG now properly match equations 5.4.1 and 5.4.2 from Duffie and Beckman (2013).


425-427: LGTM! Correct implementation of absorbed radiation calculation.

The absorbed radiation calculation properly accounts for beam, diffuse, and ground-reflected components with correct geometric factors.


493-499: LGTM! Clear documentation of assumptions.

The documentation clearly explains the assumptions for:

  1. Tilt angle calculation
  2. Row spacing optimization
  3. Surface azimuth determination

157-157: Verify the impact of function signature changes across the codebase.

The function renaming and parameter changes might affect other modules.

Run this script to check for any missed updates:

Also applies to: 632-632

✅ Verification successful

Function signature changes have been properly propagated

The codebase has been updated consistently:

  • All references use the new get_properties_PV_db function name
  • No calls to calc_pv_generation include the removed latitude parameter
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any remaining references to old function name and signature
rg "calc_properties_PV_db"
rg "calc_pv_generation.*latitude"

Length of output: 62


Script:

#!/bin/bash
# Search for PV-related function definitions and calls
ast-grep --pattern 'def get_properties_PV_db'
ast-grep --pattern 'def calc_pv_generation'

# Search for any PV-related imports and function calls with context
rg -A 2 -B 2 "from cea.technologies.solar.photovoltaic import"
rg -A 2 -B 2 "properties_PV_db|pv_generation" --type python

Length of output: 1343


Script:

#!/bin/bash
# Search for any remaining references to PV-related functions with context
rg -A 2 -B 2 "properties_PV_db|pv_generation"

# Search for any python files containing PV-related content
fd -e py -x grep -l "photovoltaic"

Length of output: 3281

@ShiZhongming
Copy link
Collaborator

Wow - this sounds great by just reading at your text @martin-mosteiro
Will test it soon!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
cea/technologies/solar/photovoltaic.py (2)

167-171: LGTM: Azimuth angle sign convention aligns with Duffie (2013).

The adjustment of azimuth angle sign convention (south = 0°, east negative, west positive) correctly follows the standard convention in Duffie (2013).

Consider using a ternary operator for more concise code:

-if latitude >= 0:
-    Az = solar_properties.Az - 180  # south is 0°, east is negative and west is positive (p. 13)
-else:
-    Az = solar_properties.Az  # north is 0°
+Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az  # south is 0° for northern hemisphere
🧰 Tools
🪛 Ruff (0.8.2)

168-171: Use ternary operator Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az instead of if-else-block

(SIM108)


206-210: Enhancement: Improved angle of incidence calculation using pvlib.

The switch to pvlib.irradiance.aoi improves accuracy and maintainability by using a well-tested library implementation.

The commented-out code can be safely removed since it's replaced by the pvlib implementation.

cea/technologies/solar/photovoltaic_thermal.py (1)

178-182: LGTM: Consistent azimuth angle convention.

The azimuth angle adjustment matches the implementation in photovoltaic.py, maintaining consistency.

Consider using a ternary operator for more concise code:

-if latitude >= 0:
-    Az = solar_properties.Az - 180  # south is 0°, east is negative and west is positive (p. 13)
-else:
-    Az = solar_properties.Az  # north is 0°
+Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az  # south is 0° for northern hemisphere
🧰 Tools
🪛 Ruff (0.8.2)

179-182: Use ternary operator Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az instead of if-else-block

(SIM108)

cea/technologies/solar/solar_collector.py (1)

764-768: LGTM: Consistent azimuth angle convention across solar modules.

The azimuth angle adjustment maintains consistency with photovoltaic.py and photovoltaic_thermal.py.

Consider using a ternary operator for more concise code:

-if latitude_deg >= 0:
-    Az = solar_properties.Az - 180  # south is 0°, east is negative and west is positive (p. 13)
-else:
-    Az = solar_properties.Az  # north is 0°
+Az = solar_properties.Az - 180 if latitude_deg >= 0 else solar_properties.Az  # south is 0° for northern hemisphere
🧰 Tools
🪛 Ruff (0.8.2)

765-768: Use ternary operator Az = solar_properties.Az - 180 if latitude_deg >= 0 else solar_properties.Az instead of if-else-block

(SIM108)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c066da0 and 23b811d.

📒 Files selected for processing (3)
  • cea/technologies/solar/photovoltaic.py (12 hunks)
  • cea/technologies/solar/photovoltaic_thermal.py (4 hunks)
  • cea/technologies/solar/solar_collector.py (4 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
cea/technologies/solar/solar_collector.py

765-768: Use ternary operator Az = solar_properties.Az - 180 if latitude_deg >= 0 else solar_properties.Az instead of if-else-block

(SIM108)

cea/technologies/solar/photovoltaic_thermal.py

179-182: Use ternary operator Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az instead of if-else-block

(SIM108)

cea/technologies/solar/photovoltaic.py

168-171: Use ternary operator Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az instead of if-else-block

(SIM108)


203-203: Local variable teta_z_rad is assigned to but never used

Remove assignment to unused variable teta_z_rad

(F841)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: windows-latest
  • GitHub Check: macos-latest
  • GitHub Check: ubuntu-latest
🔇 Additional comments (3)
cea/technologies/solar/photovoltaic.py (1)

326-326: Fixed equation 5.4.2 for diffuse radiation calculation.

The correction aligns with Duffie and Beckman (2013) by using the correct coefficient in the equation.

cea/technologies/solar/photovoltaic_thermal.py (1)

233-235: Enhancement: Consistent use of pvlib for angle calculations.

The switch to pvlib.irradiance.aoi maintains consistency with photovoltaic.py.

cea/technologies/solar/solar_collector.py (1)

776-777: Enhancement: Standardized angle calculations using pvlib.

The switch to pvlib.irradiance.aoi completes the standardization of angle calculations across all solar technology modules.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
cea/utilities/solar_equations.py (1)

634-639: Good addition of special case for flat panels!

The code correctly handles flat panels by setting azimuth to 0° since orientation doesn't matter when tilt is 0°.

Consider using a ternary operator for more concise code:

-    if B != 0:
-        teta_z = degrees(asin(xdir / sin(B))) # surface azimuth before adjusting for sign convention
-    else:
-        # for flat panels, surface azimuth doesn't matter
-        teta_z = 0
+    # surface azimuth before adjusting for sign convention (0° for flat panels as azimuth doesn't matter)
+    teta_z = degrees(asin(xdir / sin(B))) if B != 0 else 0
🧰 Tools
🪛 Ruff (0.8.2)

634-638: Use ternary operator teta_z = degrees(asin(xdir / sin(B))) if B != 0 else 0 instead of if-else-block

(SIM108)

cea/technologies/solar/photovoltaic.py (1)

167-172: Excellent fix for azimuth sign convention!

The code now correctly implements the Duffie (2013) convention:

  • Northern hemisphere: south is 0°, east is negative, west is positive
  • Southern hemisphere: north is 0°

This resolves the core issue where tilted panels were yielding less electricity than flat panels.

Consider using a ternary operator for more concise code:

-    if latitude >= 0:
-        Az = solar_properties.Az - 180  # south is 0°, east is negative and west is positive (p. 13)
-    else:
-        Az = solar_properties.Az  # north is 0°
+    # Adjust azimuth: In northern hemisphere, south is 0° (east negative, west positive)
+    # In southern hemisphere, north is 0°
+    Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az
🧰 Tools
🪛 Ruff (0.8.2)

168-171: Use ternary operator Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az instead of if-else-block

(SIM108)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23b811d and 7421944.

📒 Files selected for processing (2)
  • cea/technologies/solar/photovoltaic.py (12 hunks)
  • cea/utilities/solar_equations.py (5 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
cea/technologies/solar/photovoltaic.py

168-171: Use ternary operator Az = solar_properties.Az - 180 if latitude >= 0 else solar_properties.Az instead of if-else-block

(SIM108)


203-203: Local variable teta_z_rad is assigned to but never used

Remove assignment to unused variable teta_z_rad

(F841)

cea/utilities/solar_equations.py

634-638: Use ternary operator teta_z = degrees(asin(xdir / sin(B))) if B != 0 else 0 instead of if-else-block

(SIM108)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: windows-latest
  • GitHub Check: macos-latest
  • GitHub Check: ubuntu-latest
🔇 Additional comments (5)
cea/utilities/solar_equations.py (3)

239-244: Documentation improvements look good!

The added comments clearly explain the filtering conditions for sensor points and hours with low radiation potential.


399-402: Documentation improvement enhances understanding!

The added comment correctly explains that panels facing the equator (surface azimuth = 0° or 180°) yield identical results, which is physically accurate.


529-529: Good fix to include 0° tilt angle!

The condition now correctly includes perfectly flat panels (0°) in the first category, which aligns with the PR objectives.

cea/technologies/solar/photovoltaic.py (2)

326-326: Good fix for equation 5.4.2!

The implementation now correctly matches Duffie and Beckman (2013), and the variable naming is more consistent.


496-502: Documentation improvements enhance code understanding!

The assumptions about tilt angles are now clearly documented:

  1. Tilted roof: panel tilt matches roof tilt
  2. Wall: 90° tilt
  3. Flat roof: tilt determined by Quinn et al. method

Comment on lines +202 to +211
# teta_z_deg = radians(teta_z_deg) # surface azimuth
teta_z_rad = radians(teta_z_deg) # surface azimuth

# calculate effective incident angles necessary
teta_deg = pvlib.irradiance.aoi(tilt_angle_deg, teta_z_deg, solar_properties.Sz, solar_properties.Az)
teta_rad = teta_rad = [radians(x) for x in teta_deg]
teta_deg = pvlib.irradiance.aoi(tilt_angle_deg, teta_z_deg, solar_properties.Sz, Az)
teta_rad = [radians(x) for x in teta_deg]
# teta_rad = pd.Series(index=radiation_Wperm2.index,
# data=np.vectorize(calc_angle_of_incidence)(g_rad, lat, ha_rad, tilt_rad, teta_z_rad),
# name='aoi')

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Good switch to pvlib for angle of incidence calculation!

Using pvlib.irradiance.aoi is a better choice as it's a well-tested implementation. However, some cleanup is needed:

  1. Remove the commented-out code (lines 208-210) as it's replaced by pvlib
  2. Remove the unused variable teta_z_rad (line 203)
-    teta_z_rad = radians(teta_z_deg)  # surface azimuth
     teta_deg = pvlib.irradiance.aoi(tilt_angle_deg, teta_z_deg, solar_properties.Sz, Az)
     teta_rad = [radians(x) for x in teta_deg]
-    # teta_rad = pd.Series(index=radiation_Wperm2.index,
-    #                      data=np.vectorize(calc_angle_of_incidence)(g_rad, lat, ha_rad, tilt_rad, teta_z_rad),
-    #                      name='aoi')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# teta_z_deg = radians(teta_z_deg) # surface azimuth
teta_z_rad = radians(teta_z_deg) # surface azimuth
# calculate effective incident angles necessary
teta_deg = pvlib.irradiance.aoi(tilt_angle_deg, teta_z_deg, solar_properties.Sz, solar_properties.Az)
teta_rad = teta_rad = [radians(x) for x in teta_deg]
teta_deg = pvlib.irradiance.aoi(tilt_angle_deg, teta_z_deg, solar_properties.Sz, Az)
teta_rad = [radians(x) for x in teta_deg]
# teta_rad = pd.Series(index=radiation_Wperm2.index,
# data=np.vectorize(calc_angle_of_incidence)(g_rad, lat, ha_rad, tilt_rad, teta_z_rad),
# name='aoi')
# teta_z_deg = radians(teta_z_deg) # surface azimuth
# calculate effective incident angles necessary
teta_deg = pvlib.irradiance.aoi(tilt_angle_deg, teta_z_deg, solar_properties.Sz, Az)
teta_rad = [radians(x) for x in teta_deg]
🧰 Tools
🪛 Ruff (0.8.2)

203-203: Local variable teta_z_rad is assigned to but never used

Remove assignment to unused variable teta_z_rad

(F841)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
2 participants