Skip to content

Commit

Permalink
Merge branch 'main' into filtration_stacks
Browse files Browse the repository at this point in the history
  • Loading branch information
pheuer authored Jan 16, 2025
2 parents 88234f6 + f1a02db commit 894e23e
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 17 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# CR39py
Code for analyzing CR39 particle track data
# cr39py
[![codecov](https://codecov.io/gh/pheuer/cr39py/branch/main/graph/badge.svg?token=K0JA0QCH3S)](https://codecov.io/gh/pheuer/cr39py)
![ci workflow](https://github.com/pheuer/cr39py/actions/workflows/ci.yml/badge.svg)
![rtd](https://readthedocs.org/projects/cr39py/badge/?version=latest&style=flat)

Code for designing experiments using CR-39, calculating etch times for CR-39 processing, and analyzing CR-39 track data.

Documentation: https://cr39py.readthedocs.io/en/latest/
32 changes: 24 additions & 8 deletions docs/source/notebooks/nuclear_reaction_data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@
"# Estimating proton yields from nTOF measurements"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note: because of how ReadTheDocs works, this notebook is pre-executed"
]
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -28,7 +35,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 10,
"metadata": {},
"outputs": [
{
Expand All @@ -52,7 +59,7 @@
},
{
"cell_type": "code",
"execution_count": 27,
"execution_count": 11,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -106,19 +113,28 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 12,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"D3He Yield: 4.48e+07, Yield ratio: 0.45\n"
]
}
],
"source": [
"D_pressure = 6.5*u.atm\n",
"He_pressure = 13*u.atm\n",
"DDn_yield = 1e8\n",
"Tion = 11 * u.keV\n",
"D3He_proton_yield = d3hep_yield(DDn_yield D_pressure, He_pressure, Tion)\n",
"D3He_proton_yield = d3hep_yield(DDn_yield, D_pressure, He_pressure, Tion)\n",
"yield_ratio = D3He_proton_yield/DDn_yield\n",
"print(f\"D3He Yield: {D3He_proton_yield:.2e}, Yield ratio: {yratio:.2f}\")"
"print(f\"D3He Yield: {D3He_proton_yield:.2e}, Yield ratio: {yield_ratio:.2f}\")"
]
}],
}
],
"metadata": {
"kernelspec": {
"display_name": "dev312",
Expand Down
3 changes: 1 addition & 2 deletions src/cr39py/core/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class SilentPlotting:

def __enter__(self):
warnings.filterwarnings(
"ignore", message="FigureCanvasAgg", category=UserWarning
"ignore", "FigureCanvasAgg is non-interactive, and thus cannot be shown"
)
self.current_backend = get_backend()
plt.close("all")
Expand All @@ -41,4 +41,3 @@ def __enter__(self):
def __exit__(self, exc_type, exc_val, exc_tb):
plt.close("all")
matplotlib_use(self.current_backend)
warnings.resetwarnings()
24 changes: 21 additions & 3 deletions src/cr39py/scan/base_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ def __init__(self) -> None:
# Etch time, u.Quantity
self._etch_time = None

self.metadata = {}

@property
def tracks(self) -> TrackData:
"""
Expand Down Expand Up @@ -281,7 +283,9 @@ def axes(self) -> dict[Axis]:
# **********************************

@classmethod
def from_tracks(cls, tracks: TrackData, etch_time: float):
def from_tracks(
cls, tracks: TrackData, etch_time: float, metadata: dict | None = None
):
"""
Initialize a Scan object from an array of tracks.
Expand All @@ -292,11 +296,18 @@ def from_tracks(cls, tracks: TrackData, etch_time: float):
etch_time : float
Etch time in minutes.
metadata : dict
Dictionary of metadata to attach to the Scan object.
"""
obj = cls()

if metadata is None:
metadata = {}

obj._etch_time = etch_time * u.min
obj._tracks = tracks
obj.metadata = metadata

# Attach the selected tracks object to the axes objects
for ax in obj._axes.values():
Expand All @@ -309,6 +320,10 @@ def from_cpsa(cls, path: Path, etch_time: float | None = None):
"""
Initialize a Scan object from a CPSA file.
The etch_time can be automatically extracted from the filename
if it is included in a format like ``_#m_``, ``_#min_``, ``_#h_``,
``_#hr_``, etc.
Parameters
---------
path : `~pathlib.Path`
Expand All @@ -318,16 +333,19 @@ def from_cpsa(cls, path: Path, etch_time: float | None = None):
Etch time in minutes.
"""

# If the etch time is not provided, attempt to automatically extract
# from the CPSA filename
if etch_time is None:
etch_time = extract_etch_time(path)
if etch_time is None:
raise ValueError(
"Etch time not provided or successfully extracted from CPSA filename."
)

tracks = read_cpsa(path)
tracks, metadata = read_cpsa(path)

return cls.from_tracks(tracks, etch_time)
return cls.from_tracks(tracks, etch_time, metadata=metadata)

# **********************************
# Framesize setup
Expand Down
49 changes: 47 additions & 2 deletions src/cr39py/scan/cpsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,22 @@ def read_cpsa(path: Path) -> TrackData:
fy = pix_size * NFPy
print(f"...Microscope frame size fx, fy: {fx:.1e} um, {fy:.1e} um")

# Read the full datafile as int32 and separate out the track info
# Store the scan data in a dictionary, which will be returned along with
# the track data
metadata = {
"version": version,
"nx_frames": nx,
"ny_frames": ny,
"nframes": nframes,
"pixel_size": pix_size,
"threshold": threshold,
"NFPx": NFPx,
"NFPy": NFPy,
"frame_size_x": fx,
"frame_size_y": fy,
}

# Read the full datafile as int32 and separate out the track info
# Represents metadata from a single frame of cr39
# used when reading CPSA files
FrameHeader = namedtuple(
Expand Down Expand Up @@ -184,6 +198,16 @@ def read_cpsa(path: Path) -> TrackData:

frame_tracks.append(t)

# Read the footer, save the whole string into the metadata
# This contains a bunch of additional metadata
footer = b""
for line in file:
footer += line
footer = footer.decode("cp1250")
metadata["cpsa_footer"] = footer

# CPSA file now closed - post-process the data

# The order of the quantities in track data is:
# 0) x position (cm))
# 1) y position (cm)
Expand All @@ -205,7 +229,28 @@ def read_cpsa(path: Path) -> TrackData:
# Sort the yaxis (it's backwards...)
yax = np.sort(yax)

return tracks
# Extract some quantities from the footer using regex
# In scans that are part of a coincidence counting process, six marks will be made on the piece
# This regex pulls out the lines in the footer that record where these images were taken, which
# look like this
# IMAGES recorded before and after this scan were at:
# 0> (4.22560, 2.76639) [Notes: UL-m]
# 1> (0.52671, 2.75683) [Notes: UR-m]
# 2> (4.27408, 2.76639) [Notes: UL-FE]
# 3> (0.48003, 2.75683) [Notes: UR-NE]
# 4> (4.53513, 2.76701) [Notes: UL-NE]
# 5> (0.74559, 2.75503) [Notes: UR-FE]
note_fields = ["UL-m", "UR-m", "UL-FE", "UL-NE", "UR-FE", "UR-NE"]
for note in note_fields:
# This regex matches a line like "0> (4.22560, 2.76639) [Notes: UL-m]"
# and extracts the pair of points (4.22560, 2.76639) as the match group
pattern = f"\([+-]?([0-9]*[.][0-9]+),\s[+-]?([0-9]*[.][0-9]+)\)[\s]+\[Notes:[\s]+{note}\]\n"
match = re.search(pattern, footer)
if match is not None:
point = np.array([float(x) for x in match.groups()])
metadata[note] = point

return tracks, metadata


def extract_etch_time(path: Path) -> float:
Expand Down

0 comments on commit 894e23e

Please sign in to comment.