question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

IAM that supports AR coating like Fresnel

See original GitHub issue

Problem

Currently pvlib supports the DeSoto physical model (similar to normal glass), ASHRAE, Martin & Ruiz, and SAPM polynomial, but it doesn’t have a pure Fresnel model that allows additional interfaces like an AR coating.

  • DeSoto physical model is most similar to the Fresnel for normal glass but only has one interface, so is limited to IAM curves below it only, while an AR coating would have a greater ρ
  • Martin & Ruiz could be used to approximate an AR coated glass if the correct a_r were known. The default of a_r=0.16 is slightly above the normal glass Fresnel IAM, but an a_r=0.14 seems to match an AR coating with index of refraction of 1.2 most closely.

pvlib_iam

Proposal

a new method in pvl.iam.fresnel_ar(aoi, n_ar=1.2, n_air=1.0, n_glass=1.56) that implements the Fresnel equation

Alternative

Suggest readers to use Martin & Ruiz with a_r=0.14 instead of default.

additional content

PVsyst has switched to Fresnel equations. We can duplicate their methods ignoring additional reflections and the encapsulant layer: Fresnel-v-ASHRAE

import numpy as np
import matplotlib.pyplot as plt
plt.ion()


# constants
n_glass = 1.56
n_air = 1.0
theta_inc = np.linspace(0, 88, 100)


def snell(theta_1, n1, n2):
    """Snell's equation"""
    sintheta_2 = n1/n2 * np.sin(np.radians(theta_1))
    return sintheta_2, np.degrees(np.arcsin(sintheta_2))


def refl_s(theta_1, theta_2, n1, n2):
    """Fresnel's equation"""
    n1_costheta_1 = n1*np.cos(np.radians(theta_1))
    n2_costheta_2 = n2*np.cos(np.radians(theta_2))
    return np.abs((n1_costheta_1 - n2_costheta_2)/(n1_costheta_1 + n2_costheta_2))**2


def refl_p(theta_1, theta_2, n1, n2):
    """Fresnel's equation"""
    n1_costheta_2 = n1*np.cos(np.radians(theta_2))
    n2_costheta_1 = n2*np.cos(np.radians(theta_1))
    return np.abs((n1_costheta_2 - n2_costheta_1)/(n1_costheta_2 + n2_costheta_1))**2


def refl_eff(rs, rp):
    """effective reflectivity"""
    return (rs+rp)/2


def trans(refl):
    """transmissivity"""
    return 1-refl


def refl0(n1, n2):
    """reflectivity at normal incidence"""
    return np.abs((n1-n2)/(n1+n2))**2


def fresnel(theta_inc, n1=n_air, n2=n_glass):
    """calculate IAM using Fresnel's Law"""
    _, theta_tr = snell(theta_inc, n1, n2)
    rs = refl_s(theta_inc, theta_tr, n1, n2)
    rp = refl_p(theta_inc, theta_tr, n1, n2)
    reff = refl_eff(rs, rp)
    r0 = refl0(n1, n2)
    return trans(reff)/trans(r0)


def ashrae(theta_inc, b0=0.05):
    """ASHRAE equation"""
    return 1 - b0*(1/np.cos(np.radians(theta_inc)) - 1)


def fresnel_ar(theta_inc, n_ar, n1=n_air, n2=n_glass):
    """calculate IAM using Fresnel's law with AR"""
    # use fresnel() for n2=n_ar
    _, theta_ar = snell(theta_inc, n1, n_ar)
    rs_ar1 = refl_s(theta_inc, theta_ar, n1, n_ar)
    rp_ar1 = refl_p(theta_inc, theta_ar, n1, n_ar)
    r0_ar1 = refl0(n1, n_ar)
    # repeat with fresnel() with n1=n_ar
    _, theta_tr = snell(theta_ar, n_ar, n2)
    rs = refl_s(theta_ar, theta_tr, n_ar, n2)
    rp = refl_p(theta_ar, theta_tr, n_ar, n2)
    # note that combined reflectivity is product of transmissivity!
    # so... rho12 = 1 - (1-rho1)(1-rho2) 
    reff = refl_eff(1-(1-rs_ar1)*(1-rs), 1-(1-rp_ar1)*(1-rp))
    r0 = 1-(1-refl0(n_ar, n2))*(1-r0_ar1)
    return trans(reff)/trans(r0)


# plot Fresnel for normal glass and ASHRAE
plt.plot(theta_inc, fresnel(theta_inc))
plt.plot(theta_inc, ashrae(theta_inc))

# calculate IAM for AR with n=1.1 and plot
iam_ar11 = fresnel_ar(theta_inc, n_ar=1.1)
plt.plot(theta_inc, iam_ar11)

# repeat for AR with n=1.2
iam_ar12 = fresnel_ar(theta_inc, n_ar=1.2)
plt.plot(theta_inc, iam_ar12)

# make plot pretty
plt.legend(['Fresnel, normal glass', 'ASHRAE, $b_0=0.05$', 'Fresnel $n_{AR}=1.1$', 'Fresnel $n_{AR}=1.2$'])
plt.title("IAM correction, Fresnel vs. ASHRAE, using basic eqn's")
plt.ylabel('IAM')
plt.xlabel(r'incidence angle $\theta_{inc} [\degree]$')
plt.grid()
plt.ylim([0.55,1.05])

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:4
  • Comments:12 (12 by maintainers)

github_iconTop GitHub Comments

2reactions
adriessecommented, Dec 20, 2022

Well, from my perspective if it is clear you implement these formulas correctly for a two-layer window then it doesn’t need to match PVsyst or claim that it is doing the same as PVsyst.

2reactions
kdebrabcommented, Dec 16, 2022

In #1616 I submitted a proposal to extend the ìam.physical() function with an extra optional argument n_ar to support AR coating.

Main differences with the code proposed above by @mikofski :

  • instead of adding new functions fresnel and fresnel_ar, the existing function physical (which is equivalent with fresnel) is extended with support for AR coating through the optional argument n_ar.
  • internal transmissions are no longer ignored but taken into account.

Main differences with the previous code of iam.physical() (besides support for AR coating):

  • the Fresnel equations for the reflectance for parallel and perpendicular polarized light has been adapted from the tan formulation (as used by Desoto) to the much more common cos formulation (as used everywhere else). As far as I can tell they are mathematically fully equivalent (in combination with Snell’s law), but the cos formulation doesn’t suffer from 0/0 division for aoi equal to zero. That’s why no special handling is needed anymore for angles near zero.
  • the result for 90° is no longer exactly 0, but (on my laptop) 3.47262923e-16, which I think is fully fine. But for that I had to include a_tol in the existing test_physical unit test.
  • (probably not relevant) though aoi is normally expected to be between 0° and 180°, there were apparently also tests for negative aoi. The new code is now also consistent between positive and negative angles for angles outside the range -360°, 360° (though I didn’t include tests for it as I doubt whether we should really support that).

Main differences with PVSyst:

  • I don’t know the reason but there is still a (very minor) difference between the values in PVSyst compared to the values returned by this function.
Read more comments on GitHub >

github_iconTop Results From Across the Web

Array incidence loss (IAM) - PVsyst
The new modules with "AR coating" or "Textured glass" will use the Fresnel calculation with AR coating.
Read more >
Anti-Reflective Coating Materials: A Holistic Review from PV ...
Antireflection coatings (ARCs) are predominantly utilized to suppress the Fresnel reflection losses when light propagates from one medium to ...
Read more >
Diffuse IAM Calculation - pvlib python - Read the Docs
The incident angle modifier (IAM) is defined as the ratio of light transmitted at the ... n=1.526 and a glass with anti-reflective (AR)...
Read more >
Anti-Reflective Coatings | Creating Solutions
The term anti-reflective (AR) coating is deceiving. What it should be called is a “transmission booster” or “light booster for a lens.
Read more >
Antifogging antireflective coatings on Fresnel lenses by ...
Fresnel lenses turn out to have excellent antireflective and antifogging properties after spin-assembling both solid and mesoporous silica nanoparticles.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found