OpenAlphaDiffract-UI / frontend /src /utils /xrd-processing.test.js
linked-liszt's picture
Upload folder using huggingface_hub
6d08d46 verified
import { describe, it, expect } from 'vitest'
import {
convertWavelength,
interpolateData,
parseDIF,
extractMetadata,
} from './xrd-processing'
// ---------------------------------------------------------------------------
// convertWavelength
// ---------------------------------------------------------------------------
describe('convertWavelength', () => {
it('returns same angle when wavelengths match', () => {
const result = convertWavelength(10.0, 0.6199, 0.6199)
expect(result).toBeCloseTo(10.0, 5)
})
it('returns null for physically impossible conversion', () => {
// Large angle with large wavelength ratio can exceed sin > 1
const result = convertWavelength(80.0, 0.5, 2.0)
expect(result).toBeNull()
})
it('converts Cu Ka to synchrotron wavelength', () => {
const cuKa = 1.5406
const synchrotron = 0.6199
const theta = 20.0
const result = convertWavelength(theta, cuKa, synchrotron)
expect(result).not.toBeNull()
// Shorter wavelength -> smaller 2theta
expect(result).toBeLessThan(theta)
expect(result).toBeGreaterThan(0)
})
it('converts synchrotron to Cu Ka wavelength', () => {
const cuKa = 1.5406
const synchrotron = 0.6199
const theta = 10.0
const result = convertWavelength(theta, synchrotron, cuKa)
expect(result).not.toBeNull()
// Longer wavelength -> larger 2theta
expect(result).toBeGreaterThan(theta)
})
it('handles zero angle', () => {
const result = convertWavelength(0, 1.0, 2.0)
expect(result).toBeCloseTo(0, 5)
})
})
// ---------------------------------------------------------------------------
// interpolateData
// ---------------------------------------------------------------------------
describe('interpolateData', () => {
it('returns same data when target size matches', () => {
const x = [1, 2, 3]
const y = [10, 20, 30]
const result = interpolateData(x, y, 3)
expect(result.x).toEqual(x)
expect(result.y).toEqual(y)
})
it('interpolates to larger size with linear strategy', () => {
const x = [0, 10]
const y = [0, 100]
const result = interpolateData(x, y, 11, 0, 10, 'linear')
expect(result.x).toHaveLength(11)
expect(result.y).toHaveLength(11)
// Midpoint should be ~50
expect(result.y[5]).toBeCloseTo(50, 0)
})
it('sets out-of-range values to zero', () => {
const x = [5, 10, 15]
const y = [100, 200, 300]
const result = interpolateData(x, y, 20, 0, 20, 'linear')
// Points before x=5 should be 0
expect(result.y[0]).toBe(0)
// Points after x=15 should be 0
expect(result.y[19]).toBe(0)
})
it('supports cubic interpolation', () => {
const x = [0, 2, 4, 6, 8, 10]
const y = [0, 4, 16, 36, 64, 100]
const result = interpolateData(x, y, 11, 0, 10, 'cubic')
expect(result.x).toHaveLength(11)
expect(result.y).toHaveLength(11)
})
it('handles single point input', () => {
const x = [5]
const y = [100]
const result = interpolateData(x, y, 10, 0, 10, 'linear')
expect(result.x).toHaveLength(10)
})
})
// ---------------------------------------------------------------------------
// parseDIF
// ---------------------------------------------------------------------------
describe('parseDIF', () => {
it('parses space-separated data', () => {
const text = '5.0 100.0\n10.0 200.0\n15.0 300.0\n'
const result = parseDIF(text)
expect(result.x).toEqual([5.0, 10.0, 15.0])
expect(result.y).toEqual([100.0, 200.0, 300.0])
})
it('skips comment lines', () => {
const text = '# This is a comment\n5.0 100.0\n10.0 200.0\n'
const result = parseDIF(text)
expect(result.x).toHaveLength(2)
})
it('skips metadata lines', () => {
const text = 'CELL PARAMETERS: 5.0 5.0 5.0 90 90 90\nSPACE GROUP: Fm-3m\n5.0 100.0\n'
const result = parseDIF(text)
expect(result.x).toEqual([5.0])
})
it('skips lines starting with underscore', () => {
const text = '_cell_length_a 5.431\n5.0 100.0\n'
const result = parseDIF(text)
expect(result.x).toEqual([5.0])
})
it('handles empty input', () => {
const result = parseDIF('')
expect(result.x).toEqual([])
expect(result.y).toEqual([])
})
it('handles tab-separated data', () => {
const text = '5.0\t100.0\n10.0\t200.0\n'
const result = parseDIF(text)
expect(result.x).toEqual([5.0, 10.0])
expect(result.y).toEqual([100.0, 200.0])
})
it('skips lines starting with letters', () => {
const text = 'Header line\n5.0 100.0\nAnother header\n10.0 200.0\n'
const result = parseDIF(text)
expect(result.x).toEqual([5.0, 10.0])
})
})
// ---------------------------------------------------------------------------
// extractMetadata
// ---------------------------------------------------------------------------
describe('extractMetadata', () => {
it('detects wavelength from numeric pattern', () => {
const text = '_diffrn_radiation_wavelength 0.6199\n'
const result = extractMetadata(text)
expect(result.wavelength).toBeCloseTo(0.6199, 4)
})
it('detects Cu Ka radiation', () => {
const text = 'Radiation: Cu Ka\n'
const result = extractMetadata(text)
expect(result.wavelength).toBeCloseTo(1.5406, 4)
})
it('detects Mo Ka radiation', () => {
const text = 'Radiation: Mo Ka\n'
const result = extractMetadata(text)
expect(result.wavelength).toBeCloseTo(0.7107, 4)
})
it('extracts space group number', () => {
const text = '_symmetry_Int_Tables_number 225\n'
const result = extractMetadata(text)
expect(result.spaceGroup).toBe('225')
})
it('extracts cell parameters', () => {
const text = 'CELL PARAMETERS: 5.431 5.431 5.431 90.0 90.0 90.0\n'
const result = extractMetadata(text)
expect(result.cellParams).not.toBeNull()
expect(result.cellParams).toContain('5.431')
})
it('returns null for missing data', () => {
const result = extractMetadata('Some random text without metadata\n')
expect(result.wavelength).toBeNull()
expect(result.spaceGroup).toBeNull()
expect(result.cellParams).toBeNull()
})
it('rejects wavelengths outside X-ray range', () => {
const text = 'wavelength: 0.001\n'
const result = extractMetadata(text)
expect(result.wavelength).toBeNull()
})
})