Repository: wright-group/WrightTools

Issue:
Fluence calculation
I oftentimes find myself needing to calculate a fluence from a power reading. I then go through ten minutes of unit conversions to do the calculation. I think we should make a function in `kit._calculate.py` that does this calculation.

For instance:
```
def fluence(power_mW, reprate_Hz, photon_energy_eV, beam_radius_mm):
      
     return uj/cm2, photons/cm2
```

Any thoughts?

Discussion:
pep8speaks: Hello @darienmorrow! Thanks for submitting the PR.

 - In the file [`WrightTools/kit/_calculate.py`](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py), following are the PEP8 issues :

> [Line 21:67](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L21): [W291](https://duckduckgo.com/?q=pep8%20W291) trailing whitespace
> [Line 24:1](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L24): [W293](https://duckduckgo.com/?q=pep8%20W293) blank line contains whitespace
> [Line 44:61](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L44): [W291](https://duckduckgo.com/?q=pep8%20W291) trailing whitespace
> [Line 45:73](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L45): [W291](https://duckduckgo.com/?q=pep8%20W291) trailing whitespace
> [Line 51:1](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L51): [W293](https://duckduckgo.com/?q=pep8%20W293) blank line contains whitespace
> [Line 56:40](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L56): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 60:34](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L60): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 61:17](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L61): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 62:22](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L62): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 64:58](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L64): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 65:30](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L65): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 66:30](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L66): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 67:25](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L67): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 69:78](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L69): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 70:24](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L70): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 71:27](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L71): [E261](https://duckduckgo.com/?q=pep8%20E261) at least two spaces before inline comment
> [Line 76:1](https://github.com/wright-group/WrightTools/blob/4e15e503274465ef90aaa870da00ab2f112e4de1/WrightTools/kit/_calculate.py#L76): [W293](https://duckduckgo.com/?q=pep8%20W293) blank line contains whitespace




ksunden: we may wish to look at `unyt` to simplify the computation here, perhaps.

I'm considering replacing the  custom code in the `units` package with that library, which has some potential positive side effects
ksunden: Also, the pep8 warning still mentioned above are all in the docstring (which is why `black` didn't catch it)

I believe `autopep8` will catch that if you don't feel like tracking those down manually
ksunden: https://github.com/ambv/black/issues/318 
(`black` will NOT handle this case, as there exist cases [perhaps poorly reasoned, but still] where doing so will affect correctness of the program, and `black` commits to not making any such changes)
darienmorrow: @ksunden The reason I am not checking the value is because that requires me to be 100% sure about the correct value of the calculation. I am waiting for someone to check my math before I do that.
darienmorrow: @ksunden The reason I do not check for the value in my test is because checking for the value requires me to be absolutely sure about the result of my calculation. I was hoping for someone to check my math before I specify values.
darienmorrow: @ksunden I now check the value in the test. 
ksunden: Im holding off rereviewing until the math itself is approved, I would like to check thebither two outputs as well in the test, please.

Patch:
diff --git a/WrightTools/data/_data.py b/WrightTools/data/_data.py
index a48cf49..1124658 100644
--- a/WrightTools/data/_data.py
+++ b/WrightTools/data/_data.py
@@ -625,7 +625,7 @@ class Data(Group):
             New data object with the downscaled channels and axes
         """
         if name is None:
-            name = self.natural_name + '_downscaled'
+            name = self.natural_name + "_downscaled"
         if parent is None:
             newdata = Data(name=name)
         else:
@@ -633,13 +633,13 @@ class Data(Group):
 
         for channel in self.channels:
             name = channel.natural_name
-            newdata.create_channel(name=name,
-                                   values=downscale_local_mean(channel[:], tup),
-                                   units=channel.units)
+            newdata.create_channel(
+                name=name, values=downscale_local_mean(channel[:], tup), units=channel.units
+            )
         args = []
         for i, axis in enumerate(self.axes):
             if len(axis.variables) > 1:
-                raise NotImplementedError('downscale only works with simple axes currently')
+                raise NotImplementedError("downscale only works with simple axes currently")
             variable = axis.variables[0]
             name = variable.natural_name
             args.append(name)
diff --git a/WrightTools/kit/_calculate.py b/WrightTools/kit/_calculate.py
index 3af381c..16ef7d2 100644
--- a/WrightTools/kit/_calculate.py
+++ b/WrightTools/kit/_calculate.py
@@ -1,7 +1,7 @@
 """Calculate."""
 
 
-# --- import --------------------------------------------------------------------------------------
+# --- import -------------------------------------------------------------
 
 
 import numpy as np
@@ -9,13 +9,78 @@ import numpy as np
 from .. import units as wt_units
 
 
-# --- define --------------------------------------------------------------------------------------
+# --- define -------------------------------------------------------------
 
 
-__all__ = ["mono_resolution", "nm_width", "symmetric_sqrt"]
+__all__ = ["fluence", "mono_resolution", "nm_width", "symmetric_sqrt"]
 
 
-# --- functions -----------------------------------------------------------------------------------
+# --- functions ----------------------------------------------------------
+
+
+def fluence(
+    power_mW,
+    color,
+    beam_radius,
+    reprate_Hz,
+    pulse_width,
+    color_units="wn",
+    beam_radius_units="mm",
+    pulse_width_units="fs_t",
+    area_type="even",
+):
+    """Calculate the fluence of a beam.
+
+    Parameters
+    ----------
+    power_mW : number
+        Time integrated power of beam.
+    color : number
+        Color of beam in units.
+    beam_radius : number
+        Radius of beam in units.
+    reprate_Hz : number
+        Laser repetition rate in inverse seconds (Hz).
+    pulse_width : number
+        Pulsewidth of laser in units
+    color_units : string (optional)
+        Valid wt.units color unit identifier. Default is wn.
+    beam_radius_units : string (optional)
+        Valid wt.units distance unit identifier. Default is mm.
+    pulse_width_units : number
+        Valid wt.units time unit identifier. Default is fs.
+    area_type : string (optional)
+        Type of calculation to accomplish for Gaussian area.
+        Currently nothing other than the default of even is implemented.
+
+    Returns
+    -------
+    tuple
+        Fluence in uj/cm2, photons/cm2, and peak intensity in GW/cm2
+
+    """
+    # calculate beam area
+    if area_type == "even":
+        radius_cm = wt_units.converter(beam_radius, beam_radius_units, "cm")
+        area_cm2 = np.pi * radius_cm ** 2  # cm^2
+    else:
+        raise NotImplementedError
+    # calculate fluence in uj/cm^2
+    ujcm2 = power_mW / reprate_Hz  # mJ
+    ujcm2 *= 1e3  # uJ
+    ujcm2 /= area_cm2  # uJ/cm^2
+    # calculate fluence in photons/cm^2
+    energy = wt_units.converter(color, color_units, "eV")  # eV
+    photonscm2 = ujcm2 * 1e-6  # J/cm2
+    photonscm2 /= 1.60218e-19  # eV/cm2
+    photonscm2 /= energy  # photons/cm2
+    # calculate peak intensity in GW/cm^2
+    pulse_width_s = wt_units.converter(pulse_width, pulse_width_units, "s_t")  # seconds
+    GWcm2 = ujcm2 / 1e6  # J/cm2
+    GWcm2 /= pulse_width_s  # W/cm2
+    GWcm2 /= 1e9
+    # finish
+    return ujcm2, photonscm2, GWcm2
 
 
 def mono_resolution(grooves_per_mm, slit_width, focal_length, output_color, output_units="wn"):


Test patch:
diff --git a/tests/kit/fluence.py b/tests/kit/fluence.py
new file mode 100644
index 0000000..5355d64
--- /dev/null
+++ b/tests/kit/fluence.py
@@ -0,0 +1,20 @@
+"""Test fluence."""
+
+
+# --- import -------------------------------------------------------------
+
+
+import numpy as np
+
+import WrightTools as wt
+
+
+# --- test ---------------------------------------------------------------
+
+
+def test_0():
+    out = wt.kit.fluence(1, 2, .1, 1000, 1, "eV", "cm", "ps_t")
+    checks = (31.83098, 99336493460095.2, 0.03183098)
+    assert np.isclose(checks[0], out[0], rtol=1e-3)
+    assert np.isclose(checks[1], out[1], rtol=1e-3)
+    assert np.isclose(checks[2], out[2], rtol=1e-3)