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 4 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
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dev =
mock
pydocstyle
mongomock
httpx

[options.packages.find]
where = src
Expand Down
12 changes: 8 additions & 4 deletions src/diffcalc_api/errors/ub.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,15 @@ def __init__(self):
class NoUbMatrixError(DiffcalcAPIException):
"""When there is no U/UB matrix, some commands in diffcalc-core fail."""

def __init__(self):
def __init__(self, message: Optional[str] = None):
Comment thread
rosesyrett marked this conversation as resolved.
"""Set detail and status code."""
self.detail = (
"It seems like there is no UB matrix for this record. Please "
+ "try again after setting the UB matrix, either by calculating the UB from"
+ " existing reflections/orientations or setting it explicitly."
(
"It seems like there is no UB matrix for this record. Please "
+ "try again after setting the UB matrix, either by calculating the UB"
+ " from existing reflections/orientations or setting it explicitly."
)
if message is None
else message
)
self.status_code = ErrorCodes.NO_UB_MATRIX_ERROR
296 changes: 272 additions & 24 deletions src/diffcalc_api/routes/ub.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
"""Endpoints relating to the management of setting up the UB calculation."""

from typing import List, Optional
from typing import List, Optional, Union

from fastapi import APIRouter, Body, Depends, Query

from diffcalc_api.config import VECTOR_PROPERTIES
from diffcalc_api.errors.ub import (
# from diffcalc_api.config import VECTOR_PROPERTIES
from diffcalc_api.errors.ub import ( # InvalidPropertyError,
Comment thread
rosesyrett marked this conversation as resolved.
Outdated
BothTagAndIdxProvidedError,
InvalidPropertyError,
InvalidSetLatticeParamsError,
NoTagOrIdxProvidedError,
)
Expand Down Expand Up @@ -36,8 +35,8 @@
router = APIRouter(prefix="/ub", tags=["ub"])


@router.get("/{name}", response_model=StringResponse)
async def get_ub(
@router.get("/{name}/status", response_model=StringResponse)
async def get_ub_status(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
Expand All @@ -52,10 +51,15 @@ async def get_ub(
Returns:
a string with the current state of the UB object
"""
content = await service.get_ub(name, store, collection)
content = await service.get_ub_status(name, store, collection)
return StringResponse(payload=content)


#######################################################################################
# Reflections #
#######################################################################################


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


#######################################################################################
# Orientations #
#######################################################################################


@router.post("/{name}/orientation", response_model=InfoResponse)
async def add_orientation(
name: str,
Expand Down Expand Up @@ -247,6 +256,11 @@ async def delete_orientation(
)


#######################################################################################
# Crystal #
#######################################################################################


@router.patch("/{name}/lattice", response_model=InfoResponse)
async def set_lattice(
name: str,
Expand Down Expand Up @@ -277,6 +291,11 @@ async def set_lattice(
)


#######################################################################################
# Miscuts #
#######################################################################################


@router.put("/{name}/miscut", response_model=InfoResponse)
async def set_miscut(
name: str,
Expand Down Expand Up @@ -357,7 +376,12 @@ async def get_miscut_from_hkl(
)


@router.get("/{name}/ub", response_model=ArrayResponse)
#######################################################################################
# U/UB Matrices #
#######################################################################################


@router.get("/{name}/calculate", response_model=ArrayResponse)
async def calculate_ub(
name: str,
tag1: Optional[str] = Query(default=None, example="refl1"),
Expand Down Expand Up @@ -439,32 +463,256 @@ async def set_u(
)


@router.put("/{name}/{property}", response_model=InfoResponse)
async def modify_property(
@router.get("/{name}/ub", response_model=ArrayResponse)
async def get_ub(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Get the UB matrix.

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.

Returns:
ArrayResponse object with the UB matrix as an array.

"""
content = await service.get_ub(name, store, collection)
return ArrayResponse(payload=content)


@router.get("/{name}/u", response_model=ArrayResponse)
async def get_u(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Get the U matrix.

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.

Returns:
ArrayResponse object with the U matrix as an array.

"""
content = await service.get_u(name, store, collection)
return ArrayResponse(payload=content)


#######################################################################################
# Surface and Reference Vectors #
#######################################################################################


@router.put("/{name}/nphi", response_model=InfoResponse)
async def set_lab_reference_vector(
name: str,
target_value: XyzModel = Body(..., example={"x": 1, "y": 0, "z": 0}),
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Set the lab reference vector n_phi.

Args:
name: the name of the hkl object to access within the store
target_value: the vector positon in real space
store: accessor to the hkl object.
collection: collection within which the hkl object resides.

Returns:
InfoResponse describing the vector has been set successfully.

"""
await service.set_lab_reference_vector(name, target_value, store, collection)
return InfoResponse(
message=f"Reference vector set for crystal {name} of collection {collection}"
)


@router.put("/{name}/nhkl")
async def set_miller_reference_vector(
name: str,
property: str,
target_value: HklModel = Body(..., example={"h": 1, "k": 0, "l": 0}),
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Set a property of the UB object in a given hkl object.
"""Set the reference vector n_hkl.

Args:
name: the name of the hkl object to access within the store
property: the property of the UB object to set
target_value: the miller indices to set them to
store: accessor to the hkl object
collection: collection within which the hkl object resides
target_value: the vector positon in reciprocal space
store: accessor to the hkl object.
collection: collection within which the hkl object resides.

Returns:
InfoResponse describing the vector has been set successfully.

"""
await service.set_miller_reference_vector(name, target_value, store, collection)
return InfoResponse(
message=f"Reference vector set for crystal {name} of collection {collection}"
)


@router.put("/{name}/surface/nphi")
async def set_lab_surface_normal(
name: str,
target_value: XyzModel = Body(..., example={"x": 1, "y": 0, "z": 0}),
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Set the reference vector surf_nphi.

Args:
name: the name of the hkl object to access within the store
target_value: the vector positon in real space
store: accessor to the hkl object.
collection: collection within which the hkl object resides.

Returns:
InfoResponse describing the vector has been set successfully.

The property to be set must be a valid vector property.
"""
if property not in VECTOR_PROPERTIES:
raise InvalidPropertyError()
await service.set_lab_surface_normal(name, target_value, store, collection)
return InfoResponse(
message=f"Surface normal set for crystal {name} of collection {collection}"
)


@router.put("/{name}/surface/nhkl")
async def set_miller_surface_normal(
name: str,
target_value: HklModel = Body(..., example={"h": 1, "k": 0, "l": 0}),
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Set the reference vector surf_nhkl.

Args:
name: the name of the hkl object to access within the store
target_value: the vector positon in reciprocal space
store: accessor to the hkl object.
collection: collection within which the hkl object resides.

await service.modify_property(name, property, target_value, store, collection)
Returns:
InfoResponse describing the vector has been set successfully.

"""
await service.set_miller_surface_normal(name, target_value, store, collection)
return InfoResponse(
message=(
f"{property} has been set for UB calculation of crystal {name} in "
+ f"collection {collection}"
)
message=f"Surface normal set for crystal {name} of collection {collection}"
)


@router.get("/{name}/nphi", response_model=Union[ArrayResponse, InfoResponse])
async def get_lab_reference_vector(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Get the reference vector nphi.

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.

Returns:
ArrayResponse with the vector, or
InfoResponse if it doesn't exist

"""
lab_vector: Optional[List[List[float]]] = await service.get_lab_reference_vector(
name, store, collection
)
if lab_vector is not None:
return ArrayResponse(payload=lab_vector)
else:
return InfoResponse(message="This vector does not exist.")


@router.get("/{name}/nhkl")
async def get_miller_reference_vector(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Get the reference vector nhkl.

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.

Returns:
ArrayResponse with the vector, or
InfoResponse if it doesn't exist

"""
lab_vector: Optional[List[List[float]]] = await service.get_miller_reference_vector(
name, store, collection
)
if lab_vector is not None:
return ArrayResponse(payload=lab_vector)
else:
return InfoResponse(message="This vector does not exist.")


@router.get("/{name}/surface/nphi")
async def get_lab_surface_normal(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Get the surface normal surf_nphi.

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.

Returns:
ArrayResponse with the vector, or
InfoResponse if it doesn't exist

"""
lab_vector: Optional[List[List[float]]] = await service.get_lab_surface_normal(
name, store, collection
)
if lab_vector is not None:
return ArrayResponse(payload=lab_vector)
else:
return InfoResponse(message="This vector does not exist.")


@router.get("/{name}/surface/nhkl")
async def get_miller_surface_normal(
name: str,
store: HklCalcStore = Depends(get_store),
collection: Optional[str] = Query(default=None, example="B07"),
):
"""Get the surface normal surf_nhkl.

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.

Returns:
ArrayResponse with the vector, or
InfoResponse if it doesn't exist

"""
lab_vector: Optional[List[List[float]]] = await service.get_miller_surface_normal(
name, store, collection
)
if lab_vector is not None:
return ArrayResponse(payload=lab_vector)
else:
return InfoResponse(message="This vector does not exist.")
Loading