Skip to content
This repository was archived by the owner on May 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/diffcalc_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""API to expose diffcalc-core methods."""

from . import config, database, openapi, server
from . import config, database, openapi, server, types
from ._version_git import __version__

__all__ = ["__version__", "server", "config", "database", "openapi"]
__all__ = ["__version__", "server", "config", "database", "types", "openapi"]
29 changes: 29 additions & 0 deletions src/diffcalc_api/models/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pydantic import BaseModel

from diffcalc_api.models.ub import HklModel, MiscutModel, SphericalCoordinates, XyzModel
from diffcalc_api.types import Orientation, Reflection


class InfoResponse(BaseModel):
Expand Down Expand Up @@ -80,3 +81,31 @@ class MiscutResponse(BaseModel):
"""

payload: MiscutModel


class ReflectionResponse(BaseModel):
"""Response for any operation returning a reflection."""

payload: Reflection

class Config:
"""Necessary config to make validation easier.

As this is a response model, there is no need to enforce validation.
"""

orm_mode = True


class OrientationResponse(BaseModel):
"""Response for any operation returning an orientation."""

payload: Orientation

class Config:
"""Necessary config to make validation easier.

As this is a response model, there is no need to enforce validation.
"""

orm_mode = True
89 changes: 89 additions & 0 deletions src/diffcalc_api/routes/ub.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
ArrayResponse,
InfoResponse,
MiscutResponse,
OrientationResponse,
ReciprocalSpaceResponse,
ReflectionResponse,
SphericalResponse,
StringResponse,
)
Expand All @@ -33,6 +35,7 @@
)
from diffcalc_api.services import ub as service
from diffcalc_api.stores.protocol import HklCalcStore, get_store
from diffcalc_api.types import Orientation, Position, Reflection

router = APIRouter(prefix="/ub", tags=["ub"])

Expand Down Expand Up @@ -62,6 +65,48 @@ async def get_ub_status(
#######################################################################################


@router.get("/{name}/reflection", response_model=ReflectionResponse)
async def get_reflection(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
tag: Optional[str] = Query(default=None, example="refl1"),
idx: Optional[int] = Query(default=None),
):
"""Get a reflection from the UBCalculation object.

Both tag and idx cannot be provided. One or the other must be provided.

Args:
name: the name of the hkl object to access within the store
store: accessor to the hkl object
collection: collection within which the hkl object resides
tag: optional tag to access the reflection
idx: optional index to access the reflection

Returns:
ReflectionResponse
payload containing the reflection.
"""
if (tag is None) and (idx is None):
raise NoTagOrIdxProvidedError()

if (tag is not None) and (idx is not None):
raise BothTagAndIdxProvidedError()

ref = await service.get_reflection(name, store, collection, tag, idx)

reflection = Reflection(
ref.h,
ref.k,
ref.l,
Position(**ref.pos.asdict),
ref.energy,
ref.tag,
)
return ReflectionResponse(payload=reflection)


@router.post("/{name}/reflection", response_model=InfoResponse)
async def add_reflection(
name: str,
Expand Down Expand Up @@ -163,6 +208,50 @@ async def delete_reflection(
#######################################################################################


@router.get("/{name}/orientation", response_model=OrientationResponse)
async def get_orientation(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
tag: Optional[str] = Query(default=None, example="refl1"),
idx: Optional[int] = Query(default=None),
):
"""Get an orientation from the UBCalculation object.

Both tag and idx cannot be provided. One or the other must be provided.

Args:
name: the name of the hkl object to access within the store
store: accessor to the hkl object
collection: collection within which the hkl object resides
tag: optional tag to access the orientation
idx: optional index to access the orientation

Returns:
OrientationResponse
payload containing the orientation.
"""
if (tag is None) and (idx is None):
raise NoTagOrIdxProvidedError()

if (tag is not None) and (idx is not None):
raise BothTagAndIdxProvidedError()

orient = await service.get_orientation(name, store, collection, tag, idx)

orientation = Orientation(
orient.h,
orient.k,
orient.l,
orient.x,
orient.y,
orient.z,
Position(**orient.pos.asdict),
orient.tag if orient.tag is not None else "",
)
return OrientationResponse(payload=orientation)


@router.post("/{name}/orientation", response_model=InfoResponse)
async def add_orientation(
name: str,
Expand Down
81 changes: 80 additions & 1 deletion src/diffcalc_api/services/ub.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import numpy as np
from diffcalc.hkl.geometry import Position
from diffcalc.ub.calc import UBCalculation
from diffcalc.ub.reference import Orientation, Reflection

from diffcalc_api.errors.ub import (
InvalidIndexError,
Expand Down Expand Up @@ -48,6 +49,45 @@ async def get_ub_status(
#######################################################################################


async def get_reflection(
name: str,
store: HklCalcStore,
collection: Optional[str],
tag: Optional[str],
idx: Optional[int],
) -> Reflection:
"""Get a reflection from the UBCalculation object.

Both tag and idx cannot be provided. One or the other must be provided.
This is enforced in the route for this function, see diffcalc_api/routes/ub.py.
An error is thrown if the reflection does not exist, i.e. cannot be retrieved.

Args:
name: the name of the hkl object to access within the store
store: accessor to the hkl object
collection: collection within which the hkl object resides
tag: optional tag to access the reflection
idx: optional index to access the reflection

Returns:
Reflection
A reflection object, as defined in diffcalc_api.types.
"""
hklcalc = await store.load(name, collection)
ubcalc: UBCalculation = hklcalc.ubcalc

retrieve: Union[int, str] = (
tag if tag is not None else (idx if idx is not None else 0)
)

try:
reflection = ubcalc.get_reflection(retrieve)
except (IndexError, ValueError):
raise ReferenceRetrievalError(retrieve, "reflection")

return reflection


async def add_reflection(
name: str,
params: AddReflectionParams,
Expand Down Expand Up @@ -167,6 +207,45 @@ async def delete_reflection(
#######################################################################################


async def get_orientation(
name: str,
store: HklCalcStore,
collection: Optional[str],
tag: Optional[str],
idx: Optional[int],
) -> Orientation:
"""Get an orientation from the UBCalculation object.

Both tag and idx cannot be provided. One or the other must be provided.
This is enforced in the route for this function, see diffcalc_api/routes/ub.py.
An error is thrown if the orientation does not exist, i.e. cannot be retrieved.

Args:
name: the name of the hkl object to access within the store
store: accessor to the hkl object
collection: collection within which the hkl object resides
tag: optional tag to access the orientation
idx: optional index to access the orientation

Returns:
Orientation
An orientation object, as defined in diffcalc_api.types.
"""
hklcalc = await store.load(name, collection)
ubcalc: UBCalculation = hklcalc.ubcalc

retrieve: Union[int, str] = (
tag if tag is not None else (idx if idx is not None else 0)
)

try:
orientation = ubcalc.get_orientation(retrieve)
except (IndexError, ValueError):
raise ReferenceRetrievalError(retrieve, "orientation")

return orientation


async def add_orientation(
name: str,
params: AddOrientationParams,
Expand Down Expand Up @@ -226,7 +305,7 @@ async def edit_orientation(
try:
orientation = hklcalc.ubcalc.get_orientation(retrieve)
except (IndexError, ValueError):
raise ReferenceRetrievalError(retrieve, "reflection")
raise ReferenceRetrievalError(retrieve, "orientation")

inputs = {
"idx": retrieve,
Expand Down
51 changes: 51 additions & 0 deletions src/diffcalc_api/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Exposes types of diffcalc-core objects."""

from dataclasses import dataclass
from typing import Optional


@dataclass
class Position:
"""Diffractometer angles in degrees.

Similar to diffcalc.hkl.geometry.Position
"""

mu: float
delta: float
nu: float
eta: float
chi: float
phi: float


@dataclass
class Orientation:
"""Reference orientation of the sample.

Similar to diffcalc.ub.reference.Orientation
"""

h: float
k: float
l: float
x: float
y: float
z: float
pos: Position
tag: Optional[str]


@dataclass
class Reflection:
"""Reference reflection of the sample.

Similar to diffcalc.ub.reference.Reflection
"""

h: float
k: float
l: float
pos: Position
energy: float
tag: Optional[str]
Loading