diff options
Diffstat (limited to 'julia/CrystFEL/src')
-rw-r--r-- | julia/CrystFEL/src/CrystFEL.jl | 94 | ||||
-rw-r--r-- | julia/CrystFEL/src/cell.jl | 430 | ||||
-rw-r--r-- | julia/CrystFEL/src/crystal.jl | 121 | ||||
-rw-r--r-- | julia/CrystFEL/src/datatemplates.jl | 127 | ||||
-rw-r--r-- | julia/CrystFEL/src/detgeom.jl | 47 | ||||
-rw-r--r-- | julia/CrystFEL/src/diffcalc.jl | 44 | ||||
-rw-r--r-- | julia/CrystFEL/src/image.jl | 356 | ||||
-rw-r--r-- | julia/CrystFEL/src/indexing.jl | 151 | ||||
-rw-r--r-- | julia/CrystFEL/src/mergeutils.jl | 78 | ||||
-rw-r--r-- | julia/CrystFEL/src/millepede.jl | 42 | ||||
-rw-r--r-- | julia/CrystFEL/src/peaklist.jl | 117 | ||||
-rw-r--r-- | julia/CrystFEL/src/peaksearch.jl | 74 | ||||
-rw-r--r-- | julia/CrystFEL/src/reflists.jl | 411 | ||||
-rw-r--r-- | julia/CrystFEL/src/streams.jl | 138 | ||||
-rw-r--r-- | julia/CrystFEL/src/symmetry.jl | 134 |
15 files changed, 2364 insertions, 0 deletions
diff --git a/julia/CrystFEL/src/CrystFEL.jl b/julia/CrystFEL/src/CrystFEL.jl new file mode 100644 index 00000000..897d2614 --- /dev/null +++ b/julia/CrystFEL/src/CrystFEL.jl @@ -0,0 +1,94 @@ +""" + CrystFEL + +Julia bindings for CrystFEL data structures and routines + +## Quick start +```julia + using CrystFEL + ... +``` +""" +module CrystFEL + +libcrystfel = "libcrystfel.so" + +# Configure libcrystfel to use Julia's memory management. This is needed so +# that the Julia GC knows about the memory we allocate via libcrystfel +# routines. Otherwise, potentially very large objects will be kept hanging +# around in memory because Julia thinks it's using a very small amount of +# memory, and rarely runs the GC. In the case of image structures, the +# difference between apparent and true memory use can be a factor of a million! +function __init__() + @ccall libcrystfel.set_mm_funcs(cglobal(:jl_malloc)::Ptr{Cvoid}, + cglobal(:jl_free)::Ptr{Cvoid}, + cglobal(:jl_calloc)::Ptr{Cvoid}, + cglobal(:jl_realloc)::Ptr{Cvoid})::Cint +end + +include("cell.jl") +using .UnitCells +export UnitCell, LatticeType, CenteringType, UniqueAxis +export TriclinicLattice, MonoclinicLattice, OrthorhombicLattice +export TetragonalLattice, HexagonalLattice, RhombohedralLattice, CubicLattice +export PrimitiveCell, ACenteredCell, BCenteredCell, CCenteredCell +export BodyCenteredCell, FaceCenteredCell, RhombohedralCell, RhombohedralCellOnHexagonalAxes +export NoUniqueAxis, UnknownUniqueAxis, UniqueAxisA, UniqueAxisB, UniqueAxisC +export rotatecell + +include("detgeom.jl") +using .DetGeoms +export Panel, DetGeom + +include("symmetry.jl") +using .Symmetry +export SymOpList, asymmetricindices + +include("datatemplates.jl") +using .DataTemplates +export DataTemplate, loaddatatemplate, wavelength, cameralength +export translategroup!, rotategroup! + +include("peaklist.jl") +using .PeakLists +export PeakList + +include("reflists.jl") +using .RefLists +export RefList, loadreflist, savereflist! +export Reflection, MergedReflection, UnmergedReflection + +include("crystal.jl") +using .Crystals +export Crystal, InternalCrystal + +include("image.jl") +using .Images +export Image + +include("diffcalc.jl") +using .DiffractionCalculations +export predictreflections, calculatepartialities! +export PartialityModel, UnityModel, XSphereModel, OffsetModel, RandomModel, GeneralGaussianModel + +include("indexing.jl") +using .Indexing +export Indexer, index + +include("streams.jl") +using .Streams +export Stream, chunkwrite, chunkread, allcrystals + +include("millepede.jl") +using .Millepede +export Mille + +include("peaksearch.jl") +using .PeakSearch +export zaefpeaks, peakfinder8, peakfinder9 + +include("mergeutils.jl") +using .MergeUtils +export @addmeasurement, cstddev, mergereflections + +end # of module diff --git a/julia/CrystFEL/src/cell.jl b/julia/CrystFEL/src/cell.jl new file mode 100644 index 00000000..a18810bb --- /dev/null +++ b/julia/CrystFEL/src/cell.jl @@ -0,0 +1,430 @@ +module UnitCells + +using Random +using Printf + +import ..CrystFEL: libcrystfel +export UnitCell, LatticeType, CenteringType, UniqueAxis +export TriclinicLattice, MonoclinicLattice, OrthorhombicLattice +export TetragonalLattice, HexagonalLattice, RhombohedralLattice, CubicLattice +export PrimitiveCell, ACenteredCell, BCenteredCell, CCenteredCell +export BodyCenteredCell, FaceCenteredCell, RhombohedralCell, RhombohedralCellOnHexagonalAxes +export NoUniqueAxis, UnknownUniqueAxis, UniqueAxisA, UniqueAxisB, UniqueAxisC +export rotatecell + + +# Represents the real C-side (opaque) structure. +mutable struct InternalUnitCell end + +# The Julia-side structure, needed to house the pointer to the C structure +# Without this, we would only ever have a Ptr{DataTemplate}, not a DataTemplate. +mutable struct UnitCell + internalptr::Ptr{InternalUnitCell} +end + + +""" +Enumeration of the seven Bravais lattice types: `TriclinicLattice`, +`MonoclinicLattice`, `OrthorhombicLattice`, `TetragonalLattice`, +`RhombohedralLattice`, `HexagonalLattice`, `CubicLattice`. +""" +@enum LatticeType begin + TriclinicLattice + MonoclinicLattice + OrthorhombicLattice + TetragonalLattice + RhombohedralLattice + HexagonalLattice + CubicLattice +end + + +""" +Enumeration of unit cell centering possibilities: `PrimitiveCell`, +`ACenteredCell`, `BCenteredCell`, `CCenteredCell`, `BodyCenteredCell` +(I-centering), `FaceCenteredCell` (F-centering), and `RhombohedralCell` +(R-centering, primitive rhombohedral cell). `RhombohedralCellOnHexagonalAxes` +indicates "H-centering" as used by the protein data bank, which is different +from the "triple hexagonal cell" described in the International Tables. +""" +@enum CenteringType begin + PrimitiveCell = Int('P') + ACenteredCell = Int('A') + BCenteredCell = Int('B') + CCenteredCell = Int('C') + BodyCenteredCell = Int('I') + FaceCenteredCell = Int('F') + RhombohedralCell = Int('R') + RhombohedralCellOnHexagonalAxes = Int('H') +end + + +""" +Enumeration of unique axis possibilities. The possibilities are `UniqueAxisA`, +`UniqueAxisB` and `UniqueAxisC`. Alternatively, use `NoUniqueAxis` if the type +of unit cell does not have a unique axis (triclinic, orthorhombic, cubic or +rhombohedral). `UnknownUniqueAxis` means that the unique axis is not known. +""" +@enum UniqueAxis begin + NoUniqueAxis = Int('*') + UnknownUniqueAxis = Int('?') + UniqueAxisA = Int('a') + UniqueAxisB = Int('b') + UniqueAxisC = Int('c') +end + + +""" + UnitCell(latticetype, centering, uniqueaxis, a, b, c, α, β, γ) + +Creates a CrystFEL UnitCell, in an undefined orientation, from the given +parameters. The axis lengths (a, b, c) should be in *Ångstroms*, and the +angles (α, β, γ) should be in *degrees* - note that this is different to the +equivalent function in CrystFEL's C API. + +See the documentation for `LatticeType`, `CenteringType` and `UniqueAxis` for +possible values. You can also use the characters `'a'`, `'b'` and `'c'` for +`uniqueaxis`, or `'P'`, `'A'`, `'B'`, `'C'`, `'I'`, `'F'`, `'R'` and `'H`' for +`centering. + +Corresponds to CrystFEL C API function `cell_new_from_parameters` with follow-up +calls to `cell_set_centering`, `cell_set_lattice_type` and `cell_set_unique_axis`. +""" +function UnitCell(latticetype, centering, uniqueaxis, a, b, c, α, β, γ) + + ninety(a) = isapprox(a, 90, atol=0.1) + + if latticetype == OrthorhombicLattice + if !(ninety(α) && ninety(β) && ninety(γ)) + throw(ArgumentError("All angles must be 90°")) + end + + elseif latticetype == TetragonalLattice + if !(ninety(α) && ninety(β) && ninety(γ)) + throw(ArgumentError("All angles must be 90°")) + end + + elseif latticetype == CubicLattice + if !(ninety(α) && ninety(β) && ninety(γ)) + throw(ArgumentError("All angles must be 90°")) + end + + elseif latticetype == HexagonalLattice + if uniqueaxis == UniqueAxisA + if !isapprox(b, c, rtol=0.01) + throw(ArgumentError("b and c lengths should be equal")) + end + elseif uniqueaxis == UniqueAxisB + if !isapprox(a, c, rtol=0.01) + throw(ArgumentError("a and c lengths should be equal")) + end + elseif uniqueaxis == UniqueAxisC + if !isapprox(a, b, rtol=0.01) + throw(ArgumentError("a and b lengths should be equal")) + end + else + throw(ArgumentError("Hexagonal cell requires a unique axis")) + end + end + + out = ccall((:cell_new_from_parameters, libcrystfel), + Ptr{InternalUnitCell}, + (Cdouble,Cdouble,Cdouble,Cdouble,Cdouble,Cdouble), + a/1e10, b/1e10, c/1e10, deg2rad(α), deg2rad(β), deg2rad(γ)) + if out == C_NULL + throw(ArgumentError("Failed to create unit cell")) + end + + ccall((:cell_set_centering, libcrystfel), + Cvoid, (Ptr{InternalUnitCell},Cchar), + out, centering) + + ccall((:cell_set_unique_axis, libcrystfel), + Cvoid, (Ptr{InternalUnitCell},Cchar), + out, uniqueaxis) + + ccall((:cell_set_lattice_type, libcrystfel), + Cvoid, (Ptr{InternalUnitCell},Cint), + out, latticetype) + + cell = UnitCell(out) + + finalizer(cell) do x + ccall((:cell_free, libcrystfel), + Cvoid, (Ptr{InternalUnitCell},), x.internalptr) + end + + return cell +end + + + +""" + UnitCell(latticetype, centering, a, b, c, α, β, γ) + +A convenience constructor which attempts to determine the unique axis +automatically from the cell parameters. If the unique axis is not obvious, +an `ArgumentError` will be thrown. +""" +function UnitCell(latticetype, centering, a, b, c, α, β, γ) + + notninety(a) = !isapprox(a, 90, atol=0.5) + ninety(a) = isapprox(a, 90, atol=0.1) + onetwenty(a) = isapprox(a, 120, atol=0.1) + + if latticetype == TriclinicLattice + ua = NoUniqueAxis + elseif latticetype == OrthorhombicLattice + ua = NoUniqueAxis + elseif latticetype == RhombohedralLattice + ua = NoUniqueAxis + elseif latticetype == CubicLattice + ua = NoUniqueAxis + + elseif latticetype == MonoclinicLattice + if notninety(α) && ninety(β) && ninety(γ) + ua = UniqueAxisA + elseif ninety(α) && notninety(β) && ninety(γ) + ua = UniqueAxisB + elseif ninety(α) && ninety(β) && notninety(γ) + ua = UniqueAxisC + else + throw(ArgumentError("Can't determine unique axis")) + end + + elseif latticetype == TetragonalLattice + if isapprox(b, c, rtol=0.01) && !isapprox(a, b, rtol=0.05) + ua = UniqueAxisA + elseif isapprox(a, c, rtol=0.01) && !isapprox(a, b, rtol=0.05) + ua = UniqueAxisB + elseif isapprox(a, b, rtol=0.01) && !isapprox(c, b, rtol=0.05) + ua = UniqueAxisC + else + throw(ArgumentError("Can't determine unique axis")) + end + + elseif latticetype == HexagonalLattice + if onetwenty(α) && ninety(β) && ninety(γ) + ua = UniqueAxisA + elseif ninety(α) && onetwenty(β) && ninety(γ) + ua = UniqueAxisB + elseif ninety(α) && ninety(β) && onetwenty(γ) + ua = UniqueAxisC + else + throw(ArgumentError("Can't determine unique axis")) + end + end + + UnitCell(latticetype, centering, ua, a, b, c, α, β, γ) + +end + + +""" + UnitCell(latticetype, centering, a, b, c) + +Construct a `UnitCell` for an `OrthorhombicLattice`, `TetragonalLattice` or +`CubicLattice`. +""" +function UnitCell(latticetype::LatticeType, centering::CenteringType, a::Real, b::Real, c::Real) + if latticetype in (OrthorhombicLattice, TetragonalLattice, CubicLattice) + UnitCell(latticetype, centering, a, b, c, 90, 90, 90) + else + throw(ArgumentError("More parameters needed for this type of lattice")) + end +end + + +""" + UnitCell(CubicLattice, centering, a) + +Construct a `UnitCell` for a `CubicLattice`. +""" +function UnitCell(latticetype::LatticeType, centering::CenteringType, a::Real) + if latticetype == CubicLattice + UnitCell(latticetype, centering, a, a, a, 90, 90, 90) + else + throw(ArgumentError("More parameters needed for this type of lattice")) + end +end + + +""" + UnitCell(RhombohedralLattice, a, α) + +Construct a `UnitCell` for a `RhombohedralLattice`. +""" +function UnitCell(latticetype::LatticeType, a::Real, α::Real) + if latticetype == RhombohedralLattice + UnitCell(latticetype, RhombohedralCell, a, a, a, α, α, α) + else + throw(ArgumentError("More parameters needed for this type of lattice")) + end +end + + +function getlatticetype(cell) + lt = ccall((:cell_get_lattice_type, libcrystfel), + Cint, (Ptr{InternalUnitCell},), cell.internalptr) + cen = ccall((:cell_get_centering, libcrystfel), + Cchar, (Ptr{InternalUnitCell},), cell.internalptr) + ua = ccall((:cell_get_unique_axis, libcrystfel), + Cchar, (Ptr{InternalUnitCell},), cell.internalptr) + return LatticeType(lt),CenteringType(cen),UniqueAxis(ua) +end + + +function getcellparams(cell) + let a=Ref{Cdouble}(0), + b=Ref{Cdouble}(0), + c=Ref{Cdouble}(0), + α=Ref{Cdouble}(0), + β=Ref{Cdouble}(0), + γ=Ref{Cdouble}(0) + ccall((:cell_get_parameters, libcrystfel), + Cvoid, (Ptr{InternalUnitCell}, + Ref{Cdouble},Ref{Cdouble},Ref{Cdouble}, + Ref{Cdouble},Ref{Cdouble},Ref{Cdouble}), + cell.internalptr, a, b, c, α, β, γ) + return (a=a[], b=b[], c=c[], α=α[], β=β[], γ=γ[]) + end +end + + +# Returns the direct-space basis vectors as a Julia matrix +# See matrix-notation.pdf for information. This returns an "M-matrix". +function directcartesianmatrix(uc) + ax = Ref{Cdouble}(0) + ay = Ref{Cdouble}(0) + az = Ref{Cdouble}(0) + bx = Ref{Cdouble}(0) + by = Ref{Cdouble}(0) + bz = Ref{Cdouble}(0) + cx = Ref{Cdouble}(0) + cy = Ref{Cdouble}(0) + cz = Ref{Cdouble}(0) + out = @ccall libcrystfel.cell_get_cartesian(uc.internalptr::Ptr{InternalUnitCell}, + ax::Ref{Cdouble}, ay::Ref{Cdouble}, az::Ref{Cdouble}, + bx::Ref{Cdouble}, by::Ref{Cdouble}, bz::Ref{Cdouble}, + cx::Ref{Cdouble}, cy::Ref{Cdouble}, cz::Ref{Cdouble})::Cint + if out != 0 + throw(ErrorException("Failed to convert cell parameters")) + end + return [ax[] bx[] cx[]; ay[] by[] cy[]; az[] bz[] cz[]] +end + + +# Returns the reciprocal-space basis vectors as a Julia matrix +# See matrix-notation.pdf for information. This returns an "R-matrix". +function reciprocalcartesianmatrix(uc) + ax = Ref{Cdouble}(0) + ay = Ref{Cdouble}(0) + az = Ref{Cdouble}(0) + bx = Ref{Cdouble}(0) + by = Ref{Cdouble}(0) + bz = Ref{Cdouble}(0) + cx = Ref{Cdouble}(0) + cy = Ref{Cdouble}(0) + cz = Ref{Cdouble}(0) + out = @ccall libcrystfel.cell_get_reciprocal(uc.internalptr::Ptr{InternalUnitCell}, + ax::Ref{Cdouble}, ay::Ref{Cdouble}, az::Ref{Cdouble}, + bx::Ref{Cdouble}, by::Ref{Cdouble}, bz::Ref{Cdouble}, + cx::Ref{Cdouble}, cy::Ref{Cdouble}, cz::Ref{Cdouble})::Cint + if out != 0 + throw(ErrorException("Failed to convert cell parameters")) + end + return [ax[] ay[] az[]; bx[] by[] bz[]; cx[] cy[] cz[]] +end + + +function Base.propertynames(uc::UnitCell; private=false) + (:a, :b, :c, :α, :β, :γ, :latticetype, :cellparams, + :directcartesian, :reciprocalcartesian, + :internalptr) +end + + +function Base.getproperty(uc::UnitCell, name::Symbol) + if name === :internalptr + getfield(uc, :internalptr) + elseif name === :cellparams + return getcellparams(uc) + elseif name === :latticetype + return getlatticetype(uc) + elseif name === :a + return getcellparams(uc).a + elseif name === :b + return getcellparams(uc).b + elseif name === :c + return getcellparams(uc).c + elseif name === :α + return getcellparams(uc).α + elseif name === :β + return getcellparams(uc).β + elseif name === :γ + return getcellparams(uc).γ + elseif name === :directcartesian + return directcartesianmatrix(uc) + elseif name === :reciprocalcartesian + return reciprocalcartesianmatrix(uc) + end +end + + +function Base.show(io::IO, uc::UnitCell) + write(io, "UnitCell(") + lt,cen,ua = uc.latticetype + show(io, lt); write(io, ", ") + show(io, cen); write(io, ", ") + show(io, ua); write(io, ",\n ") + @printf(io, "%.3f Å, %.3f Å, %.3f Å, %.3f°, %.3f°, %.3f°", + uc.a*1e10, uc.b*1e10, uc.c*1e10, + rad2deg(uc.α), rad2deg(uc.β), rad2deg(uc.γ)) + write(io, ")") +end + + +# This type is for talking to libcrystfel, and is named to avoid conflicting +# with other quaternion libraries. +mutable struct CrystFELQuaternion + w::Cdouble + x::Cdouble + y::Cdouble + z::Cdouble +end + +function randomquat() + r = ()->2.0*rand(Float64)-1.0 + q = [r(), r(), r(), r()] + q ./ √(sum(q.^2)) +end + + +""" + rotatecell(uc::UnitCell, quaternion) + +Rotate a unit cell according to a quaternion (represented as a vector of 4 floats). +""" +function rotatecell(uc, quat) + q = CrystFELQuaternion(quat...) + out = @ccall libcrystfel.cell_rotate(uc.internalptr::Ptr{InternalUnitCell}, + q::CrystFELQuaternion)::Ptr{InternalUnitCell} + if out == C_NULL + throw(ErrorException("Failed to rotate unit cell")) + end + UnitCell(out) +end + + +""" + rotatecell(uc::UnitCell) + +Rotate a unit cell at random in three dimensions. Use this routine for +simulating serial crystallography datasets. + +Equivalent to CrystFEL routine `cell_rotate(uc, random_quaternion(<rng>))`. +""" +rotatecell(uc) = rotatecell(uc, randomquat()) + + +end # of module diff --git a/julia/CrystFEL/src/crystal.jl b/julia/CrystFEL/src/crystal.jl new file mode 100644 index 00000000..f05f651f --- /dev/null +++ b/julia/CrystFEL/src/crystal.jl @@ -0,0 +1,121 @@ +module Crystals + +using Printf + +import ..CrystFEL: libcrystfel +import ..CrystFEL.RefLists: RefList, InternalRefList, UnmergedReflection +import ..CrystFEL.UnitCells: UnitCell, InternalUnitCell +export Crystal, InternalCrystal + +# Represents the real C-side (opaque) structure. +mutable struct InternalCrystal end + +mutable struct Crystal + internalptr::Ptr{InternalCrystal} + cell +end + + +function Crystal(cell::UnitCell; profileradius=2e6, mosaicity=0) + + out = ccall((:crystal_new, libcrystfel), + Ptr{InternalCrystal}, ()) + + if out == C_NULL + throw(ArgumentError("Failed to create crystal")) + end + + # We make a copy of the cell, to avoid memory model shenanigans + uccopy = ccall((:cell_new_from_cell, libcrystfel), + Ptr{InternalUnitCell}, (Ptr{InternalUnitCell},), + cell.internalptr) + + ccall((:crystal_set_cell, libcrystfel), + Cvoid, (Ptr{InternalCrystal},Ptr{InternalUnitCell}), + out, uccopy) + + ccall((:crystal_set_profile_radius, libcrystfel), + Cvoid, (Ptr{InternalCrystal},Cdouble), + out, profileradius) + + ccall((:crystal_set_mosaicity, libcrystfel), + Cvoid, (Ptr{InternalCrystal},Cdouble), + out, mosaicity) + + cr = Crystal(out, nothing) + + finalizer(cr) do x + ccall((:crystal_free, libcrystfel), Cvoid, (Ptr{InternalCrystal},), + x.internalptr) + end + + return cr +end + + +function Base.setproperty!(cr::Crystal, name::Symbol, val) + if name === :internalptr + setfield!(cr, :internalptr, val) + end +end + + +function getcell(cr) + out = @ccall libcrystfel.crystal_relinquish_cell(cr.internalptr::Ptr{InternalCrystal})::Ptr{InternalUnitCell} + if getfield(cr, :cell) === nothing || getfield(cr, :cell).internalptr != out + if out != C_NULL + setfield!(cr, :cell, UnitCell(out)) + else + setfield!(cr, :cell, nothing) + end + end + return getfield(cr, :cell) +end + + +function Base.getproperty(cr::Crystal, name::Symbol) + if name === :internalptr + getfield(cr, :internalptr) + elseif name === :cell + return getcell(cr) + elseif name === :Bfac + return @ccall libcrystfel.crystal_get_Bfac(cr.internalptr::Ptr{InternalCrystal})::Cdouble + elseif name === :osf + return @ccall libcrystfel.crystal_get_osf(cr.internalptr::Ptr{InternalCrystal})::Cdouble + elseif name === :mos + return @ccall libcrystfel.crystal_get_mosaicity(cr.internalptr::Ptr{InternalCrystal})::Cdouble + elseif name === :r + return @ccall libcrystfel.crystal_get_profile_radius(cr.internalptr::Ptr{InternalCrystal})::Cdouble + elseif name === :resolution + return @ccall libcrystfel.crystal_get_resolution_limit(cr.internalptr::Ptr{InternalCrystal})::Cdouble + elseif name === :flag + return @ccall libcrystfel.crystal_get_user_flag(cr.internalptr::Ptr{InternalCrystal})::Cint + else + throw(ErrorException("Type Crystal has no field "*String(name))) + end +end + + +function Base.show(io::IO, mime::MIME"text/plain", cr::Crystal) + @printf(io, "CrystFEL.Crystal(%p):\n\n", cr.internalptr) + if cr.cell !== nothing + show(io, cr.cell) + write(io, "\n\n") + else + write(io, "Unit cell parameters not set\n\n") + end + println(io, " Linear scale factor: ", cr.osf) + println(io, " Debye-Walle factor: ", cr.Bfac) + println(io, " Mosaicity: ", cr.mos) + println(io, " Profile radius: ", cr.r/1e9, " nm⁻¹") + println(io, " Resolution limit: ", cr.resolution) + println(io, " Flag: ", cr.flag) +end + + +function Base.show(io::IO, cr::Crystal) + @printf(io, "CrystFEL.Crystal(%p)", cr.internalptr) +end + + +end # of module diff --git a/julia/CrystFEL/src/datatemplates.jl b/julia/CrystFEL/src/datatemplates.jl new file mode 100644 index 00000000..b063aa90 --- /dev/null +++ b/julia/CrystFEL/src/datatemplates.jl @@ -0,0 +1,127 @@ +module DataTemplates + +import ..CrystFEL: libcrystfel +export DataTemplate, InternalDataTemplate, loaddatatemplate +export wavelength, cameralength +export translategroup!, rotategroup! + +# Represents the real C-side (opaque) structure. +mutable struct InternalDataTemplate end + +# The Julia-side structure, needed to house the pointer to the C structure +# Without this, we would only ever have a Ptr{DataTemplate}, not a DataTemplate. +mutable struct DataTemplate + internalptr::Ptr{InternalDataTemplate} +end + +""" + loaddatatemplate(filename) + +Creates a CrystFEL DataTemplate by loading a geometry file. + +Corresponds to CrystFEL C API function `data_template_new_from_file()`. +""" +function loaddatatemplate(filename::AbstractString) + + out = ccall((:data_template_new_from_file, libcrystfel), + Ptr{InternalDataTemplate}, (Cstring,), filename) + if out == C_NULL + throw(ArgumentError("Failed to load geometry file")) + end + + dt = DataTemplate(out) + + finalizer(dt) do x + ccall((:data_template_free, libcrystfel), + Cvoid, (Ptr{InternalDataTemplate},), x.internalptr) + end + + return dt +end + + +""" + wavelength(datatemplate) + +Retrieves the radiation wavelength from a `DataTemplate`, if possible. + +It's not always possible to do this. Some geometry files declare the +wavelength as a parameter to be retrieved fom each image's metadata. A +program using this routine should take this possibility into account. + +Corresponds to CrystFEL C API function `data_template_get_wavelength_if_possible`. +""" +function wavelength(dtempl::DataTemplate) + wl = ccall((:data_template_get_wavelength_if_possible, libcrystfel), + Cdouble, (Ptr{InternalDataTemplate},), dtempl.internalptr) + if isnan(wl) + return nothing + else + return wl + end +end + + +""" + cameralength(datatemplate) + +Retrieves the camera length from a `DataTemplate`, if possible. + +It's not always possible to do this. Some geometry files declare the +detector position(s) as a parameter to be retrieved fom each image's metadata. A +program using this routine should take this possibility into account. + +Corresponds to CrystFEL C API function `data_template_get_clen_if_possible`. +""" +function cameralength(dtempl::DataTemplate) + clen = ccall((:data_template_get_clen_if_possible, libcrystfel), + Cdouble, (Ptr{InternalDataTemplate},), dtempl.internalptr) + if isnan(clen) + return nothing + else + return clen + end +end + + +""" + translategroup!(datatemplate, groupname, xshift, yshift, zshift) + +Modifies `DataTemplate` by moving the specified panel group by the specified +amount (in metres). + +Corresponds to CrystFEL C API function `data_template_translate_group_m`. +""" +function translategroup!(dtempl::DataTemplate, groupname, xshift, yshift, zshift) + r = @ccall libcrystfel.data_template_translate_group_m(dtempl.internalptr::Ptr{InternalDataTemplate}, + groupname::Cstring, + xshift::Cdouble, + yshift::Cdouble, + zshift::Cdouble)::Cint + if r != 0 + throw(ErrorException("Failed to shift DataTemplate")) + end + +end + + +""" + rotategroup!(datatemplate, groupname, angle, axis) + +Modifies `DataTemplate` by rotating the specified panel group by the specified +amount (in degrees) about the specified xaxis (:x, :y or :z). + +Corresponds to CrystFEL C API function `data_template_rotate_group`. +""" +function rotategroup!(dtempl::DataTemplate, groupname, angle, axis) + r = @ccall libcrystfel.data_template_rotate_group(dtempl.internalptr::Ptr{InternalDataTemplate}, + groupname::Cstring, + deg2rad(angle)::Cdouble, + String(axis)[1]::Cchar)::Cint + if r != 0 + throw(ErrorException("Failed to rotate DataTemplate")) + end + +end + +end # of module diff --git a/julia/CrystFEL/src/detgeom.jl b/julia/CrystFEL/src/detgeom.jl new file mode 100644 index 00000000..ba943d98 --- /dev/null +++ b/julia/CrystFEL/src/detgeom.jl @@ -0,0 +1,47 @@ +module DetGeoms +export Panel + +mutable struct Panel + name::Cstring + cx::Cdouble + cy::Cdouble + cz::Cdouble + pixel_pitch::Cdouble + adu_per_photon::Cdouble + max_adu::Cdouble + fsx::Cdouble + fsy::Cdouble + fsz::Cdouble + ssx::Cdouble + ssy::Cdouble + ssz::Cdouble + w::Cint + h::Cint + group::Ptr{Cvoid} +end + + +mutable struct DetGeom + panels::Ptr{Panel} + n_panels::Cint + top_group::Ptr{Cvoid} +end + + +function Base.show(io::IO, p::Panel) + write(io, "Panel(") + write(io, "name=\"") + write(io, unsafe_string(p.name)) + write(io, "\", center=(") + show(io, p.cx); write(io, ", "); show(io, p.cy); write(io, ", "); show(io, p.cz) + write(io, "), fs=(") + show(io, p.fsx); write(io, ", "); show(io, p.fsy); write(io, ", "); show(io, p.fsz) + write(io, "), ss=(") + show(io, p.ssx); write(io, ", "); show(io, p.ssy); write(io, ", "); show(io, p.ssz) + write(io, "), size=(") + show(io, p.w); write(io, ", "); show(io, p.h) + write(io, "))") +end + + +end # of module diff --git a/julia/CrystFEL/src/diffcalc.jl b/julia/CrystFEL/src/diffcalc.jl new file mode 100644 index 00000000..1c6aea9c --- /dev/null +++ b/julia/CrystFEL/src/diffcalc.jl @@ -0,0 +1,44 @@ +module DiffractionCalculations + +import ..CrystFEL: libcrystfel +import ..CrystFEL.Images: InternalImage, Image +import ..CrystFEL.Crystals: InternalCrystal, Crystal +import ..CrystFEL.RefLists: RefList, UnmergedReflection, InternalRefList +import ..CrystFEL.Symmetry: SymOpList +export predictreflections, calculatepartialities! +export PartialityModel, UnityModel, XSphereModel, OffsetModel, RandomModel, GeneralGaussianModel + + +""" +Enumeration of the available partiality models. +""" +@enum PartialityModel begin + UnityModel + XSphereModel + OffsetModel + RandomModel + GeneralGaussianModel +end + + +function predictreflections(cr::Crystal, image::Image; maxres=1e10) + + refls = @ccall libcrystfel.predict_to_res(cr.internalptr::Ptr{InternalCrystal}, + image.internalptr::Ptr{InternalImage}, + maxres::Cdouble)::Ptr{InternalRefList} + sym = SymOpList("1") + return RefList{UnmergedReflection}(refls, sym) +end + + +function calculatepartialities!(reflist::RefList{UnmergedReflection}, + cr::Crystal, image::Image; model=XSphereModel, maxres=1e10) + + @ccall libcrystfel.calculate_partialities(reflist.internalptr::Ptr{InternalRefList}, + cr.internalptr::Ptr{InternalCrystal}, + image.internalptr::Ptr{InternalImage}, + model::Cint)::Cvoid +end + + +end # of module diff --git a/julia/CrystFEL/src/image.jl b/julia/CrystFEL/src/image.jl new file mode 100644 index 00000000..4090dc61 --- /dev/null +++ b/julia/CrystFEL/src/image.jl @@ -0,0 +1,356 @@ +module Images + +using Printf + +import ..CrystFEL: libcrystfel +import ..CrystFEL.DataTemplates: DataTemplate, InternalDataTemplate +import ..CrystFEL.DetGeoms: DetGeom +import ..CrystFEL.PeakLists: PeakList, InternalPeakList +import ..CrystFEL.Crystals: Crystal, InternalCrystal +import ..CrystFEL.RefLists: RefList, InternalRefList, UnmergedReflection +import ..CrystFEL.Symmetry: SymOpList +export Image + +const HEADER_CACHE_SIZE = 128 + +mutable struct CrystalRefListPair + crystal::Ptr{InternalCrystal} + reflist::Ptr{InternalRefList} + owns_crystal::Cint + owns_reflist::Cint +end + +mutable struct InternalImage + dp::Ptr{Ptr{Cfloat}} + bad::Ptr{Ptr{Cint}} + sat::Ptr{Ptr{Cfloat}} + hit::Cint + crystals::Ptr{CrystalRefListPair} + n_crystals::Cint + indexed_by::Cint + n_indexing_tries::Cint + detgeom::Ptr{DetGeom} + data_source_type::Cint + filename::Cstring + ev::Cstring + data_block::Ptr{Cvoid} + data_block_size::Csize_t + meta_data::Cstring + header_cache::NTuple{HEADER_CACHE_SIZE, Ptr{Cvoid}} + n_cached_headers::Cint + id::Cint + serial::Cint + spectrum::Ptr{Cvoid} + lambda::Cdouble + div::Cdouble + bw::Cdouble + peak_resolution::Cdouble + peaklist::Ptr{InternalPeakList} + ida::Ptr{Cvoid} + owns_peaklist::Cint +end + + +mutable struct Image + internalptr::Ptr{InternalImage} + peaklist::Union{Nothing,PeakList} + crystals + reflists +end + + +function makecrystallist(image, listptr, n) + + crystals = [] + + if listptr == C_NULL + return crystals + end + + for i in 1:n + pairptr = unsafe_load(listptr, i) + + # Re-use old Crystal if possible + n = findfirst(getfield(image, :crystals)) do x + x.internalptr == pairptr.crystal + end + + if n !== nothing + cr = getfield(image, :crystals)[n] + else + cr = Crystal(pairptr.crystal, nothing) + end + + if pairptr.reflist == C_NULL + reflist = nothing + else + reflist = RefList{UnmergedReflection}(pairptr.reflist, SymOpList("1")) + pairptr.owns_reflist = 0 + finalizer(reflist) do x + @ccall libcrystfel.reflist_free(x.internalptr::Ptr{InternalRefList})::Cvoid + end + end + push!(crystals, (crystal=cr, reflections=reflist)) + pairptr.owns_crystal = 0 + unsafe_store!(listptr, pairptr, i) + finalizer(cr) do x + @ccall libcrystfel.crystal_free(x.internalptr::Ptr{InternalCrystal})::Cvoid + end + end + + image.crystals = map(x->x.crystal, crystals) + image.reflists = map(x->x.reflections, crystals) + return crystals + +end + + +function getpeaklist(image) + idata = unsafe_load(image.internalptr) + if (getfield(image, :peaklist) === nothing) || + (idata.peaklist != getfield(image, :peaklist).internalptr) + if idata.peaklist != C_NULL + setfield!(image, :peaklist, PeakList(idata.peaklist)) + # From now on, Julia is completely responsible for freeing the peaklist + idata.owns_peaklist = 0 + unsafe_store!(image.internalptr, idata) + else + setfield!(image, :peaklist, nothing) + end + end + return getfield(image, :peaklist) +end + + +function Base.getproperty(image::Image, name::Symbol) + if name === :internalptr + getfield(image, :internalptr) + elseif name === :peaklist + getpeaklist(image) + else + idata = unsafe_load(image.internalptr) + + if name === :crystals + return makecrystallist(image, + getfield(idata, :crystals), + getfield(idata, :n_crystals)) + + else + getfield(idata, name) + end + end +end + + +strdup(str) = @ccall libcrystfel.cfstrdup(str::Cstring)::Cstring + + +function assert_type(val, type) + if !(val isa type) + throw(ArgumentError("Must be a "*string(type)*" (have "*string(typeof(val))*" instead)")) + end +end + + +function set_peaklist(image, new_pl) + + assert_type(new_pl, PeakList) + + idata = unsafe_load(image.internalptr) + if (idata.owns_peaklist == 1) && (idata.peaklist != C_NULL) + @ccall libcrystfel.image_feature_list_free(idata.peaklist::Ptr{InternalPeakList})::Cvoid + end + idata.peaklist = new_pl.internalptr + idata.owns_peaklist = 0 + unsafe_store!(image.internalptr, idata) + setfield!(image, :peaklist, new_pl) + +end + + +function Base.setproperty!(image::Image, name::Symbol, val) + if name === :internalptr + setfield!(image, :internalptr, val) + else + + if name === :peaklist + return set_peaklist(image, val) + + elseif name === :filename + assert_type(val, AbstractString) + val = strdup(val) + + elseif name === :ev + assert_type(val, AbstractString) + val = strdup(val) + + elseif name === :crystals + return setfield!(image, :crystals, val) + + elseif name === :reflists + return setfield!(image, :reflists, val) + + end + + idata = unsafe_load(image.internalptr) + setproperty!(idata, name, val) + unsafe_store!(image.internalptr, idata) + + end +end + + +function Base.propertynames(image::Image; private=false) + if private + fieldnames(InternalImage) + else + tuple(fieldnames(InternalImage)..., :internalptr) + end +end + + +function Base.push!(image::Image, cr::Crystal) + @ccall libcrystfel.image_add_crystal(image.internalptr::Ptr{InternalImage}, + cr.internalptr::Ptr{InternalCrystal})::Cvoid + + idata = unsafe_load(image.internalptr) + ncryst = idata.n_crystals + pairptr = unsafe_load(idata.crystal_refls, ncryst) + pairptr.owns_crystal = 0 + unsafe_store!(idata.crystal_refls, pairptr, ncryst) + push!(getfield(image, :crystals), cr) +end + + +function Base.push!(image::Image, cr::Crystal, reflections::RefList{UnmergedReflection}) + @ccall libcrystfel.image_add_crystal_refls(image.internalptr::Ptr{InternalImage}, + cr.internalptr::Ptr{InternalCrystal}, + reflections.internalptr::Ptr{InternalRefList})::Cvoid + idata = unsafe_load(image.internalptr) + ncryst = idata.n_crystals + pairptr = unsafe_load(idata.crystals, ncryst) + pairptr.owns_crystal = 0 + pairptr.owns_reflist = 0 + unsafe_store!(idata.crystals, pairptr, ncryst) + push!(getfield(image, :crystals), cr) + push!(getfield(image, :reflists), reflections) +end + + +""" + Image(dtempl::DataTemplate) + +Creates a CrystFEL image structure, not linked to any file or data block, +i.e. for simulation purposes. This will fail if `dtempl` contains any +references to metadata fields, e.g. `photon_energy = /LCLS/photon_energy eV`. + +Corresponds to CrystFEL C API function `image_create_for_simulation()`. +""" +function Image(dtempl::DataTemplate) + + out = ccall((:image_create_for_simulation, libcrystfel), + Ptr{Image}, (Ref{InternalDataTemplate},), dtempl.internalptr) + if out == C_NULL + throw(ArgumentError("Failed to create image")) + end + + image = Image(out, nothing, [], []) + + finalizer(image) do x + ccall((:image_free, libcrystfel), Cvoid, (Ptr{InternalImage},), x.internalptr) + end + + return image +end + + +""" + Image(dtempl::DataTemplate, filename::AbstractString, event::AbstractString, + no_image_data=false, no_mask_data=false) + +Loads an image from the filesystem. + +Corresponds to CrystFEL C API function `image_read()`. +""" +function Image(dtempl::DataTemplate, + filename::AbstractString, + event::AbstractString="//", + no_image_data=false, + no_mask_data=false) + + out = @ccall libcrystfel.image_read(dtempl.internalptr::Ptr{InternalDataTemplate}, + filename::Cstring, event::Cstring, + no_image_data::Cint, no_mask_data::Cint, + C_NULL::Ptr{Cvoid})::Ptr{Image} + if out == C_NULL + throw(ArgumentError("Failed to load image")) + end + + image = Image(out, nothing, [], []) + + finalizer(image) do x + ccall((:image_free, libcrystfel), Cvoid, (Ptr{InternalImage},), x.internalptr) + end + + return image +end + + +function Base.show(io::IO, mime::MIME"text/plain", image::Image) + + idata = unsafe_load(image.internalptr) + @printf(io, "CrystFEL.Image(%p):\n\n", image.internalptr) + + println(io, " Serial number: ", idata.serial) + write(io, " Filename: ") + if idata.filename == C_NULL + write(io, "<not set>") + else + write(io, unsafe_string(idata.filename)) + end + write(io, "\n") + + write(io, " Frame ID: ") + if idata.ev == C_NULL + write(io, "<not set>") + else + write(io, unsafe_string(idata.ev)) + end + write(io, "\n") + + write(io, "\n") + println(io, " Wavelength: ", idata.lambda*1e10, " Å") + println(io, " Bandwidth: ", idata.bw*100, " %") + println(io, " Divergence: ", idata.div*1e3, " mrad") + + write(io, "\n") + if idata.peaklist != C_NULL + let npk = @ccall libcrystfel.image_feature_count(idata.peaklist::Ptr{InternalPeakList})::Cint + println(io, " Number of peaks: ", npk) + end + else + println(io, " Number of peaks: 0 (no peak list)") + end + + println(io, " Estimated resolution: ", 1e10/idata.peak_resolution, " Å") + write(io, " Hit flag: ") + if idata.hit != 0 + write(io, "set") + else + write(io, "not set") + end + write(io, "\n") + + write(io, "\n") + println(io, " Number of crystals: ", idata.n_crystals) + println(io, " Number of indexing attempts made: ", idata.n_crystals) + println(io, " Indexed by algorithm: ", idata.indexed_by) +end + + +function Base.show(io::IO, image::Image) + @printf(io, "CrystFEL.Image(%p)", image.internalptr) +end + + +end # of module diff --git a/julia/CrystFEL/src/indexing.jl b/julia/CrystFEL/src/indexing.jl new file mode 100644 index 00000000..543246ec --- /dev/null +++ b/julia/CrystFEL/src/indexing.jl @@ -0,0 +1,151 @@ +module Indexing + +import ..CrystFEL: libcrystfel +import ..CrystFEL.UnitCells: UnitCell, InternalUnitCell +import ..CrystFEL.Images: Image, InternalImage +import ..CrystFEL.DataTemplates: wavelength, cameralength +export Indexer, index + +mutable struct IndexingPriv end + +mutable struct Indexer + indexingpriv::Ptr{IndexingPriv} +end + + +function indexflags(retry, multilattice, refine, peakcheck, cellcheck) + flags = 0 + if retry + flags |= 1 + end + if multilattice + flags |= 2 + end + if refine + flags |= 4 + end + if peakcheck + flags |= 32 + end + if cellcheck + flags |= 64 + end + return flags +end + + +""" + Indexer(methods, dtempl, cell; + tolerances=(0.05,0.05,0.05,1.5,1.5,1.5), + retry=true, multilattice=false, refine=true, + peakcheck=true, cellcheck=true, + wavelength_estimate=nothing, clen_estimate=missing, + n_threads=1) + +Creates a new CrystFEL indexing engine, which you can later apply to CrystFEL +`Image` structures using `index()`. +""" +function Indexer(methods, dtempl, cell; tolerances=(0.05,0.05,0.05,1.5,1.5,1.5), + retry=true, multilattice=false, refine=true, peakcheck=true, cellcheck=true, + wavelength_estimate=nothing, clen_estimate=missing, n_threads=1) + + taketwoopts = Ref{Ptr{Cvoid}}(C_NULL) + xgandalfopts = Ref{Ptr{Cvoid}}(C_NULL) + pinkindexeropts = Ref{Ptr{Cvoid}}(C_NULL) + felixopts = Ref{Ptr{Cvoid}}(C_NULL) + fromfileopts = Ref{Ptr{Cvoid}}(C_NULL) + asdfopts = Ref{Ptr{Cvoid}}(C_NULL) + + @ccall libcrystfel.default_method_options(taketwoopts::Ref{Ptr{Cvoid}}, + xgandalfopts::Ref{Ptr{Cvoid}}, + pinkindexeropts::Ref{Ptr{Cvoid}}, + felixopts::Ref{Ptr{Cvoid}}, + fromfileopts::Ref{Ptr{Cvoid}}, + asdfopts::Ref{Ptr{Cvoid}})::Cvoid + + flags = indexflags(retry, multilattice, refine, peakcheck, cellcheck) + + let wlfromdtempl = wavelength(dtempl) + if !isnothing(wlfromdtempl) + wavelength_estimate = wlfromdtempl + else + if isnothing(wavelength_estimate) + throw(ArgumentError("Wavelength cannot be determined from data template. "* + "Use Indexer(wavelength_estimate=...)")) + end + end + end + + let clenfromdtempl = cameralength(dtempl) + if !isnothing(clenfromdtempl) + clen_estimate = clenfromdtempl + else + if isnothing(clen_estimate) + throw(ArgumentError("Camera length cannot be determined from data template. "* + "Use Indexer(clen_estimate=...)")) + end + end + end + + tols = Vector{Cfloat}(undef, 6) + tols[1] = tolerances[1] + tols[2] = tolerances[2] + tols[3] = tolerances[3] + tols[4] = deg2rad(tolerances[4]) + tols[5] = deg2rad(tolerances[5]) + tols[6] = deg2rad(tolerances[6]) + + out = @ccall libcrystfel.setup_indexing(methods::Cstring, + cell.internalptr::Ptr{InternalUnitCell}, + tols::Ptr{Cfloat}, + flags::Cint, + wavelength_estimate::Cdouble, + clen_estimate::Cdouble, + n_threads::Cint, + taketwoopts[]::Ptr{Cvoid}, + xgandalfopts[]::Ptr{Cvoid}, + pinkindexeropts[]::Ptr{Cvoid}, + felixopts[]::Ptr{Cvoid}, + fromfileopts[]::Ptr{Cvoid}, + asdfopts[]::Ptr{Cvoid})::Ptr{IndexingPriv} + + if out == C_NULL + throw(ErrorException("Indexing setup failed")) + end + + indexer = Indexer(out) + + finalizer(indexer) do x + @ccall libcrystfel.cleanup_indexing(x.indexingpriv::Ptr{IndexingPriv})::Cvoid + end + + return indexer + +end + + +""" + index(image::Image, indexer::Indexer; mille=nothing) + +Index `image` using `indexer`. + +If `mille` is a valid `Mille` object, detector geometry alignment data will +be written. +""" +function index(image::Image, idxr::Indexer; mille=nothing) + + if mille === nothing + imille = C_NULL + else + imille = mille.internalptr + end + + @ccall libcrystfel.index_pattern_4(image.internalptr::Ptr{InternalImage}, + idxr.indexingpriv::Ptr{IndexingPriv}, + C_NULL::Ptr{Cvoid}, + C_NULL::Ptr{Cvoid}, + imille::Ptr{Cvoid})::Cvoid +end + + +end # of module diff --git a/julia/CrystFEL/src/mergeutils.jl b/julia/CrystFEL/src/mergeutils.jl new file mode 100644 index 00000000..701992db --- /dev/null +++ b/julia/CrystFEL/src/mergeutils.jl @@ -0,0 +1,78 @@ +module MergeUtils + +import ..CrystFEL: libcrystfel +using ..CrystFEL.RefLists +using ..CrystFEL.Symmetry +import ..CrystFEL.UnitCells: InternalUnitCell +export @addmeasurement, cstddev, mergereflections + + +macro addmeasurement(measurement, weight, + mean, sumweight, wksp) + return quote + delta = $(esc(measurement)) - $(esc(mean)) + newsumweight = $(esc(sumweight)) + $(esc(weight)) + R = delta * $(esc(weight)) / newsumweight + $(esc(mean)) += R + $(esc(wksp)) += $(esc(sumweight)) * delta * R + $(esc(sumweight)) = newsumweight + end +end + +cstddev(nmeas, work1, work2) = sqrt(work2/work1)/sqrt(nmeas) + + +struct Polarisation + fraction::Cdouble + angle::Cdouble + disable::Cint +end + +function polarisation_correction!(reflist, cell, polfrac, polangle) + pol = Polarisation(polfrac, rad2deg(polangle), 0) + @ccall libcrystfel.polarisation_correction(reflist.internalptr::Ptr{InternalRefList}, + cell.internalptr::Ptr{InternalUnitCell}, + pol::Ref{Polarisation})::Cvoid +end + + +function mergereflections(correction, crystalrefls, sym) + + merged = RefList{MergedReflection}(sym) + + for (cr,reflections) in crystalrefls + + polarisation_correction!(reflections, cr.cell, 1.0, 0.0) + + for refl in reflections + + indices = asymmetricindices(sym, refl.indices) + model_version = get!(merged, indices) + @addmeasurement(correction(refl, cr), 1.0, + model_version.intensity, + model_version.temp1, + model_version.temp2) + model_version.nmeasurements += 1 + + end + end + + for refl in merged + if refl.nmeasurements > 1 + refl.sigintensity = cstddev(refl.nmeasurements, refl.temp1, refl.temp2) + else + # Cannot delete a reflection from a list (especially not + # while iterating), but setting nmeasurements to zero + # prevents it from being written to the output file. + refl.nmeasurements = 0 + end + end + + return merged + +end + +mergereflections(crystalrefls, sym) = mergereflections((refl,cr)->refl.intensity, crystalrefls, sym) + + +end # of module diff --git a/julia/CrystFEL/src/millepede.jl b/julia/CrystFEL/src/millepede.jl new file mode 100644 index 00000000..a65256dc --- /dev/null +++ b/julia/CrystFEL/src/millepede.jl @@ -0,0 +1,42 @@ +module Millepede + +import ..CrystFEL: libcrystfel +export Mille + +mutable struct InternalMille end + +mutable struct Mille + internalptr::Ptr{InternalMille} +end + + +""" + Mille(filename) + +Creates a new "Mille" object, which can be passed to `CrystFEL.Indexing.index()` +to accumulate detector geometry alignment information in the specified file. + +When you've finished adding data, call `close()` on the Mille object. This will +be done automatically when the object is freed by the garbage collector, but +that might not happen until Julia exits. + +Corresponds to CrystFEL C API routine `crystfel_mille_new`. +""" +function Mille(filename::AbstractString) + out = @ccall libcrystfel.crystfel_mille_new(filename::Cstring)::Ptr{InternalMille} + if out == C_NULL + throw(ArgumentError("Failed to create Millepede object")) + end + finalizer(close, Mille(out)) +end + + +function Base.close(x::Mille) + if x.internalptr != C_NULL + @ccall libcrystfel.crystfel_mille_free(x.internalptr::Ptr{InternalMille})::Cvoid + x.internalptr = C_NULL + end +end + + +end # of module diff --git a/julia/CrystFEL/src/peaklist.jl b/julia/CrystFEL/src/peaklist.jl new file mode 100644 index 00000000..9eedad27 --- /dev/null +++ b/julia/CrystFEL/src/peaklist.jl @@ -0,0 +1,117 @@ +module PeakLists + +using Printf +import ..CrystFEL: libcrystfel +export PeakList, InternalPeakList + +mutable struct InternalPeak + fs::Cdouble + ss::Cdouble + panelnumber::Cint + intensity::Cdouble + name::Cstring +end + +mutable struct InternalPeakList end + +mutable struct PeakList + internalptr::Ptr{InternalPeakList} +end + + +function PeakList() + out = @ccall libcrystfel.image_feature_list_new()::Ptr{InternalPeakList} + if out == C_NULL + throw(ArgumentError("Failed to create peak list")) + end + finalizer(PeakList(out)) do pl + @ccall libcrystfel.image_feature_list_free(pl.internalptr::Ptr{InternalPeakList})::Cvoid + end +end + + +Base.copy(::PeakList) = throw(ErrorException("Cannot copy(::PeakList), must deepcopy")) + +function Base.deepcopy(peaklist::PeakList) + out = @ccall libcrystfel.image_feature_list_copy(peaklist.internalptr::Ptr{InternalPeakList})::Ptr{InternalPeakList} + if out == C_NULL + throw(ErrorException("Failed to copy peak list")) + end + PeakList(out) +end + +function Base.length(peaklist::PeakList) + @ccall libcrystfel.image_feature_count(peaklist.internalptr::Ptr{InternalPeakList})::Cint +end + +Base.firstindex(peaklist::PeakList) = 1 +Base.lastindex(peaklist::PeakList) = length(peaklist) + + +function Base.push!(peaklist::PeakList, fs, ss, panelnumber, intensity, name=nothing) + + rname = isnothing(name) ? C_NULL : @ccall strdup(name::Cstring)::Cstring + + @ccall libcrystfel.image_add_feature(peaklist.internalptr::Ptr{InternalPeakList}, + fs::Cdouble, ss::Cdouble, panelnumber::Cint, + intensity::Cdouble, rname::Cstring)::Cvoid +end + + +function Base.getindex(peaklist::PeakList, n) + out = @ccall(libcrystfel.image_get_feature(peaklist.internalptr::Ptr{InternalPeakList}, + (n-1)::Cint)::Ptr{InternalPeak}) + if out == C_NULL + throw(BoundsError(peaklist, n)) + end + pdata = unsafe_load(out) + if pdata.name == C_NULL + nname = nothing + else + nname = unsafe_string(pdata.name) + end + return (fs=pdata.fs, ss=pdata.ss, panelnumber=pdata.panelnumber, + intensity=pdata.intensity, name=nname) +end + + +function Base.iterate(peaklist::PeakList) + if length(peaklist) > 0 + return peaklist[1],(1,length(peaklist)) + else + return nothing + end +end + + +function Base.iterate(peaklist::PeakList, state) + let nxt = state[1]+1 + len = state[2] + if nxt == len+1 + return nothing + else + return peaklist[nxt],(nxt,state[2]) + end + end +end + + +function Base.show(io::IO, ::MIME"text/plain", peaklist::PeakList) + println(io, "Peak list with ", length(peaklist), " peaks") + print(io, " fs ss panel intensity name") + let n = 0 + for pk in Iterators.take(peaklist, 11) + if n == 10 + # We have printed 10 already, and are here again. Truncate... + print(io, "\n ⋮ ⋮ ⋮ ⋮ ⋮") + break + end + write(io, "\n") + @printf(io, "%7.2f %7.2f %6i %10.2f %s", + pk.fs, pk.ss, pk.panelnumber, pk.intensity, pk.name) + n += 1 + end + end +end + +end # of module diff --git a/julia/CrystFEL/src/peaksearch.jl b/julia/CrystFEL/src/peaksearch.jl new file mode 100644 index 00000000..aaaee16c --- /dev/null +++ b/julia/CrystFEL/src/peaksearch.jl @@ -0,0 +1,74 @@ +module PeakSearch + +import ..CrystFEL: libcrystfel +import ..CrystFEL.Images: InternalImage +import ..CrystFEL.PeakLists: PeakList, InternalPeakList + +export zaefpeaks, peakfinder8, peakfinder9 + + +function tf10(val) + if val + return 1 + else + return 0 + end +end + + +function zaefpeaks(image; threshold=100, mingrad=100000, minsnr=5, + radiusinn=4, radiusmid=5, radiusout=7, usesaturated=true) + out = @ccall libcrystfel.search_peaks(image.internalptr::Ptr{InternalImage}, + threshold::Cfloat, + mingrad::Cfloat, + minsnr::Cfloat, + radiusinn::Cdouble, + radiusmid::Cdouble, + radiusout::Cdouble, + tf10(usesaturated)::Cint)::Ptr{InternalPeakList} + if out == C_NULL + throw(ErrorException("Peak search failed")) + end + PeakList(out) +end + + + +function peakfinder8(image; threshold=100, minsnr=5, minpix=2, maxpix=200, + localbg=3, minres=0, maxres=5000, usesaturated=true, maxpeaks=2000) + out = @ccall libcrystfel.peakfinder8(image.internalptr::Ptr{InternalImage}, + maxpeaks::Cint, + threshold::Cfloat, + minsnr::Cfloat, + minpix::Cint, + maxpix::Cint, + localbg::Cint, + minres::Cint, + maxres::Cint, + tf10(usesaturated)::Cint, + 0::Cint, + C_NULL::Ptr{Cvoid})::Ptr{InternalPeakList} + if out == C_NULL + throw(ErrorException("Peak search failed")) + end + PeakList(out) +end + + +function peakfinder9(image; minsnrbig=7, minsnrpeak=6, minsnrwhole=5, minbgsig=11, + brightpxcut=-Inf, window=5) + out = @ccall libcrystfel.search_peaks_peakfinder9(image.internalptr::Ptr{InternalImage}, + minsnrbig::Cfloat, + minsnrpeak::Cfloat, + minsnrwhole::Cfloat, + minbgsig::Cfloat, + brightpxcut::Cfloat, + window::Cint)::Ptr{InternalPeakList} + if out == C_NULL + throw(ErrorException("Peak search failed")) + end + PeakList(out) +end + + +end # of module diff --git a/julia/CrystFEL/src/reflists.jl b/julia/CrystFEL/src/reflists.jl new file mode 100644 index 00000000..ba7311da --- /dev/null +++ b/julia/CrystFEL/src/reflists.jl @@ -0,0 +1,411 @@ +module RefLists + +using Printf +import ..CrystFEL: libcrystfel +import ..CrystFEL.Symmetry: SymOpList, InternalSymOpList, symmetry_name +export RefList, loadreflist, savereflist! +export Reflection, UnmergedReflection, MergedReflection +export InternalRefList + + +# The internal libcrystfel structures, not exposed directly +# We only ever have e.g. a Ptr{InternalRefList}, never a real InternalRefList +mutable struct InternalRefList end +mutable struct InternalReflection end +mutable struct InternalRefListIterator end + +# The Julian exposed types +abstract type Reflection end + +mutable struct RefList{T<:Reflection} + internalptr::Ptr{InternalRefList} + symmetry::SymOpList +end + +mutable struct RefListIterator + lastrefl::Ptr{InternalReflection} + internalptr::Ptr{InternalRefListIterator} +end + +mutable struct MergedReflection <: Reflection + internalptr::Ptr{InternalReflection} +end + +mutable struct UnmergedReflection <: Reflection + internalptr::Ptr{InternalReflection} +end + + +function RefList{MergedReflection}(sym::SymOpList) + out = @ccall libcrystfel.reflist_new()::Ptr{InternalRefList} + if out == C_NULL + throw(ErrorException("Failed to create RefList")) + end + finalizer(RefList{MergedReflection}(out, sym)) do x + @ccall libcrystfel.reflist_free(x.internalptr::Ptr{InternalRefList})::Cvoid + end +end + + +function Base.iterate(reflist::RefList{T}) where T + + rli = Ref{Ptr{InternalRefListIterator}}(C_NULL) + refl = ccall((:first_refl, libcrystfel), + Ptr{InternalReflection}, (Ptr{InternalRefList},Ref{Ptr{InternalRefListIterator}}), + reflist.internalptr, rli) + + if refl == C_NULL + return nothing # no reflections! + end + + iter = RefListIterator(refl,rli[]) + finalizer(iter) do x + ccall((:free_reflistiterator, libcrystfel), + Cvoid, (Ptr{InternalRefListIterator},), x.internalptr) + end + + return T(refl),iter + +end + + +function Base.iterate(::RefList{T}, iter) where T + + refl = ccall((:next_refl, libcrystfel), + Ptr{InternalReflection}, (Ptr{InternalReflection},Ptr{InternalRefListIterator}), + iter.lastrefl, iter.internalptr) + + if refl == C_NULL + iter.internalptr = C_NULL # libcrystfel already freed it + return nothing + end + + iter.lastrefl = refl + + return T(refl),iter + +end + + +Base.IteratorEltype(::RefList{T}) where T = T +Base.isdone(iter::RefListIterator) = ((iter.internalptr == C_NULL) && (iter.lastrefl != C_NULL)) +Base.length(reflist::RefList) = ccall((:num_reflections, libcrystfel), + Cint, (Ptr{InternalRefList},), reflist.internalptr) + + +function Base.getindex(reflist::RefList{T}, indices) where T + + refl = @ccall libcrystfel.find_refl(reflist.internalptr::Ptr{InternalRefList}, + indices[1]::Cint, + indices[2]::Cint, + indices[3]::Cint)::Ptr{InternalReflection} + + if refl == C_NULL + return nothing + else + return T(refl) + end +end + + +function Base.get!(reflist::RefList{T}, indices) where T + + refl = @ccall libcrystfel.find_refl(reflist.internalptr::Ptr{InternalRefList}, + indices[1]::Cint, + indices[2]::Cint, + indices[3]::Cint)::Ptr{InternalReflection} + + if refl == C_NULL + return push!(reflist, indices) + else + return T(refl) + end +end + + +function Base.push!(reflist::RefList{T}, indices) where T + + refl = @ccall libcrystfel.add_refl(reflist.internalptr::Ptr{InternalRefList}, + indices[1]::Cint, + indices[2]::Cint, + indices[3]::Cint)::Ptr{InternalReflection} + + if refl == C_NULL + return nothing + else + return T(refl) + end +end + + +function loadreflist(filename::AbstractString) + + psym = Ref{Cstring}() + out = ccall((:read_reflections_2, libcrystfel), + Ptr{InternalRefList}, (Cstring,Ref{Cstring}), + filename, psym) + if out == C_NULL + throw(ArgumentError("Failed to load reflection list")) + end + + symmetryname = unsafe_string(psym[]) + return RefList{MergedReflection}(out, SymOpList(symmetryname)) + +end + + +function savereflist!(reflist::RefList{MergedReflection}, filename::AbstractString) + + r = @ccall libcrystfel.write_reflist_2(filename::Cstring, + reflist.internalptr::Ptr{InternalRefList}, + reflist.symmetry.internalptr::Ptr{InternalSymOpList})::Cint + + if r != 0 + throw(ErrorException("Failed to save reflection list")) + end + +end + + +function Base.show(io::IO, ::MIME"text/plain", reflist::RefList{MergedReflection}) + println(io, "Merged reflection list in point group ", symmetry_name(reflist.symmetry)) + print(io, " h k l intensity σ(intens) nmeas") + let n = 0 + for refl in Iterators.take(reflist, 11) + if n == 10 + # We have printed 10 already, and are here again. Truncate... + print(io, "\n ⋮ ⋮ ⋮ ⋮ ⋮ ⋮") + break + end + let ind = refl.indices + write(io, "\n") + @printf(io, "%4i %4i %4i %10.2f %10.2f %5i", ind[1], ind[2], ind[3], + refl.intensity, refl.sigintensity, refl.nmeasurements) + n += 1 + end + end + end +end + + +function Base.show(io::IO, ::MIME"text/plain", reflist::RefList{UnmergedReflection}) + println(io, "Unmerged reflection list in point group ", symmetry_name(reflist.symmetry)) + print(io, " h k l intensity σ(intens) fs ss panel") + let n = 0 + for refl in Iterators.take(reflist, 11) + if n == 10 + # We have printed 10 already, and are here again. Truncate... + print(io, "\n ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮") + break + end + let ind = refl.indices, + pos = refl.detectorposition + write(io, "\n") + @printf(io, "%4i %4i %4i %10.2f %10.2f %7.2f %7.2f %6i", + ind[1], ind[2], ind[3], refl.intensity, refl.sigintensity, + pos.fs, pos.ss, pos.panelnumber) + n += 1 + end + end + end +end + + +function detectorpos(refl::Reflection) + pfs = Ref{Cdouble}(0) + pss = Ref{Cdouble}(0) + pn = ccall((:get_panel_number, libcrystfel), + Cint, (Ptr{InternalReflection},), refl.internalptr) + ccall((:get_detector_pos, libcrystfel), + Cint, (Ptr{InternalReflection},Ref{Cdouble},Ref{Cdouble}), + refl.internalptr, pfs, pss) + (fs=pfs[], ss=pss[], panelnumber=pn) +end + + +function indices(refl::Reflection) + h = Ref{Cint}(0) + k = Ref{Cint}(0) + l = Ref{Cint}(0) + ccall((:get_indices, libcrystfel), + Cint, (Ptr{InternalReflection},Ref{Cint},Ref{Cint},Ref{Cint}), + refl.internalptr, h, k, l) + [h[], k[], l[]] +end + + +function symmetricindices(refl::Reflection) + h = Ref{Cint}(0) + k = Ref{Cint}(0) + l = Ref{Cint}(0) + ccall((:get_symmetric_indices, libcrystfel), + Cint, (Ptr{InternalReflection},Ref{Cint},Ref{Cint},Ref{Cint}), + refl.internalptr, h, k, l) + [h[], k[], l[]] +end + + +function Base.getproperty(refl::Reflection, name::Symbol) + if name === :intensity + ccall((:get_intensity, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :sigintensity + ccall((:get_esd_intensity, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :partiality + ccall((:get_partiality, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :khalf + ccall((:get_khalf, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :kpred + ccall((:get_kpred, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :excitationerror + ccall((:get_exerr, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :lorentzfactor + ccall((:get_lorentz, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :phase + ccall((:get_phase, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :peak + ccall((:get_peak, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :meanbackground + ccall((:get_mean_bg, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :temp1 + ccall((:get_temp1, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :temp2 + ccall((:get_temp2, libcrystfel), + Cdouble, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :nmeasurements + ccall((:get_redundancy, libcrystfel), + Cint, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :flag + ccall((:get_flag, libcrystfel), + Cint, (Ptr{InternalReflection},), refl.internalptr) + elseif name === :detectorposition + detectorpos(refl) + elseif name === :indices + indices(refl) + elseif name === :symmetricindices + symmetricindices(refl) + else + getfield(refl, name) + end +end + + +function Base.setproperty!(refl::Reflection, name::Symbol, val) + if name === :intensity + @ccall libcrystfel.set_intensity(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :sigintensity + @ccall libcrystfel.set_esd_intensity(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :partiality + @ccall libcrystfel.set_partiality(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :khalf + @ccall libcrystfel.set_khalf(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :kpred + @ccall libcrystfel.set_kpred(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :excitationerror + @ccall libcrystfel.set_exerr(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :lorentzfactor + @ccall libcrystfel.set_lorentz(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :phase + @ccall libcrystfel.set_phase(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :peak + @ccall libcrystfel.set_peak(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :meanbackground + @ccall libcrystfel.set_mean_bg(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :temp1 + @ccall libcrystfel.set_temp1(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :temp2 + @ccall libcrystfel.set_temp2(refl.internalptr::Ptr{InternalReflection}, + val::Cdouble)::Cvoid + elseif name === :nmeasurements + @ccall libcrystfel.set_redundancy(refl.internalptr::Ptr{InternalReflection}, + val::Cint)::Cvoid + elseif name === :flag + @ccall libcrystfel.set_flag(refl.internalptr::Ptr{InternalReflection}, + val::Cint)::Cvoid + elseif name === :detectorposition + @ccall libcrystfel.set_detector_pos(refl.internalptr::Ptr{InternalReflection}, + val.fs::Cdouble, val.ss::Cdouble)::Cvoid + @ccall libcrystfel.set_panel_number(refl.internalptr::Ptr{InternalReflection}, + val.panelnumber::Cint)::Cvoid + elseif name === :indices + throw(ErrorException("Cannot set the indices of a Reflection")) + elseif name === :symmetricindices + @ccall libcrystfel.set_symmetric_indices(refl.internalptr::Ptr{InternalReflection}, + val[1]::Cint, val[2]::Cint, val[3]::Cint)::Cvoid + else + setfield!(refl, name, val) + end +end + + +function Base.propertynames(::UnmergedReflection; private=false) + names = (:intensity,:sigintensity,:partiality,:khalf,:kpred,:lorentzfactor, + :excitationerror,:phase,:peak,:meanbackground,:temp1,:temp2, + :nmeasurements,:flag,:detectorposition,:indices,:symmetricindices) + if private + tuple(names..., :internalptr) + else + names + end +end + + +function Base.propertynames(::MergedReflection; private=false) + names = (:intensity,:sigintensity,:phase,:temp1,:temp2, + :nmeasurements,:flag,:indices,:symmetricindices) + if private + tuple(names..., :internalptr) + else + names + end +end + + +function Base.show(io::IO, refl::MergedReflection) + write(io, "MergedReflection(") + show(io, refl.indices) + write(io, ", intensity=") + show(io, refl.intensity) + write(io, ", σ(intensity)=") + show(io, refl.sigintensity) + write(io, ", nmeasurements=") + show(io, refl.nmeasurements) + write(io, ")") +end + + +function Base.show(io::IO, refl::UnmergedReflection) + write(io, "UnmergedReflection(") + show(io, refl.indices) + write(io, " at "); + show(io, refl.detectorposition) + write(io, ", intensity=") + show(io, refl.intensity) + write(io, ", σ(intensity)=") + show(io, refl.sigintensity) + write(io, ")") +end + + +end # of module diff --git a/julia/CrystFEL/src/streams.jl b/julia/CrystFEL/src/streams.jl new file mode 100644 index 00000000..ce75c906 --- /dev/null +++ b/julia/CrystFEL/src/streams.jl @@ -0,0 +1,138 @@ +module Streams + +import ..CrystFEL: libcrystfel +import ..CrystFEL.DataTemplates: DataTemplate, InternalDataTemplate +import ..CrystFEL.Images: Image, InternalImage +export Stream, chunkwrite, chunkread, allcrystals + +# Represents the real C-side (opaque) structure. +mutable struct InternalStream end + +# The Julia-side structure, needed to house the pointer to the C structure +mutable struct Stream + internalptr::Ptr{InternalStream} +end + + +""" + Stream(filename, "w", dtempl) + +Opens a CrystFEL stream for writing. Note that you must provide a `DataTemplate`, +which is needed to translate "panel coordinates" to "file coordinates". + +Corresponds to CrystFEL C API routine `stream_open_for_write`. +""" +function Stream(filename, mode::AbstractString, dtempl::DataTemplate) + + if mode == "w" + out = @ccall libcrystfel.stream_open_for_write(filename::Cstring, + dtempl.internalptr::Ptr{InternalDataTemplate})::Ptr{InternalStream} + if out == C_NULL + throw(ErrorException("Failed to open stream for reading")) + end + + @ccall libcrystfel.stream_write_data_template(out::Ptr{InternalStream}, + dtempl.internalptr::Ptr{InternalDataTemplate})::Cvoid + + finalizer(close, Stream(out)) + + elseif mode =="r" + throw(ArgumentError("To open a stream for reading, don't provide the DataTemplate")) + + else + throw(ArgumentError("Unrecognised CrystFEL stream mode"*mode)) + end +end + + +""" + Stream(filename, "r") + +Opens a CrystFEL stream for reading. + +Close the stream with `close` when you've finished (this will happen +automatically when the `Stream` object is finalized). + +Corresponds to CrystFEL C API routine `stream_open_for_read`. +""" +function Stream(filename, mode::AbstractString) + + if mode == "r" + out = @ccall libcrystfel.stream_open_for_read(filename::Cstring)::Ptr{InternalStream} + if out == C_NULL + throw(ErrorException("Failed to open stream for reading")) + end + finalizer(close, Stream(out)) + + elseif mode == "w" + throw(ArgumentError("To open a stream for writing, you must provide " + *"a DataTemplate: use Stream(filename, \"w\", dtempl)")) + + else + throw(ArgumentError("Unrecognised CrystFEL stream mode"*mode)) + end +end + + +function Base.close(st::Stream) + if st.internalptr != C_NULL + @ccall libcrystfel.stream_close(st.internalptr::Ptr{InternalStream})::Cvoid + st.internalptr = C_NULL + end +end + + +function streamflags(peaks, reflections, imagedata) + flags = 0 + if reflections + flags |= 2 + end + if peaks + flags |= 4 + end + if imagedata + flags |= 8 + end + return flags +end + + +function chunkwrite(st::Stream, image::Image; peaks=true, reflections=true) + st.internalptr == C_NULL && throw(ErrorException("Stream is closed")) + flags = streamflags(peaks, reflections, false) + @ccall libcrystfel.stream_write_chunk(st.internalptr::Ptr{InternalStream}, + image.internalptr::Ptr{InternalImage}, + flags::Cint)::Cvoid +end + + +function chunkread(st::Stream; peaks=true, reflections=true, datageom=true) + + st.internalptr == C_NULL && throw(ErrorException("Stream is closed")) + + flags = streamflags(peaks, reflections, datageom) + out = @ccall libcrystfel.stream_read_chunk(st.internalptr::Ptr{InternalStream}, + flags::Cint)::Ptr{InternalImage} + out == C_NULL && return nothing + + finalizer(Image(out, nothing, [], [])) do x + ccall((:image_free, libcrystfel), Cvoid, (Ptr{InternalImage},), x.internalptr) + end + +end + + +function allcrystals(st) + Channel() do ch + while true + image = chunkread(st, peaks=false, reflections=true, datageom=false) + image === nothing && break + for cr in image.crystals + put!(ch, (cr.crystal, cr.reflections)) + end + end + end +end + + +end # of module diff --git a/julia/CrystFEL/src/symmetry.jl b/julia/CrystFEL/src/symmetry.jl new file mode 100644 index 00000000..4b4caa4d --- /dev/null +++ b/julia/CrystFEL/src/symmetry.jl @@ -0,0 +1,134 @@ +module Symmetry + +import ..CrystFEL: libcrystfel +export SymOpList, InternalSymOpList, InternalIntegerMatrix +export symmetry_name, asymmetricindices + + +# Types for pointers returned by libcrystfel +mutable struct InternalSymOpList end +mutable struct InternalIntegerMatrix end + + +# Exposed types +mutable struct SymOpList + internalptr::Ptr{InternalSymOpList} +end + + +mutable struct SymOp + internalptr::Ptr{InternalIntegerMatrix} +end + + +function SymOpList(pointgroup::AbstractString) + + out = ccall((:get_pointgroup, libcrystfel), + Ptr{InternalSymOpList}, (Cstring,), pointgroup) + if out == C_NULL + throw(OutOfMemoryError()) + end + + sym = SymOpList(out) + + finalizer(sym) do x + ccall((:free_symoplist, libcrystfel), + Cvoid, (Ptr{InternalSymOpList},), x.internalptr) + end + + return sym + +end + + +function Base.getindex(sym::SymOpList, i::Int) + + if (i<1) || (i > length(sym)) + throw(BoundsError()) + end + + out = ccall((:get_symop, libcrystfel), + Ptr{InternalIntegerMatrix}, + (Ptr{InternalSymOpList},Ptr{Cvoid},Cint), + sym.internalptr,C_NULL,i-1) + + if out == C_NULL + throw(OutOfMemoryError()) + end + + return SymOp(out) + +end + + +function Base.length(sym::SymOpList) + return ccall((:num_equivs, libcrystfel), + Cint, (Ptr{InternalSymOpList},Ptr{Cvoid}), + sym.internalptr, C_NULL) +end + + +function hkl_op(op::SymOp) + s = ccall((:name_equiv, libcrystfel), + Cstring, + (Ptr{InternalIntegerMatrix},), + op.internalptr) + return unsafe_string(s) +end + + +function symmetry_name(sym::SymOpList) + s = ccall((:symmetry_name, libcrystfel), + Cstring, + (Ptr{InternalSymOpList},), + sym.internalptr) + return unsafe_string(s) +end + + +function Base.iterate(sym::SymOpList) + return (sym[1], 2) +end + + +function Base.iterate(sym::SymOpList, i) + if i > length(sym) + return nothing + else + return (sym[i], i+1) + end +end + + +function asymmetricindices(sym::SymOpList, indices) + ho = Ref{Cint}(0) + ko = Ref{Cint}(0) + lo = Ref{Cint}(0) + @ccall libcrystfel.get_asymm(sym.internalptr::Ptr{InternalSymOpList}, + indices[1]::Cint, indices[2]::Cint, indices[3]::Cint, + ho::Ref{Cint}, ko::Ref{Cint}, lo::Ref{Cint})::Cvoid + (ho[], ko[], lo[]) +end + + +function Base.show(io::IO, ::MIME"text/plain", sym::SymOpList) + print(io, length(sym), "-element SymOpList (\"", symmetry_name(sym), "\")") + for op in sym + print(io, "\n", hkl_op(op)) + end +end + + +function Base.show(io::IO, sym::SymOpList) + write(io, "SymOpList(\"", symmetry_name(sym), "\")") +end + + +function Base.show(io::IO, op::SymOp) + write(io, "SymOp(") + write(io, "\"") + write(io, hkl_op(op)) + write(io, "\")") +end + +end # of module |