GithubHelp home page GithubHelp logo

usnistgov / scatmech Goto Github PK

View Code? Open in Web Editor NEW
30.0 7.0 13.0 2.13 MB

SCATMECH: Polarized light scattering C++ class library

Home Page: https://pages.nist.gov/SCATMECH/index.htm

License: Other

C++ 99.78% Makefile 0.20% HTML 0.02%
brdf scatter optics bsdf mueller-matrix rigorous-coupled-wave electromagnetics polarization optical-properties diffuse

scatmech's Introduction

SCATMECH: Polarized Light Scattering C++ Class Library

SCATMECH is an object-oriented C++ class library developed to distribute models for light scattering applications. Included in the library are models for diffuse surface scattering that predict the bidirectional reflectance distribution function (BRDF), codes for calculating scattering by isolated particles, and codes for reflection, transmission, and diffraction from gratings. Emphasis has been given to those diffuse scatter models that are physics-based and which predict the polarization properties of the scattered light. The library also includes a number of classes that are useful for working with polarized light or the optics of thin films. The library is designed to enable expansion of new models.

See https://pages.nist.gov/SCATMECH/index.htm for full documentation.

This software was developed by employees of the National Institute of Standards and Technology (NIST), an agency of the Federal Government. Pursuant to title 17 United States Code Section 105, works of NIST employees are not subject to copyright protection in the United States and are considered to be in the public domain. Permission to freely use, copy, modify, and distribute this software and its documentation without fee is hereby granted, provided that this notice and disclaimer of warranty appears in all copies.

THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.

scatmech's People

Contributors

thomas-germer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

scatmech's Issues

scattering model for nanotextured interfaces

Hi Sir,

At the moment, I am engaged in development of full scattering model for nanotextured interfaces. I am wondering whether your model is compatible for my application. If so, I am requesting you to please suggest on how to achieve that?

Thank you.

-lm value required for Makefile on ubuntu

Sweet library, thanks very much. In order to compile your example programs, I need to add -lm to the $(LIBRARIES) variable in the makefile

This is on Ubuntu 19.04, using gcc version 8.3.0

erf and tgamma function

erf and tgamma function is not found
sizedistribution.cpp, line63, no tgamma
line 86,88 no erf.
my compiler is vs2010, maybe these are contained in newer compilers.

and in torrspar.cpp, line 143
this erf conditional function is not right while xx ==1

and why we need unit_sdf in facet.cpp, line 22, this line will cause init problem.

some kind of problem in call to int eigen(CFARRAY...

After looking more closely at some Mueller Matrices and whether or not they are valid, have noticed that there is an inconsistency in the way eigen values / eigen vectors are calculated and then used in the validity test. This came up because the code has been saying that some matrices are valid, but when we look at them manually with the criteria from the Givens Kostinski paper, they are showing as invalid, namely in the part where the eigen vector associated with the largest eigen vector is not a valid stokes vector even though SCATMECH says that it is valid.

Take for example the following invalid Mueller Matrix:

[[10, 1, 2, 3],
 [ 1, 2, 3, 4],
 [ 2, 3, 4, 5],
 [ 3, 4, 5, 6],

If I analyze in python, I get the following:

import numpy as np
arr = np.array(
    [[10, 1, 2, 3],
     [ 1, 2, 3, 4],
     [ 2, 3, 4, 5],
     [ 3, 4, 5, 6]])
G = np.eye(4) * -1
G[0, 0] = 1
M = G @ arr.T @ G @ arr
values, vectors = np.linalg.eig(M)
for i in range(4):
    print(values[i])
    print(vectors[:, i])  # eigen vectors are the columns of this array

the output is:

140.78750550020018
[-0.16528353  0.43218914  0.55979591  0.68740268]
86.91830502018094
[ 0.98294741 -0.00674521 -0.08486839 -0.16299156]
0.2941894796189907
[-0.08522041 -0.79155813 -0.09714189  0.59727436]
-7.044956869562579e-16
[ 1.87078240e-15  4.08248290e-01 -8.16496581e-01  4.08248290e-01]

now if I add the following code to the SCATMECH::MuellerMatrix::valid function, enclosed as diagnostic code in comments:

    bool
    MuellerMatrix::valid() const
    {
        // Expanding the Mueller matrix slightly helps to ensure that numerical errors don't cause
        // legitimate Mueller matrices to appear invalid.
        MuellerMatrix expanded = *this + sqrt(numeric_limits<double>::epsilon()) * MuellerDepolarizer(m[0][0], 1);
        CFARRAY Q(4,1);
        CFARRAY W(4,4);
        CFARRAY M(4,4);
        for (int i=1; i<=4; ++i) {
            double Gi = i==1 ? 1 : -1;
            for (int j=1; j<=4; ++j) {
                M(i,j) = 0;
                for (int k=1; k<=4; ++k) {
                    double Gk = k==1 ? 1 : -1;
                    M(i,j) += Gi*expanded.m[k-1][i-1]*Gk*expanded.m[k-1][j-1];
                }
            }
        }
        eigen(M,Q,W,4);

        // added diagnostic code
        for (int i = 0; i < 4; ++i) {
          std::cout << Q(i + 1) <<  std::endl;
          for (int j = 0; j < 4; ++j) {
            std::cout << W(i+1, j+1) << ", ";  // eigen vectors are the rows of W,
                                               // as implied by the valid Stokes vector check below
          }
          std::cout << endl;
        }
        // end diagnostic code

        int imax=0;
        double max = -numeric_limits<double>::max();
		double small = (norm(Q(1)) + norm(Q(2)) + norm(Q(3)) + norm(Q(4)))*numeric_limits<double>::epsilon()*10.;
        for (int i=1; i<=4; ++i) {
            if (fabs(imag(Q(i)))>small) return false;
            if (abs(Q(i))>max) {
                max = abs(Q(i));
                imax = i;
            }
        }
        if (sqr(real(W(imax,1)))<sqr(real(W(imax,2)))+sqr(real(W(imax,3)))+sqr(real(W(imax,4)))) return false;
        return true;
    }

and then I create the following program:

#include <iostream>
#include <iomanip>
#include "mueller.h"

int main() {
  using namespace std;
  auto mm = SCATMECH::MuellerMatrix({10, 1, 2, 3},
                                    { 1, 2, 3, 4},
                                    { 2, 3, 4, 5},
                                    { 3, 4, 5, 6});
  bool valid = mm.valid();
  cout << endl << "valid: " << valid << endl;
  return 0;
}

it gives the following output:

(140.788,0)
(-0.165284,0), (1.03934,0), (0.0864765,0), (5.6923e-15,0), 
(86.9183,0)
(0.432189,0), (-0.00713222,0), (0.803225,0), (0.408248,0), 
(0.294189,0)
(0.559796,0), (-0.0897377,0), (0.0985737,0), (-0.816497,0), 
(-4.02899e-15,0)
(0.687403,0), (-0.172343,0), (-0.606078,0), (0.408248,0),

So we see that SCATMECH identifies the same eigenvalues as those evaluated by numpy. Furthermore, the first element of the first eigen vector from python corresponds to the first elements of the eigenvectors from SCATMECH, or rather illustrated here:

[numpy]
[-0.16528353  0.43218914  0.55979591  0.68740268]

[scatmech]
...
(-0.165284,0), ...
...
(0.432189,0), ...
...
(0.559796,0), ...
...
(0.687403,0), ...

There is no such correspondence to the other eigenvector values as output from scatmech

firstly this may indicate that there are some indexing discrepancies, but also that something else may be wrong in the handling of the matrices.

the outputs from python/numpy were confirmed match with those from Mathematica so we are reasonably certain that they are correct.

discrepancy in eigen values in valid function

** don't bother... I was doing the wrong thing, this is not an issue **

not sure if this one is in the valid function or in the call to eigen. Was double checking validity of a mueller matrix and realized that for a given mueller matrix marked as valid by SCATMECH, python was showing complex eigen values. For examples and testing, I switched to using the armadillo library after confirming that I was getting identical values compared with numpy.

For diagnostic, I copied the MuellerMatrix::valid function into a new member function called validverbose (makes git merging testing just a bit easer). See the code and full outputs below where you see that armadillo is showing complex eigenvalues which scatmech is showing only real eigenvalues for the same matrix

example code:

#include <iostream>
#include <iomanip>
#include <mueller.h>
#include <armadillo>

using namespace std;
using namespace SCATMECH;

#define MATRIX {{ 1.66684837e+01,  1.04131056e-02,  1.25833154e-02, -2.15311646e-02},\
                { 1.05972383e-02,  1.66433868e+01, -3.44291121e-01, -1.42302616e-02},\
                { 4.15327400e-02,  3.62396240e-01,  1.66116199e+01,  2.86380202e-03},\
                {-2.79513281e-02,  1.99737698e-02,  2.94938814e-02,  1.66172810e+01}}

int main() {
  // first with armadillo
  auto M = arma::mat(MATRIX);
  cout << endl << "armadillo:" << endl << "---" << endl;
  arma::cx_vec eigenval;
  arma::cx_mat eigenvec;
  arma::eig_gen(eigenval, eigenvec, M);
  cout << "Matrix:" << endl << M;
  cout << "Eigen Values:" << endl << eigenval.t();
  cout << "Eigen Vectors:" << endl << eigenvec;

  // repeat with SCATMECH
  auto MM = MuellerMatrix(MATRIX);
  cout << endl << "SCATMECH:" << endl << "---" << endl;
  bool valid = MM.validverbose();  // prints out eigen values/vectors
  cout << boolalpha << "SCATMECH validverbose?: " << valid << endl;
  cout << boolalpha << "SCATMECH valid?: " << MM.valid() << endl; // just to make sure I get the same result

  return 0;
}

Output from example:

armadillo:
---
Matrix:
   16.6685    0.0104    0.0126   -0.0215
    0.0106   16.6434   -0.3443   -0.0142
    0.0415    0.3624   16.6116    0.0029
   -0.0280    0.0200    0.0295   16.6173
Eigen Values:
    (+1.663e+01,-3.523e-01)    (+1.663e+01,+3.523e-01)    (+1.668e+01,-0.000e+00)    (+1.661e+01,-0.000e+00)
Eigen Vectors:
    (+2.119e-02,-2.610e-02)    (+2.119e-02,+2.610e-02)    (-9.025e-01,+0.000e+00)    (+3.348e-01,+0.000e+00)
    (+3.071e-02,+6.963e-01)    (+3.071e-02,-6.963e-01)    (+9.019e-02,+0.000e+00)    (-4.522e-02,+0.000e+00)
    (+7.126e-01,+0.000e+00)    (+7.126e-01,-0.000e+00)    (-5.415e-02,+0.000e+00)    (-3.357e-02,+0.000e+00)
    (+4.343e-02,-5.832e-02)    (+4.343e-02,+5.832e-02)    (+4.177e-01,+0.000e+00)    (+9.406e-01,+0.000e+00)

SCATMECH:
---
Mueller Matrix: 
  1.6668e+01  1.0413e-02  1.2583e-02 -2.1531e-02
  1.0597e-02  1.6643e+01 -3.4429e-01 -1.4230e-02
  4.1533e-02  3.6240e-01  1.6612e+01  2.8638e-03
 -2.7951e-02  1.9974e-02  2.9494e-02  1.6617e+01
Small: 6.8047e-10
Eigen Values: 
( 2.7562e+02, 0.0000e+00),( 2.7656e+02, 0.0000e+00),( 2.7766e+02, 0.0000e+00),( 2.7732e+02, 0.0000e+00),
Eigen Vectors:
(-1.8214e-01, 0.0000e+00),( 1.7116e-01, 0.0000e+00),( 1.0158e+00, 0.0000e+00),(-5.1181e-01, 0.0000e+00),
( 9.2719e-02,-0.0000e+00),(-4.2647e-01, 0.0000e+00),( 2.5268e-01, 0.0000e+00),(-1.0995e+00, 0.0000e+00),
(-6.9992e-01, 0.0000e+00),( 6.1631e-01, 0.0000e+00),( 3.7482e-01, 0.0000e+00),(-5.8088e-01, 0.0000e+00),
( 6.8435e-01,-0.0000e+00),( 6.4256e-01, 0.0000e+00),( 7.8765e-02, 0.0000e+00),(-3.0891e-01, 0.0000e+00),
SCATMECH validverbose?: true
SCATMECH valid?: true

For validverbose:

    bool
    MuellerMatrix::validverbose() const
    {
        // Expanding the Mueller matrix slightly helps to ensure that numerical errors don't cause
        // legitimate Mueller matrices to appear invalid.
        MuellerMatrix expanded = *this + sqrt(numeric_limits<double>::epsilon()) * MuellerDepolarizer(m[0][0], 1);
        CFARRAY Q(4,1);
        CFARRAY W(4,4);
        CFARRAY M(4,4);
        for (int i=1; i<=4; ++i) {
            double Gi = i==1 ? 1 : -1;
            for (int j=1; j<=4; ++j) {
                M(i,j) = 0;
                for (int k=1; k<=4; ++k) {
                    double Gk = k==1 ? 1 : -1;
                    M(i,j) += Gi*expanded.m[k-1][i-1]*Gk*expanded.m[k-1][j-1];
                }
            }
        }
        eigen(M,Q,W,4);
        int imax=0;
        double max = -numeric_limits<double>::max();
        double small = (norm(Q(1)) + norm(Q(2)) + norm(Q(3)) + norm(Q(4)))*numeric_limits<double>::epsilon()*10.;

        //print the mueller matrix
        std::cout << "Mueller Matrix: " << std::endl;
        for (int i=0; i<4; ++i) {
          for (int j=0; j<4; ++j) {
            std::cout << std::setprecision(4) << std::scientific << std::setw(12) << expanded[i][j];
          }
          std::cout << std::endl;
        }
        //end

        //print the eigenvalues
        std::cout << "Small: " << small << std::endl;
        std::cout << "Eigen Values: " << std::endl;
        for (int i=1; i<=4; ++i) {
          std::cout << std::setprecision(4) << "(" << std::scientific << std::setw(11) << real(Q(i)) << ","
                    << std::setprecision(4) << std::scientific << std::setw(11) << imag(Q(i)) << "),";
        }
        std::cout << std::endl;
        //end

        for (int i=1; i<=4; ++i) {
            if (fabs(imag(Q(i)))>small) return false;
            if (abs(Q(i))>max) {
                max = abs(Q(i));
                imax = i;
            }
        }

        //print the eigenvectors
        std::cout << "Eigen Vectors:" << std::endl;
        for (int i=1; i<=4; ++i) {
          for (int j=1; j<=4; ++j) {
            std::cout << std::setprecision(4) << "(" << std::scientific << std::setw(11) << real(W(i, j)) << ","
                      << std::setprecision(4) << std::scientific << std::setw(11) << imag(W(i, j)) << "),";
          }
          std::cout << std::endl;
        }
        //end
        // Check that the vector with the largest eigenvalue is a valid Stokes vector.
        // After normalizing to the 1st element, the others must be real and the
        // sum-of-squares must be less than 1.
        COMPLEX W1 = W(1, imax);
        COMPLEX W2 = W(2, imax) / W1;
        COMPLEX W3 = W(3, imax) / W1;
        COMPLEX W4 = W(4, imax) / W1;
        if (fabs(imag(W2)) > small) return false;
        if (fabs(imag(W3)) > small) return false;
        if (fabs(imag(W4)) > small) return false;
        if (1. < sqr(real(W2)) + sqr(real(W3)) + sqr(real(W4))) return false;
        return true;
    }

Lu Chipman Decomposition can produce invalid mueller matrix

It looks like it could just be related to numerical precision, but I wanted to do a Lu Chipman Decomposition of a Mueller Matrix, and the depolarizing matrix produced was deemed to be invalid. It seems like there might be very small deltas in some of the zeros, but also it seems like the code should probably handle these kinds of scenarios somehow.

Below is the C++ program demonstrating the issue, along with the output:

#include "mueller.h"
#include <stdio.h>
#include <string>

using namespace std;
using namespace SCATMECH;

void print_mm(string title, MuellerMatrix& mm) {
  cout << endl << title << endl;
  for(int i=0; i < 4; i++){
    printf("% 18.14f % 18.14f % 18.14f % 18.14f \n",
           mm[i][0], mm[i][1], mm[i][2], mm[i][3]);
  }
  if (mm.valid()) {cout << "Mueller Matrix is Valid" << endl;}
  else {cout << "Mueller Matrix is NOT Valid" << endl;}
}

int main() {
  MuellerMatrix mm = MuellerZero();
  mm[0][0] = mm[1][1] = 23.05576897;
  mm[0][1] = mm[1][0] = -2.25341964;
  mm[2][2] = mm[3][3] = 22.897892;
  mm[2][3] = 0.39066115;
  mm[3][2] = -0.39066115;

  MuellerMatrix mmP = MuellerZero();
  MuellerMatrix mmR = MuellerZero();
  MuellerMatrix mmD = MuellerZero();

  mm.Lu_Chipman_Decomposition(mmP, mmR, mmD);
  print_mm("Main MM", mm);
  print_mm("Depolarizer", mmP);
  print_mm("Retarder", mmR);
  print_mm("Diattenuator", mmD);

  MuellerMatrix test = MuellerUnit(1.0);
  test[2][2] = test[3][3] = 0.99807550356182;
  print_mm("Test Depolarizer", test);
}

to compile I just added this to your Makefile:

muellertest/muellertest : muellertest/muellertest.cpp scatmech.a
	$(COMPILE) -o muellertest/muellertest.o -c muellertest/muellertest.cpp
	$(LINK) -o muellertest/muellertest$(EXE) muellertest/muellertest.o $(LIBRARIES)

and it produces this output, note that the Depolarizer is invalid whereas my manually created matrix is valid:

Main MM
 23.05576897000000  -2.25341964000000   0.00000000000000   0.00000000000000 
 -2.25341964000000  23.05576897000000   0.00000000000000   0.00000000000000 
  0.00000000000000   0.00000000000000  22.89789200000000   0.39066115000000 
  0.00000000000000   0.00000000000000  -0.39066115000000  22.89789200000000 
Mueller Matrix is Valid

Depolarizer
  1.00000000000000   0.00000000000000   0.00000000000000   0.00000000000000 
 -0.00000000000000   1.00000000000000   0.00000000000000   0.00000000000000 
  0.00000000000000   0.00000000000000   0.99807550356182  -0.00000000000000 
  0.00000000000000   0.00000000000000  -0.00000000000000   0.99807550356182 
Mueller Matrix is NOT Valid

Retarder
  1.00000000000000   0.00000000000000   0.00000000000000   0.00000000000000 
  0.00000000000000   1.00000000000000   0.00000000000000   0.00000000000000 
  0.00000000000000   0.00000000000000   0.99985449274432   0.01705852687086 
  0.00000000000000   0.00000000000000  -0.01705852687086   0.99985449274432 
Mueller Matrix is Valid

Diattenuator
 23.05576897000000  -2.25341964000000   0.00000000000000   0.00000000000000 
 -2.25341964000000  23.05576897000000   0.00000000000000   0.00000000000000 
  0.00000000000000   0.00000000000000  22.94538260138795   0.00000000000000 
  0.00000000000000   0.00000000000000   0.00000000000000  22.94538260138795 
Mueller Matrix is Valid

Test Depolarizer
  1.00000000000000   0.00000000000000   0.00000000000000   0.00000000000000 
  0.00000000000000   1.00000000000000   0.00000000000000   0.00000000000000 
  0.00000000000000   0.00000000000000   0.99807550356182   0.00000000000000 
  0.00000000000000   0.00000000000000   0.00000000000000   0.99807550356182 
Mueller Matrix is Valid

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.