Skip to content

emission

Compute rough emission curves.

bb_emission_eq(sun_theta, wls, albedo, emiss, solar_dist)

Return isothermal blackbody emission spectrum assuming radiative equilibrium.

Parameters:

Name Type Description Default
sun_theta float

Solar incidence angle [deg].

required
wls ndarray

Wavelengths [microns].

required
albedo float

Hemispherical broadband albedo.

required
emiss float

Emissivity.

required
solar_dist float

Solar distance (au).

required

Returns:

Type Description
ndarray

Blackbody emission spectrum vs. wls [W m^-2].

Source code in roughness/emission.py
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
def bb_emission_eq(sun_theta, wls, albedo, emiss, solar_dist):
    """
    Return isothermal blackbody emission spectrum assuming radiative
    equilibrium.

    Parameters:
        sun_theta (float): Solar incidence angle [deg].
        wls (ndarray): Wavelengths [microns].
        albedo (float): Hemispherical broadband albedo.
        emiss (float): Emissivity.
        solar_dist (float): Solar distance (au).

    Returns:
        (np.ndarray): Blackbody emission spectrum vs. wls [W m^-2].
    """
    cinc = np.cos(np.deg2rad(sun_theta))
    albedo_array = get_albedo_scaling(sun_theta, albedo)
    rad_isot = get_emission_eq(cinc, albedo_array, emiss, solar_dist)
    temp_isot = get_temp_sb(rad_isot)
    bb_emission = cmrad2mrad(bbrw(wls, temp_isot))
    return bb_emission

bbr(wavenumber, temp, radunits='wn')

Return blackbody radiance at given wavenumber(s) and temp(s).

Units='wn' for wavenumber: W/(m^2 sr cm^-1) 'wl' for wavelength: W/(m^2 sr μm)

Translated from ff_bbr.c in davinci_2.22.

Parameters:

Name Type Description Default
wavenumber num | arr

Wavenumber(s) to compute radiance at [cm^-1]

required
temp num | arr

Temperatue(s) to compute radiance at [K]

required
radunits str

Return units in terms of wn or wl (wn: cm^-1; wl: μm)

'wn'
Source code in roughness/emission.py
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
def bbr(wavenumber, temp, radunits="wn"):
    """
    Return blackbody radiance at given wavenumber(s) and temp(s).

    Units='wn' for wavenumber: W/(m^2 sr cm^-1)
          'wl' for wavelength: W/(m^2 sr μm)

    Translated from ff_bbr.c in davinci_2.22.

    Parameters:
        wavenumber (num | arr): Wavenumber(s) to compute radiance at [cm^-1]
        temp (num | arr): Temperatue(s) to compute radiance at [K]
        radunits (str): Return units in terms of wn or wl (wn: cm^-1; wl: μm)
    """
    # Derive Planck radiation constants a and b from h, c, Kb
    a = 2 * HC * CCM**2  # [J cm^2 / s] = [W cm^2]
    b = HC * CCM / KB  # [cm K]

    if np.isscalar(temp):
        if temp <= 0:
            temp = 1
    elif isinstance(temp, xr.DataArray):
        temp = temp.clip(min=1)
    else:
        temp[temp < 0] = 1
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        rad = (a * wavenumber**3) / (np.exp(b * wavenumber / temp) - 1.0)
    if radunits == "wl":
        rad = wnrad2wlrad(wavenumber, rad)  # [W/(cm^2 sr μm)]
    return rad

bbrw(wavelength, temp, radunits='wl')

Return blackbody radiance at given wavelength(s) and temp(s).

Parameters:

Name Type Description Default
wavelength num | arr

Wavelength(s) to compute radiance at [microns]

required
temp num | arr

Temperatue(s) to compute radiance at [K]

required
Source code in roughness/emission.py
852
853
854
855
856
857
858
859
860
def bbrw(wavelength, temp, radunits="wl"):
    """
    Return blackbody radiance at given wavelength(s) and temp(s).

    Parameters:
        wavelength (num | arr): Wavelength(s) to compute radiance at [microns]
        temp (num | arr): Temperatue(s) to compute radiance at [K]
    """
    return bbr(wl2wn(wavelength), temp, radunits)

btemp(wavenumber, radiance, radunits='wn')

Return brightness temperature [K] from wavenumber and radiance.

Translated from ff_bbr.c in davinci_2.22.

Source code in roughness/emission.py
863
864
865
866
867
868
869
870
871
872
873
874
875
876
def btemp(wavenumber, radiance, radunits="wn"):
    """
    Return brightness temperature [K] from wavenumber and radiance.

    Translated from ff_bbr.c in davinci_2.22.
    """
    # Derive Planck radiation constants a and b from h, c, Kb
    a = 2 * HC * CCM**2  # [J cm^2 / s] = [W cm^2]
    b = HC * CCM / KB  # [cm K]
    if radunits == "wl":
        # [W/(cm^2 sr μm)] -> [W/(cm^2 sr cm^-1)]
        radiance = wlrad2wnrad(wn2wl(wavenumber), radiance)
    T = (b * wavenumber) / np.log(1.0 + (a * wavenumber**3 / radiance))
    return T

btempw(wavelength, radiance, radunits='wl')

Return brightness temperature [K] at wavelength given radiance.

Source code in roughness/emission.py
879
880
881
882
883
884
def btempw(wavelength, radiance, radunits="wl"):
    """
    Return brightness temperature [K] at wavelength given radiance.

    """
    return btemp(wl2wn(wavelength), radiance, radunits=radunits)

cmrad2mrad(cmrad)

Convert radiance from units W/(cm^2 sr μm) to W/(m^2 sr μm).

Source code in roughness/emission.py
919
920
921
def cmrad2mrad(cmrad):
    """Convert radiance from units W/(cm^2 sr μm) to W/(m^2 sr μm)."""
    return cmrad * 1e4  # [W/(cm^2 sr μm)] -> [W/(m^2 sr μm)]

cos_facet_sun_inc(surf_theta, surf_az, sun_theta, sun_az)

Return cos solar incidence of surface slopes (surf_theta, surf_az) given sun location (sun_theta, sun_az) using the dot product.

If cinc < 0, angle b/t surf and sun is > 90 degrees. Limit these values to cinc = 0.

Parameters:

Name Type Description Default
surf_theta float | ndarray

Surface slope(s) in degrees.

required
surf_az float | ndarray

Surface azimuth(s) in degrees.

required
sun_theta float

Solar incidence angle in degrees.

required
sun_az float

Solar azimuth from North in degrees.

required
Source code in roughness/emission.py
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
def cos_facet_sun_inc(surf_theta, surf_az, sun_theta, sun_az):
    """
    Return cos solar incidence of surface slopes (surf_theta, surf_az) given
    sun location (sun_theta, sun_az) using the dot product.

    If cinc < 0, angle b/t surf and sun is > 90 degrees. Limit these values to
    cinc = 0.

    Parameters:
        surf_theta (float | np.ndarray): Surface slope(s) in degrees.
        surf_az (float | np.ndarray): Surface azimuth(s) in degrees.
        sun_theta (float): Solar incidence angle in degrees.
        sun_az (float): Solar azimuth from North in degrees.
    """
    facet_vec = rh.sph2cart(np.deg2rad(surf_theta), np.deg2rad(surf_az))
    target_vec = rh.sph2cart(np.deg2rad(sun_theta), np.deg2rad(sun_az))
    cinc = (facet_vec * target_vec).sum(axis=2)
    cinc[cinc < 0] = 0
    if isinstance(surf_theta, xr.DataArray):
        cinc = xr.ones_like(surf_theta) * cinc
        cinc.name = "cos(inc)"
    return cinc

directional_emiss(theta)

Return directional emissivity at angle theta.

Source code in roughness/emission.py
615
616
617
618
619
620
621
622
623
def directional_emiss(theta):
    """
    Return directional emissivity at angle theta.
    """
    thetar = np.deg2rad(theta)
    # emiss = 0.993 - 0.0302 * thetar - 0.0897 * thetar**2
    # Powell 2023 (mean goes down to ~0.8)
    emiss = np.cos(thetar) ** 0.151
    return np.clip(emiss, 0.8, 1)

emission_2component(wavelength, temp_illum, temp_shadow, shadow_table, emissivity=1)

Return roughness emission spectrum at given wls given the temperature of illuminated and shadowed surfaces and fraction of the surface in shadow.

Parameters:

Name Type Description Default
wavelength num | arr

Wavelength(s) [microns]

required
temp_illum num | arr

Temperature(s) of illuminated fraction of surface

required
temp_shadow num | arr

Temperature(s) of shadowed fraction of surface

required
shadow_table num | arr

Proportion(s) of surface in shadow

required
Source code in roughness/emission.py
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
def emission_2component(
    wavelength,
    temp_illum,
    temp_shadow,
    shadow_table,
    emissivity=1,
):
    """
    Return roughness emission spectrum at given wls given the temperature
    of illuminated and shadowed surfaces and fraction of the surface in shadow.

    Parameters:
        wavelength (num | arr): Wavelength(s) [microns]
        temp_illum (num | arr): Temperature(s) of illuminated fraction of
            surface
        temp_shadow (num | arr): Temperature(s) of shadowed fraction of
            surface
        shadow_table (num | arr): Proportion(s) of surface in shadow
    """
    rad_illum = temp2rad(wavelength, temp_illum, emissivity)
    rad_shade = temp2rad(wavelength, temp_shadow, emissivity)
    rad_total = rad_illum * (1 - shadow_table) + rad_shade * shadow_table
    return rad_total

emission_spectrum(emission_table, weight_table=None)

Return emission spectrum as weighted sum of facets in emission_table.

Weight_table must have same slope, az coords as emission_table.

Parameters:

Name Type Description Default
emission_table DataArray

Table of radiance at each facet.

required
weight_table DataArray

Table of facet weights.

None
Source code in roughness/emission.py
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
def emission_spectrum(emission_table, weight_table=None):
    """
    Return emission spectrum as weighted sum of facets in emission_table.

    Weight_table must have same slope, az coords as emission_table.

    Parameters:
        emission_table (xr.DataArray): Table of radiance at each facet.
        weight_table (xr.DataArray): Table of facet weights.
    """
    if weight_table is None and isinstance(emission_table, xr.DataArray):
        weight_table = 1 / (len(emission_table.az) * len(emission_table.theta))
    elif weight_table is None:
        weight_table = 1 / np.prod(emission_table.shape[:2])

    if isinstance(emission_table, xr.DataArray):
        weighted = emission_table.weighted(weight_table.fillna(0))
        spectrum = weighted.sum(dim=("az", "theta"))
        spectrum.name = "Radiance [W/m²/sr/μm]"
    else:
        weighted = emission_table * weight_table
        spectrum = np.sum(weighted, axis=(0, 1))
    return spectrum

get_albedo_scaling(inc, albedo=0.12, a=None, b=None, mode='vasavada')

Return albedo scaled with solar incidence angle (eq A5; Keihm, 1984).

Parameters a and b have been measured since Keihm (1984) and may differ for non-lunar bodies. Optionally supply a and b or select a mode: modes: 'king': Use lunar a, b from King et al. (2020) 'hayne': Use lunar a, b from Hayne et al. (2017) 'vasavada': Use lunar a, b from Vasavada et al. (2012) else: Use original a, b from Keihm (1984) Note: if a or b is supplied, both must be supplied and supercede mode

Parameters:

Name Type Description Default
albedo num

Lunar average bond albedo (at inc=0).

0.12
inc num | arr

Solar incidence angle [deg].

required
mode str

Scale albedo based on published values of a, b.

'vasavada'
Source code in roughness/emission.py
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
def get_albedo_scaling(inc, albedo=0.12, a=None, b=None, mode="vasavada"):
    """
    Return albedo scaled with solar incidence angle (eq A5; Keihm, 1984).

    Parameters a and b have been measured since Keihm (1984) and may differ for
    non-lunar bodies. Optionally supply a and b or select a mode:
        modes:
            'king': Use lunar a, b from King et al. (2020)
            'hayne': Use lunar a, b from Hayne et al. (2017)
            'vasavada': Use lunar a, b from Vasavada et al. (2012)
            else: Use original a, b from Keihm (1984)
    Note: if a or b is supplied, both must be supplied and supercede mode

    Parameters:
        albedo (num): Lunar average bond albedo (at inc=0).
        inc (num | arr): Solar incidence angle [deg].
        mode (str): Scale albedo based on published values of a, b.
    """
    if a is not None and b is not None:
        pass
    elif mode == "king":
        # King et al. (2020)
        a = 0.0165
        b = -0.03625
    elif mode == "hayne":
        # Hayne et al. (2017)
        a = 0.06
        b = 0.25
    elif mode == "vasavada":
        # Vasavada et al. (2012)
        a = 0.045
        b = 0.14
    else:
        # Keihm et al. (1984)
        a = 0.03
        b = 0.14
    alb_scaled = albedo + a * (inc / 45) ** 3 + b * (inc / 90) ** 8
    return alb_scaled

get_emission_eq(cinc, albedo, emissivity, solar_dist)

Return radiated emission assuming radiative equillibrium.

Parameters:

Name Type Description Default
cinc num

Cosine of solar incidence.

required
albedo num

Hemispherical broadband albedo.

required
emissivity num

Hemispherical broadband emissivity.

required
solar_dist num

Solar distance (au).

required
Source code in roughness/emission.py
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
def get_emission_eq(cinc, albedo, emissivity, solar_dist):
    """
    Return radiated emission assuming radiative equillibrium.

    Parameters:
        cinc (num): Cosine of solar incidence.
        albedo (num): Hemispherical broadband albedo.
        emissivity (num): Hemispherical broadband emissivity.
        solar_dist (num): Solar distance (au).
    """
    f_sun = SC / solar_dist**2
    rad_eq = f_sun * cinc * (1 - albedo) / emissivity
    if isinstance(cinc, xr.DataArray):
        rad_eq.name = "Radiance [W/m²]"
    return rad_eq

get_emissivity_table(facet_theta, facet_az, sc_theta, sc_az)

Return emissivity table for given shadow_table and solar incidence angle.

Source code in roughness/emission.py
769
770
771
772
773
774
775
776
777
def get_emissivity_table(facet_theta, facet_az, sc_theta, sc_az):
    """
    Return emissivity table for given shadow_table and solar incidence angle.

    """
    cinc = rn.get_facet_cos_theta(facet_theta, facet_az, sc_theta, sc_az)
    # Bandfield et al. (2015) nighttime fit
    emissivity = 0.99 * cinc**0.14
    return emissivity

get_facet_temp_illum_eq(shadow_table, sun_theta, sun_az, albedo, emiss, solar_dist, rerad=True)

Return temperature of illuminated facets assuming radiative equilibrium. Uses shadow_table generated by get_shadow_table.

Parameters:

Name Type Description Default
shadow_table ndarray

Shadow table.

required
sun_theta float

Solar incidence angle.

required
sun_az float

Solar azimuth.

required
albedo float

Hemispherical broadband albedo.

required
emiss float

Emissivity.

required
solar_dist float

Solar distance (au).

required
rerad bool

If True, include re-radiation from adjacent facets. Default is True.

True

Returns:

Type Description
ndarray

Facet temperatures.

Source code in roughness/emission.py
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
def get_facet_temp_illum_eq(
    shadow_table, sun_theta, sun_az, albedo, emiss, solar_dist, rerad=True
):
    """
    Return temperature of illuminated facets assuming radiative
    equilibrium. Uses shadow_table generated by get_shadow_table.

    Parameters:
        shadow_table (ndarray): Shadow table.
        sun_theta (float): Solar incidence angle.
        sun_az (float): Solar azimuth.
        albedo (float): Hemispherical broadband albedo.
        emiss (float): Emissivity.
        solar_dist (float): Solar distance (au).
        rerad (bool, optional): If True, include re-radiation from adjacent
            facets. Default is True.

    Returns:
        (np.ndarray): Facet temperatures.
    """
    # Produce sloped facets at same resolution as shadow_table
    f_theta, f_az = rh.facet_grids(shadow_table)
    cinc = rn.get_facet_cos_theta(f_theta, f_az, sun_theta, sun_az)
    # cinc = cos_facet_sun_inc(f_theta, f_az, sun_theta, sun_az)
    facet_inc = np.degrees(np.arccos(cinc))
    albedo_array = get_albedo_scaling(facet_inc, albedo)
    # Compute total radiance of sunlit facets assuming radiative eq
    rad_illum = get_emission_eq(cinc, albedo_array, emiss, solar_dist)
    if rerad:
        rad0 = rad_illum[0, 0]  # Radiance of level surface facet
        alb0 = albedo_array[0, 0]  # Albedo of level surface facet
        rad_illum += get_reradiation(
            cinc, f_theta, albedo, emiss, solar_dist, rad0, alb0
        )

    # Convert to temperature with Stefan-Boltzmann law
    facet_temp_illum = get_temp_sb(rad_illum)
    return facet_temp_illum

get_rad_factor(rad, solar_irr, emission=None, emissivity=None)

Return radiance factor (I/F) given observed radiance, modeled emission, solar irradiance and emissivity. If no emissivity is supplied, assume Kirchoff's Law (eq. 6, Bandfield et al., 2018).

Parameters:

Name Type Description Default
rad num | arr

Observed radiance [W m^-2 μm^-1]

required
emission num | arr

Emission to remove from rad [W m^-2 μm^-1]

None
solar_irr num | arr

Solar irradiance [W m^-2 μm^-1]

required
emissivity num | arr

Emissivity (if None, assume Kirchoff)

None
Source code in roughness/emission.py
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
def get_rad_factor(rad, solar_irr, emission=None, emissivity=None):
    """
    Return radiance factor (I/F) given observed radiance, modeled emission,
    solar irradiance and emissivity. If no emissivity is supplied,
    assume Kirchoff's Law (eq. 6, Bandfield et al., 2018).

    Parameters:
        rad (num | arr): Observed radiance [W m^-2 μm^-1]
        emission (num | arr): Emission to remove from rad [W m^-2 μm^-1]
        solar_irr (num | arr): Solar irradiance [W m^-2 μm^-1]
        emissivity (num | arr): Emissivity (if None, assume Kirchoff)
    """
    emissivity = 1 if emissivity is None else emissivity
    if emission is not None and emissivity is None:
        # Assume Kirchoff's Law to compute emissivity
        emissivity = (rad - solar_irr) / (emission - solar_irr)
    return (rad - emissivity * emission) / solar_irr

get_rad_sb(temp)

Return radiance assuming the Stefan-Boltzmann Law given temperature.

Parameters:

Name Type Description Default
temp num | arr

Temperature [K]

required
Source code in roughness/emission.py
710
711
712
713
714
715
716
717
718
719
720
def get_rad_sb(temp):
    """
    Return radiance assuming the Stefan-Boltzmann Law given temperature.

    Parameters:
        temp (num | arr): Temperature [K]
    """
    rad = SB * temp**4
    if isinstance(temp, xr.DataArray):
        rad.name = "Radiance [W m^-2]"
    return rad

get_reradiation(cinc, surf_theta, albedo, emissivity, solar_dist, rad0, alb0=0.12)

Return emission incident on surf_theta from re-radiating adjacent level surfaces. Approximation from JB modified from downwelling radiance.

Parameters:

Name Type Description Default
cinc num | arr

Cosine of solar incidence.

required
surf_theta num | arr

Surface slope(s) in degrees.

required
albedo num | arr

Albedo (hemispherical if num, scaled by cinc if array).

required
emissivity num

Hemispherical broadband emissivity.

required
solar_dist num

Solar distance (au).

required
rad0 num

Radiance of level surface facet.

required
alb0 num

Albedo of level surface facet.

0.12
Source code in roughness/emission.py
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
def get_reradiation(
    cinc, surf_theta, albedo, emissivity, solar_dist, rad0, alb0=0.12
):
    """
    Return emission incident on surf_theta from re-radiating adjacent level
    surfaces. Approximation from JB modified from downwelling radiance.

    Parameters:
        cinc (num | arr): Cosine of solar incidence.
        surf_theta (num | arr): Surface slope(s) in degrees.
        albedo (num | arr): Albedo (hemispherical if num, scaled by cinc if array).
        emissivity (num): Hemispherical broadband emissivity.
        solar_dist (num): Solar distance (au).
        rad0 (num): Radiance of level surface facet.
        alb0 (num): Albedo of level surface facet.
    """
    # Albedo scaled by angle b/t facet and level surface (e.g. surf_theta)
    # TODO: Why scale by alb_level / 0.12?
    level_albedo = get_albedo_scaling(surf_theta, albedo) * alb0 / 0.12
    level_rad = get_emission_eq(cinc, level_albedo, emissivity, solar_dist)

    # Assuming infinite level plane visible from facet slope, the factor
    #  surf_theta / 180 gives incident reradiation
    rerad = (surf_theta / 180) * emissivity * (rad0 + alb0 * level_rad)

    if isinstance(rerad, xr.DataArray):
        rerad = rerad.clip(min=0)
    else:
        rerad[rerad < 0] = 0
    return rerad

get_reradiation_alt(rad_table, sun_theta, sun_az, tflat, tvert, albedo=0.12, emiss=0.95)

Return emission incident on surf_theta from re-radiating adjacent level surfaces using lookup table. Modified from JB downwelling approximation.

Source code in roughness/emission.py
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
def get_reradiation_alt(
    rad_table, sun_theta, sun_az, tflat, tvert, albedo=0.12, emiss=0.95
):
    """
    Return emission incident on surf_theta from re-radiating adjacent level
    surfaces using lookup table. Modified from JB downwelling approximation.

    """
    # cinc = rn.get_facet_cos_theta(facet_theta, facet_az, sun_theta, sun_az)

    # Reflected radiance from sun to facet to each other facet?
    # (solar rad * facet albedo)/pi * cos(inc to each facet)

    # Emission from each facet to each other facet assuming lambertian
    # (sigT^4 * facet emissivity)/pi * cos(inc to each facet) * (1-albedo)
    # should be weighted by P(facet) - what is it scaled to? if we weight,
    #  we're presuming a total amount of rerad right?
    # rerad_table = get_rad_sb(temp_table) * emissivity / np.pi
    # rerad_table = rn.rotate_az_lookup(rerad_table, target_az=180, az0=0)

    # rerad_table *= (rerad_table.theta / 180) * albedo

    # # Get scattered solar rad from level surface to each facet
    # albedo_eff = get_albedo_scaling(sun_theta, albedo)
    # rad_eq_sun = get_rad_sb(t_flat) * emiss / (1 - albedo_eff)
    # rerad_scattered = rad_eq_sun * albedo_eff

    # # Get reradiated emission from level suface to each facet
    # # frac is integrated solid angle in each theta, az bin
    # naz = len(rad_table.az)  # Assume az symmetric
    # facet_theta = rad_table.theta
    # dtheta = (facet_theta[1] - facet_theta[0]) / 2
    # theta_bins = np.append(facet_theta - dtheta, facet_theta[-1] + dtheta)
    # thetar = np.deg2rad(theta_bins)
    # theta_frac = np.cos(thetar[:-1]) - np.cos(thetar[1:])
    # frac = theta_frac / naz

    # # emiss = directional_emiss(90 - facet_theta)
    # rerad_emitted = get_rad_sb(t_flat) * emiss

    # # Final radiance scattered and emitted from flat surface
    # # rerad = frac * rerad_emitted + frac * rerad_scattered
    # rerad = (rerad_emitted + rerad_scattered)

    albedo_eff = get_albedo_scaling(sun_theta, albedo)
    rad_eq_sun = get_rad_sb(tflat) * emiss / (1 - albedo_eff)
    rerad_scattered = rad_eq_sun * albedo_eff
    rerad_emitted = get_rad_sb(tflat) * emiss
    # print(rerad_emitted, rerad_scattered)
    # Adjacent facet approx 1 - cos(facet_theta)
    naz = len(rad_table.az)  # Assume az symmetric
    facet_theta = rad_table.theta
    dtheta = (facet_theta[1] - facet_theta[0]) / 2
    theta_bins = np.append(facet_theta - dtheta, facet_theta[-1] + dtheta)
    thetar = np.deg2rad(theta_bins)
    theta_frac = np.cos(thetar[:-1]) - np.cos(thetar[1:])
    fflat = theta_frac / naz

    # Final radiance scattered and emitted from flat surface
    albflat = get_albedo_scaling(facet_theta, albedo=albedo)
    rerad_flat = fflat * (rerad_emitted + rerad_scattered * (1 - albflat))

    # Rerad vert
    albedo_eff = get_albedo_scaling(90 - sun_theta, albedo)
    rad_eq_sun = get_rad_sb(tvert) * emiss / (1 - albedo_eff)
    rerad_scattered = rad_eq_sun * albedo_eff
    rerad_emitted = get_rad_sb(tvert) * emiss
    # print(rerad_emitted, rerad_scattered)
    # # Adjacent facet approx 1 - sin(facet_theta)
    # naz = len(table.az)  # Assume az symmetric
    # facet_theta = table.theta
    # theta_frac = 1 - np.sin(np.deg2rad(facet_theta))
    # frac = theta_frac / naz

    dtheta = (rad_table.theta[1] - rad_table.theta[0]) / 2
    theta_bins = np.append(
        rad_table.theta - dtheta, rad_table.theta[-1] + dtheta
    )
    daz = (rad_table.az[1] - rad_table.az[0]) / 2
    az_bins = np.append(rad_table.az - daz, rad_table.az[-1] + daz)
    # theta, az = np.meshgrid(theta_bins, az_bins)
    thetar = np.deg2rad(theta_bins)
    azr = np.deg2rad(az_bins - sun_az)
    ftheta = np.sin(thetar[1:]) - np.sin(thetar[:-1])
    faz = np.abs(np.cos(azr[1:] / 2) - np.cos(azr[:-1] / 2))
    faz = faz / faz.sum()
    fvert = faz[:, np.newaxis] * ftheta

    # Final radiance scattered and emitted from vert surface
    #  todo: 1/2 vert because half is scattered to space
    albvert = get_albedo_scaling(90 - facet_theta, albedo=albedo)
    rerad_vert = fvert * (
        rerad_emitted + rerad_scattered * (1 - albvert.values)
    )
    rerad_tot = 4 * np.pi * (rerad_flat.values + rerad_vert)

    # rerad is too low when we integrate over facets. probably because we're
    #  only accounting for one flat and one vert surface. seemed to be a bit
    #  better when we didn't integrate fflat and fvert = 1, but instead used
    #  something like 1 - cos(facet_theta) and 1 - sin(facet_theta)
    return rerad_tot

get_reradiation_jb(sun_theta, facet_theta, t_flat, albedo=0.12, emiss=0.95, alb_scaling='vasavada', solar_dist=1)

Return emission incident on surf_theta from re-radiating adjacent level surfaces using JB downwelling approximation.

Source code in roughness/emission.py
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
def get_reradiation_jb(
    sun_theta,
    facet_theta,
    t_flat,
    albedo=0.12,
    emiss=0.95,
    alb_scaling="vasavada",
    solar_dist=1,
):
    """
    Return emission incident on surf_theta from re-radiating adjacent level
    surfaces using JB downwelling approximation.
    """
    # emiss = 0.77
    facet_alb = get_albedo_scaling(
        facet_theta, albedo=albedo, mode=alb_scaling
    )
    rerad_emitted = get_rad_sb(t_flat) * emiss
    rerad_reflected = (
        SC
        / solar_dist**2
        * np.cos(np.deg2rad(sun_theta))
        * get_albedo_scaling(sun_theta, albedo, mode=alb_scaling)
    )
    rerad_absorbed = (facet_theta / 180) * (
        rerad_emitted + (1 - facet_alb) * rerad_reflected
    )
    return rerad_absorbed

get_reradiation_new(tflat, albedo=0.12, emiss=0.95)

Compute upwelling reradiation from flat surface.

Source code in roughness/emission.py
626
627
628
629
630
631
632
def get_reradiation_new(tflat, albedo=0.12, emiss=0.95):
    """Compute upwelling reradiation from flat surface."""
    q_emis = get_rad_sb(tflat) * emiss
    q_sun = q_emis / (1 - albedo)
    q_scattered = q_sun * albedo
    q_rerad = (1 - albedo) * (q_emis + q_scattered)
    return q_rerad

get_shadow_temp(sun_theta, sun_az, temp0)

Return temperature of shadows. Nominally 100 K less than the rad_eq surf_temp. At high solar incidence, scale shadow temperature by inc angle. Evening shadows are warmer than morning shadows. Tuned for lunar eqautor by Bandfield et al. (2015, 2018).

Parameters:

Name Type Description Default
sun_theta num

Solar incidence angle [deg]

required
sun_az num

Solar azimuth from N [deg]

required
temp0 num

Temperature of illuminated level surface facet [K]

required
Source code in roughness/emission.py
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
def get_shadow_temp(sun_theta, sun_az, temp0):
    """
    Return temperature of shadows. Nominally 100 K less than the rad_eq
    surf_temp. At high solar incidence, scale shadow temperature by inc angle.
    Evening shadows are warmer than morning shadows. Tuned for lunar eqautor
    by Bandfield et al. (2015, 2018).

    Parameters:
        sun_theta (num): Solar incidence angle [deg]
        sun_az (num): Solar azimuth from N [deg]
        temp0 (num): Temperature of illuminated level surface facet [K]
    """
    shadow_temp_factor = 1
    if sun_theta >= 60 and 0 <= sun_az < 180:
        shadow_temp_factor = 1 - 0.6 * (sun_theta % 60) / 30
    elif sun_theta >= 60 and 180 <= sun_az < 360:
        shadow_temp_factor = 1 - 0.75 * (sun_theta % 60) / 30
    shadow_temp = temp0 - 100 * shadow_temp_factor
    return shadow_temp

get_shadow_temp_new(temp_table, tparams, cst=0.5, tlookup=TLOOKUP)

Return cast shadow temperature using current and dawn (coldest) temperatures and the cast shadow time, cst.

Parameters:

Name Type Description Default
temp_table ndarray

Temperature table.

required
tparams dict

Dictionary of temperature lookup parameters.

required
cst float

Fraction of time since dawn. Default is 0.5.

0.5
tlookup path

Temperature lookup xarray or path to file. Default is TLOOKUP.

TLOOKUP

Returns:

Type Description
ndarray

Cast shadow temperature table.

Source code in roughness/emission.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def get_shadow_temp_new(temp_table, tparams, cst=0.5, tlookup=TLOOKUP):
    """
    Return cast shadow temperature using current and dawn (coldest)
    temperatures and the cast shadow time, cst.

    Parameters:
        temp_table (ndarray): Temperature table.
        tparams (dict): Dictionary of temperature lookup parameters.
        cst (float, optional): Fraction of time since dawn. Default is 0.5.
        tlookup (path, optional): Temperature lookup xarray or path
            to file. Default is TLOOKUP.

    Returns:
        (np.ndarray): Cast shadow temperature table.
    """
    # Shadow age is cst fraction * time since dawn
    shadow_age = (tparams["tloc"] - 6) * cst

    # Tloc of last illumination is tloc - shadow_age
    last_illum = tparams["tloc"] - shadow_age

    # Twarm is when surfaces were last illuminated
    twparams = tparams.copy()
    twparams["tloc"] = last_illum
    twarm = get_temp_table(twparams, tlookup)

    # Tcold is dawn temp
    tcparams = tparams.copy()
    tcparams["tloc"] = 6
    tcold = get_temp_table(tcparams, tlookup)

    # Linearly scale shadow temp between last illuminated temp and dawn temp
    tshadow = cst * tcold + (1 - cst) * twarm
    return tshadow.interp_like(temp_table)

get_shadow_temp_table(temp_table, sun_theta, sun_az, tparams, cst, tlookup=TLOOKUP)

Return shadow temperature for each facet in temp_illum.

Facets where solar incidence angle > 90 degrees (shaded relief) retain the temp_table value since the sun has simply set in thermal model.

Facets where solar incidence angle < 90 degrees (cast shadow) are set to a shadow temperature following Bandfield et al. (2018).

Parameters:

Name Type Description Default
temp_table DataArray

Table of facet temperatures.

required
sun_theta float

Solar incidence angle.

required
sun_az float

Solar azimuth.

required
tparams dict

Dictionary of temperature lookup parameters.

required
cst float

Cast shadow time.

required

Returns:

Type Description
ndarray

Shadow temperature table.

Source code in roughness/emission.py
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
def get_shadow_temp_table(
    temp_table, sun_theta, sun_az, tparams, cst, tlookup=TLOOKUP
):
    """
    Return shadow temperature for each facet in temp_illum.

    Facets where solar incidence angle > 90 degrees (shaded relief) retain the
    temp_table value since the sun has simply set in thermal model.

    Facets where solar incidence angle < 90 degrees (cast shadow) are set to a
    shadow temperature following Bandfield et al. (2018).

    Parameters:
        temp_table (xr.DataArray): Table of facet temperatures.
        sun_theta (float): Solar incidence angle.
        sun_az (float): Solar azimuth.
        tparams (dict): Dictionary of temperature lookup parameters.
        cst (float): Cast shadow time.

    Returns:
        (np.ndarray): Shadow temperature table.
    """
    tshadow = get_shadow_temp_new(temp_table, tparams, cst, tlookup)
    # tshadow = get_shadow_temp(sun_theta, sun_az, tflat)

    # Determine where facets are past local sunset (cinc > 0)
    facet_theta, facet_az = rh.facet_grids(temp_table)
    cinc = rn.get_facet_cos_theta(facet_theta, facet_az, sun_theta, sun_az)
    temp_shadow = temp_table.copy()

    # Take normal nighttime temp for facets past sunset
    # Take tshadow for facets in cast shadow unless normal temp is colder
    temp_shadow = xr.where(
        (cinc < 0) | (temp_shadow < tshadow), temp_shadow, tshadow
    )
    return temp_shadow

get_solar_irradiance(solar_spec, solar_dist)

Return solar irradiance from solar spectrum and solar dist assuming lambertian surface.

Parameters:

Name Type Description Default
solar_spec arr

Solar spectrum [W m^-2 μm^-1] at 1 au

required
solar_dist num

Solar distance (au)

required
Source code in roughness/emission.py
788
789
790
791
792
793
794
795
796
797
def get_solar_irradiance(solar_spec, solar_dist):
    """
    Return solar irradiance from solar spectrum and solar dist
    assuming lambertian surface.

    Parameters:
        solar_spec (arr): Solar spectrum [W m^-2 μm^-1] at 1 au
        solar_dist (num): Solar distance (au)
    """
    return solar_spec / (solar_dist**2 * np.pi)

get_temp_sb(rad)

Return temperature assuming the Stefan-Boltzmann Law given radiance.

Parameters:

Name Type Description Default
rad num | arr

Radiance [W m^-2]

required
Source code in roughness/emission.py
697
698
699
700
701
702
703
704
705
706
707
def get_temp_sb(rad):
    """
    Return temperature assuming the Stefan-Boltzmann Law given radiance.

    Parameters:
        rad (num | arr): Radiance [W m^-2]
    """
    temp = (rad / SB) ** 0.25
    if isinstance(rad, xr.DataArray):
        temp.name = "Temperature [K]"
    return temp

get_temp_table(params, tlookup=None)

Return 2D temperature table of surface facets (inc, az).

Parameters:

Name Type Description Default
params dict

Dictionary of temperature lookup parameters.

required
tlookup path

Temperature lookup xarray or path to file.

None

Returns:

Type Description
ndarray

Facet temperatures (dims: az, theta)

Source code in roughness/emission.py
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
def get_temp_table(params, tlookup=None):
    """
    Return 2D temperature table of surface facets (inc, az).

    Parameters:
        params (dict): Dictionary of temperature lookup parameters.
        tlookup (path): Temperature lookup xarray or path to file.

    Returns:
        (np.ndarray): Facet temperatures (dims: az, theta)
    """
    if tlookup is None:
        tlookup = TLOOKUP
    temp_table = rn.get_table_xarr(params, da="tsurf", los_lookup=tlookup)
    return temp_table

mrad2cmrad(mrad)

Convert radiance from units W/(m^2 sr μm) to W/(cm^2 sr μm).

Source code in roughness/emission.py
924
925
926
def mrad2cmrad(mrad):
    """Convert radiance from units W/(m^2 sr μm) to W/(cm^2 sr μm)."""
    return mrad * 1e-4  # [W/(m^2 sr μm)] -> [W/(cm^2 sr μm)]

rough_emission_eq(geom, wls, rms, albedo, emissivity, solar_dist, rerad=False, flookup=None)

Return emission spectrum corrected for subpixel roughness assuming radiative equillibrium. Uses ray-casting generated shadow_lookup table at sl_path.

Parameters:

Name Type Description Default
geom list of float

View geometry (sun_theta, sun_az, sc_theta, sc_az)

required
wls arr

Wavelengths [microns]

required
rms arr

RMS roughness [degrees]

required
albedo num

Hemispherical broadband albedo

required
emissivity num

Emissivity

required
solar_dist num

Solar distance (au)

required
rerad bool

If True, include re-radiation from adjacent facets

False
flookup str

Los lookup path

None

Returns:

Name Type Description
rough_emission arr

Emission spectrum vs. wls [W m^-2]

Source code in roughness/emission.py
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
def rough_emission_eq(
    geom, wls, rms, albedo, emissivity, solar_dist, rerad=False, flookup=None
):
    """
    Return emission spectrum corrected for subpixel roughness assuming
    radiative equillibrium. Uses ray-casting generated shadow_lookup table at
    sl_path.

    Parameters:
        geom (list of float): View geometry (sun_theta, sun_az, sc_theta, sc_az)
        wls (arr): Wavelengths [microns]
        rms (arr): RMS roughness [degrees]
        albedo (num): Hemispherical broadband albedo
        emissivity (num): Emissivity
        solar_dist (num): Solar distance (au)
        rerad (bool): If True, include re-radiation from adjacent facets
        flookup (str): Los lookup path

    Returns:
        rough_emission (arr): Emission spectrum vs. wls [W m^-2]
    """
    sun_theta, sun_az, sc_theta, sc_az = geom
    if isinstance(wls, np.ndarray):
        wls = rh.wl2xr(wls)
    # If no roughness, return isothermal emission
    if rms == 0:
        return bb_emission_eq(sun_theta, wls, albedo, emissivity, solar_dist)

    # Select shadow table based on rms, solar incidence and az
    shadow_table = rn.get_shadow_table(rms, sun_theta, sun_az, flookup)
    temp_illum = get_facet_temp_illum_eq(
        shadow_table,
        sun_theta,
        sun_az,
        albedo,
        emissivity,
        solar_dist,
        rerad=rerad,
    )

    # Compute temperatures for shadowed facets (JB approximation)
    temp0 = temp_illum[0, 0]  # Temperature of level surface facet
    temp_shade = get_shadow_temp(sun_theta, sun_az, temp0)

    # Compute 2 component emission of illuminated and shadowed facets
    emission_table = emission_2component(
        wls, temp_illum, temp_shade, shadow_table, emissivity
    )

    # Weight each facet by its probability of occurrance
    weights = rn.get_weight_table(rms, sun_theta, sc_theta, sc_az, flookup)
    weights = weights.interp_like(emission_table)
    weights = weights / weights.sum()
    rough_emission = emission_spectrum(emission_table, weights)
    return rough_emission

rough_emission_lookup(geom, wls, rms=15, tparams=None, rerad=True, flookup=None, tlookup=None, emissivity=1, cast_shadow_time=0.05)

Return rough emission spectrum given geom and params to tlookup.

Parameters:

Name Type Description Default
geom list of float

Viewing geometry specified as four angles (solar_incidence, solar_azimuth, view_emission, view_azimuth)

required
wls arr

Wavelengths [microns]

required
rms arr

RMS roughness [degrees]. Default is 15.

15
rerad bool

If True, include re-radiation from adjacent facets. Default is True.

True
tparams dict

Dictionary of temperature lookup parameters. Default is None.

None
flookup path

Facet lookup xarray or path to file. Default is None.

None
tlookup path

Temperature lookup xarray or path to file. Default is None.

None
emissivity float

Emissivity. Default is 1.

1
cast_shadow_time float

Time spent in cast shadow as fraction of one day. Default is 0.05.

0.05

Returns:

Type Description
DataArray

Rough emission spectrum.

Source code in roughness/emission.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def rough_emission_lookup(
    geom,
    wls,
    rms=15,
    tparams=None,
    rerad=True,
    flookup=None,
    tlookup=None,
    emissivity=1,
    cast_shadow_time=0.05,
):
    """
    Return rough emission spectrum given geom and params to tlookup.

    Parameters:
        geom (list of float): Viewing geometry specified as four angles
            (solar_incidence, solar_azimuth, view_emission, view_azimuth)
        wls (arr): Wavelengths [microns]
        rms (arr, optional): RMS roughness [degrees]. Default is 15.
        rerad (bool, optional): If True, include re-radiation from adjacent
            facets. Default is True.
        tparams (dict, optional): Dictionary of temperature lookup parameters.
            Default is None.
        flookup (path, optional): Facet lookup xarray or path to
            file. Default is None.
        tlookup (path, optional): Temperature lookup xarray or path
            to file. Default is None.
        emissivity (float, optional): Emissivity. Default is 1.
        cast_shadow_time (float, optional): Time spent in cast shadow as
            fraction of one day. Default is 0.05.

    Returns:
        (xr.DataArray): Rough emission spectrum.
    """
    if tparams is None:
        tparams = {}  # TODO: raise more useful error here
    sun_theta, sun_az, sc_theta, sc_az = geom
    if isinstance(wls, np.ndarray):
        wls = rh.wl2xr(wls)

    # Query los and temperature lookup tables
    shadow_table = rn.get_shadow_table(rms, sun_theta, sun_az, flookup)
    # if not shadow_table.isnull().all():
    # shadow_table = shadow_table.dropna("theta")

    # Get illum and shadowed facet temperatures
    temp_table = get_temp_table(tparams, tlookup)
    tflat = temp_table.sel(theta=0, az=0).values
    # tvert = temp_table.sel(theta=90).interp(az=sun_az).values
    temp_table = temp_table.interp_like(shadow_table)
    temp_shade = get_shadow_temp_table(
        temp_table, sun_theta, sun_az, tparams, cast_shadow_time, tlookup
    )
    if rerad:
        rad_table = get_rad_sb(temp_table)
        rad_shade = get_rad_sb(temp_shade)
        albedo = 0.12
        if "albedo" in tparams:
            albedo = tparams["albedo"]
        rerad = get_reradiation_jb(sun_theta, rad_table.theta, tflat, albedo)
        # rerad = get_reradiation_alt(rad_table, sun_theta, sun_az, tflat,
        #                             tvert, albedo, emissivity)
        # rerad_new = get_reradiation_new(tflat, albedo, emissivity)
        temp_table = get_temp_sb(rad_table + rerad)
        temp_shade = get_temp_sb(rad_shade + rerad)

    # Directional emissivity table
    # facet_theta, facet_az = rh.facet_grids(temp_table)
    # emissivity = get_emissivity_table(facet_theta, facet_az, sc_theta, sc_az)

    # Compute 2 component emission of illuminated and shadowed facets
    emission_table = emission_2component(
        wls, temp_table, temp_shade, shadow_table, emissivity
    )

    # Weight each facet by its prob of occurring and being visible from sc
    # weights = rn.get_weight_table(rms, sun_theta, sc_theta, sc_az, flookup)

    # weights = weights.interp_like(emission_table)
    # weights = weights / weights.sum()

    view_table = rn.get_view_table(rms, sc_theta, sc_az, flookup)
    # Get probability each facet exists at given rms
    facet_weight = rn.get_facet_table(rms, sun_theta, flookup)
    rough_emission = emission_spectrum(
        emission_table, facet_weight * view_table
    )
    return rough_emission

rough_emission_lookup_new(geom, wls, rms=15, tparams=None, rerad=True, flookup=None, tlookup=None, emissivity=1, cast_shadow_time=0.05)

Return rough emission spectrum given geom and params to tlookup.

Parameters:

Name Type Description Default
geom list of float

Viewing geometry specified as four angles (solar_incidence, solar_azimuth, view_emission, view_azimuth)

required
wls arr

Wavelengths [microns]

required
rms arr

RMS roughness [degrees]. Default is 15.

15
rerad bool

If True, include re-radiation from adjacent facets. Default is True.

True
tparams dict

Dictionary of temperature lookup parameters. Default is None.

None
flookup path

Facet lookup xarray or path to file. Default is None.

None
tlookup path

Temperature lookup xarray or path to file. Default is None.

None
emissivity float

Emissivity. Default is 1.

1
cast_shadow_time float

Time spent in cast shadow as fraction of one day. Default is 0.05.

0.05

Returns:

Type Description
DataArray

Rough emission spectrum.

Source code in roughness/emission.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def rough_emission_lookup_new(
    geom,
    wls,
    rms=15,
    tparams=None,
    rerad=True,
    flookup=None,
    tlookup=None,
    emissivity=1,
    cast_shadow_time=0.05,
):
    """
    Return rough emission spectrum given geom and params to tlookup.

    Parameters:
        geom (list of float): Viewing geometry specified as four angles
            (solar_incidence, solar_azimuth, view_emission, view_azimuth)
        wls (arr): Wavelengths [microns]
        rms (arr, optional): RMS roughness [degrees]. Default is 15.
        rerad (bool, optional): If True, include re-radiation from adjacent
            facets. Default is True.
        tparams (dict, optional): Dictionary of temperature lookup parameters.
            Default is None.
        flookup (path, optional): Facet lookup xarray or path to
            file. Default is None.
        tlookup (path, optional): Temperature lookup xarray or path
            to file. Default is None.
        emissivity (float, optional): Emissivity. Default is 1.
        cast_shadow_time (float, optional): Time spent in cast shadow as
            fraction of one day. Default is 0.05.

    Returns:
        (xr.DataArray): Rough emission spectrum.
    """
    if tparams is None:
        tparams = {}  # TODO: raise more useful error here
    sun_theta, sun_az, sc_theta, sc_az = geom
    if isinstance(wls, np.ndarray):
        wls = rh.wl2xr(wls)

    # Query los and temperature lookup tables
    shadow_table = rn.get_shadow_table(rms, sun_theta, sun_az, flookup)

    # Get illum and shadowed facet temperatures
    temp_table = get_temp_table(tparams, tlookup)
    tflat = temp_table.sel(theta=0, az=0).values
    tvert = temp_table.sel(theta=90).interp(az=sun_az).values
    temp_table = temp_table.interp_like(shadow_table)
    temp_shade = get_shadow_temp_table(
        temp_table, sun_theta, sun_az, tparams, cast_shadow_time, tlookup
    )
    rad_table = get_rad_sb(temp_table)
    rad_shade = get_rad_sb(temp_shade)

    if rerad:
        albedo = tparams.get("albedo", 0.12)
        rerad = get_reradiation_alt(
            rad_table, sun_theta, sun_az, tflat, tvert, albedo, emissivity
        )
        print(
            [
                f"{float(v):.0f}"
                for v in [rerad.sum(), rad_table.sum(), rad_shade.sum()]
            ]
        )
        rad_table += rerad
        rad_shade += rerad

    # Get norm factor from weighted hemispherical radiance in view direction
    rad_tot = rad_table * (1 - shadow_table) + rad_table * shadow_table
    view_table = rn.get_view_table(rms, sc_theta, sc_az, flookup)
    # view_table = rn.get_los_table(rms, sc_theta, sc_az, flookup, "prob")
    # view_table = view_table.where(view_table > 0)
    facet_weight = rn.get_facet_table(rms, sun_theta, flookup)
    rweighted = rad_tot * facet_weight * view_table

    norm = get_rad_sb(tflat) / float(rweighted.sum(dim=["theta", "az"]))
    # print(f'{tflat:.0f}', norm)
    rad_table *= norm
    rad_shade *= norm

    # Convert each facet to effective temperature and compute rad spectrum
    temp_table = get_temp_sb(rad_table)
    temp_shade = get_temp_sb(rad_shade)
    emission_table = emission_2component(
        wls, temp_table, temp_shade, shadow_table, emissivity
    )
    rough_emission = emission_spectrum(
        emission_table, facet_weight * view_table
    )
    return rough_emission

temp2rad(wavelength, temp, emissivity=1)

Return "graybody" radiance at temperature and emissivity.

Source code in roughness/emission.py
780
781
782
783
784
785
def temp2rad(wavelength, temp, emissivity=1):
    """
    Return "graybody" radiance at temperature and emissivity.
    """
    rad = bbrw(wavelength, temp) * emissivity
    return cmrad2mrad(rad)

wl2wn(wavelength)

Convert wavelength (μm) to wavenumber (cm-1).

Source code in roughness/emission.py
914
915
916
def wl2wn(wavelength):
    """Convert wavelength (μm) to wavenumber (cm-1)."""
    return 10000 / wavelength

wlrad2wnrad(wl, wlrad)

Convert radiance from units W/(cm2 sr cm-1) to W/(cm2 sr μm).

Source code in roughness/emission.py
900
901
902
903
904
905
906
def wlrad2wnrad(wl, wlrad):
    """
    Convert radiance from units W/(cm2 sr cm-1) to W/(cm2 sr μm).
    """
    wn = 1 / wl  # [μm] -> [μm-1]
    wnrad = wlrad / wn**2  # [1/μm] -> [1/μm-1]
    return wnrad * 1e-4  # [1/μm-1] -> [1/cm-1]

wn2wl(wavenumber)

Convert wavenumber (cm-1) to wavelength (μm).

Source code in roughness/emission.py
909
910
911
def wn2wl(wavenumber):
    """Convert wavenumber (cm-1) to wavelength (μm)."""
    return 10000 / wavenumber

wnrad2wlrad(wavenumber, rad)

Convert radiance from units W/(cm2 sr cm-1) to W/(cm2 sr μm).

Parameters:

Name Type Description Default
wavenumber num | arr

Wavenumber(s) [cm^-1]

required
rad num | arr

Radiance in terms of wn units [W/(cm^2 sr cm^-1)]

required
Source code in roughness/emission.py
887
888
889
890
891
892
893
894
895
896
897
def wnrad2wlrad(wavenumber, rad):
    """
    Convert radiance from units W/(cm2 sr cm-1) to W/(cm2 sr μm).

    Parameters:
        wavenumber (num | arr): Wavenumber(s) [cm^-1]
        rad (num | arr): Radiance in terms of wn units [W/(cm^2 sr cm^-1)]
    """
    wavenumber_microns = wavenumber * 1e-4  # [cm-1] -> [μm-1]
    rad_microns = rad * 1e4  # [1/cm-1] -> [1/μm-1]
    return rad_microns * wavenumber_microns**2  # [1/μm-1] -> [1/μm]