writer: _model.cif from rugnux live keeps the input model's metadata, as the served model.cif does

The placed-model END block lost everything but the atoms, so the writer's _model.cif had no sequence
references, authors, software or secondary structure, and gemmi relabelled the subchains. The block now
also carries label_asym_id, label_entity_id and label_seq_id per atom and the input model's metadata as the
mmCIF categories gemmi writes without coordinates (metadata_mmcif), which the writer parses with gemmi and
puts back. The writer's _model.cif is now the same text as the one rugnux writes itself and as
/rugnux/model.cif serves. Older blocks without the new fields are read as before.

Test: a predicted myoglobin model through END -> CBOR -> writer gives byte-identical _model.cif/_model.pdb
to WritePlacedModel and to PlacedModelMmcif, with its _struct_ref, _entity_poly_seq, _audit_author,
_software and _struct_conf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
(cherry picked from commit 29d752e6a107e3b8bc71ad6e9af0d81064966537)
This commit is contained in:
leonarski_f committed 2026-10-22 20:18:51 +02:00
1 parent a105c67f62
commit 90bbba33bc
13 files changed
+117 -18

No files matched your search

+3 -3
View File
@@ -3615,9 +3615,9 @@ paths:
get:
summary: Get the model as placed by the last rugnux live run
description: |
The model placed against the data by molecular replacement or validation, as mmCIF: the coordinates, cell
and space group of the `_model.cif` file written beside the data, with the input model's own metadata
(entities, citation) kept. Kept as long as `/rugnux/status`.
The model placed against the data by molecular replacement or validation, as mmCIF - the same text as the
`_model.cif` file written beside the data: the data's cell and space group, with the input model's own
metadata (entities, sequences, references, authors, software) kept. Kept as long as `/rugnux/status`.
security:
- bearerAuth: []
responses:
+4 -4
View File
@@ -864,9 +864,9 @@ and validation of every following rugnux live collection, until it is removed wi
</div></div><div class="sc-jaXxmE eqYzvz"><div class="sc-iMTnTL fkPTul"><h5 class="sc-dAlyuH sc-hwdzOV jbQuod JjoyL">Authorizations:</h5><svg class="sc-cwHptR iZRiKW" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg></div><div class="sc-krNlru gkwgxc"><span class="sc-jMakVo bPNrjo"><span class="sc-ihgnxF jlNGfN"><i>bearerAuth</i></span></span></div></div><div><h3 class="sc-fMMURN egoxtT">Responses</h3><div><button class="sc-kzqdkY jgSwKD"><svg class="sc-cwHptR hWEnWx" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg><strong class="sc-dSIIpw bpEYcE">200<!-- --> </strong><div class="sc-eeDRCY sc-eBMEME sc-dCFHLb gTGgei kekOtA kCaLQd"><p>Everything OK</p>
</div></button></div><div><button class="sc-kzqdkY bXzzUN"><svg class="sc-cwHptR cSkZlF" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg><strong class="sc-dSIIpw bpEYcE">401<!-- --> </strong><div class="sc-eeDRCY sc-eBMEME sc-dCFHLb gTGgei kekOtA kCaLQd"><p>The current dataset is protected and no valid bearer token was presented.</p>
</div></button></div></div></div><div class="sc-iGgWBj sc-gsFSXq ibAZSi eDeKjd"><div class="sc-ejfMa-d bNUPQg"><button class="sc-EgOXT cFEcOl"><span type="get" class="sc-eZYNyq qGfV http-verb get">get</span><span class="sc-iEXKAA gUNSDu">/rugnux/status</span><svg class="sc-cwHptR iVFMLx" style="margin-right:-25px" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg></button><div aria-hidden="true" class="sc-dlWCHZ kWacYr"><div class="sc-hHOBiw cndTNO"><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>Test Jungfraujoch system</p>
</div><div tabindex="0" role="button"><div class="sc-kWtpeL gEncxT"><span>http://localhost:5232</span>/rugnux/status</div></div></div></div></div><div><h3 class="sc-kpDqfm eBBgSB"> <!-- -->Response samples<!-- --> </h3><div class="sc-bXCLTC dHowgL" data-rttabs="true"><ul class="react-tabs__tab-list" role="tablist"><li class="tab-success react-tabs__tab--selected" role="tab" id="tab_R_jiia_0" aria-selected="true" aria-disabled="false" aria-controls="panel_R_jiia_0" tabindex="0" data-rttab="true">200</li></ul><div class="react-tabs__tab-panel react-tabs__tab-panel--selected" role="tabpanel" id="panel_R_jiia_0" aria-labelledby="tab_R_jiia_0"><div><div class="sc-bbSZdi gCVTxk"><span class="sc-fjvvzt jWReSk">Content type</span><div class="sc-JrDLc iTKzPJ">application/json</div></div><div class="sc-uVWWZ gQROPU"><div class="sc-esYiGF dWQNJI"><div class="sc-koXPp efufqz"><button><div class="sc-fhzFiK kwZDms">Copy</div></button><button> Expand all </button><button> Collapse all </button></div><div tabindex="0" class="sc-eeDRCY gTGgei sc-fXSgeo bnrBiz"><div class="redoc-json"><code><button class="collapser" aria-label="collapse"></button><span class="token punctuation">{</span><span class="ellipsis"></span><ul class="obj collapsible"><li><div class="hoverable "><span class="property token string">"state"</span>: <span class="token string">&quot;Idle&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"placed_model"</span>: <span class="token boolean">true</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"probe"</span>: <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"probe_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"start_to_first_image_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"message"</span>: <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"images_expected"</span>: <span class="token number">0</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"expected_collection_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"images_received"</span>: <span class="token number">0</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"elapsed_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"step_progress"</span>: <span class="token number">1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"steps"</span>: <button class="collapser" aria-label="collapse"></button><span class="token punctuation">[</span><span class="ellipsis"></span><ul class="array collapsible"><li><div class="hoverable collapsed"><button class="collapser" aria-label="expand"></button><span class="token punctuation">{</span><span class="ellipsis"></span><ul class="obj collapsible"><li><div class="hoverable collapsed"><span class="property token string">"title"</span>: <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable collapsed"><span class="property token string">"started_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable collapsed"><span class="property token string">"duration_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable collapsed"><span class="property token string">"notes"</span>: <button class="collapser" aria-label="expand"></button><span class="token punctuation">[</span><span class="ellipsis"></span><ul class="array collapsible"><li><div class="hoverable collapsed"><span class="token string">&quot;string&quot;</span></div></li></ul><span class="token punctuation">]</span></div></li></ul><span class="token punctuation">}</span></div></li></ul><span class="token punctuation">]</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"last_image_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"cut"</span>: <spaLine truncated
and space group of the <code>_model.cif</code> file written beside the data, with the input model&#39;s own metadata
(entities, citation) kept. Kept as long as <code>/rugnux/status</code>.</p>
</div><div tabindex="0" role="button"><div class="sc-kWtpeL gEncxT"><span>http://localhost:5232</span>/rugnux/status</div></div></div></div></div><div><h3 class="sc-kpDqfm eBBgSB"> <!-- -->Response samples<!-- --> </h3><div class="sc-bXCLTC dHowgL" data-rttabs="true"><ul class="react-tabs__tab-list" role="tablist"><li class="tab-success react-tabs__tab--selected" role="tab" id="tab_R_jiia_0" aria-selected="true" aria-disabled="false" aria-controls="panel_R_jiia_0" tabindex="0" data-rttab="true">200</li></ul><div class="react-tabs__tab-panel react-tabs__tab-panel--selected" role="tabpanel" id="panel_R_jiia_0" aria-labelledby="tab_R_jiia_0"><div><div class="sc-bbSZdi gCVTxk"><span class="sc-fjvvzt jWReSk">Content type</span><div class="sc-JrDLc iTKzPJ">application/json</div></div><div class="sc-uVWWZ gQROPU"><div class="sc-esYiGF dWQNJI"><div class="sc-koXPp efufqz"><button><div class="sc-fhzFiK kwZDms">Copy</div></button><button> Expand all </button><button> Collapse all </button></div><div tabindex="0" class="sc-eeDRCY gTGgei sc-fXSgeo bnrBiz"><div class="redoc-json"><code><button class="collapser" aria-label="collapse"></button><span class="token punctuation">{</span><span class="ellipsis"></span><ul class="obj collapsible"><li><div class="hoverable "><span class="property token string">"state"</span>: <span class="token string">&quot;Idle&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"placed_model"</span>: <span class="token boolean">true</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"probe"</span>: <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"probe_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"start_to_first_image_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"message"</span>: <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"images_expected"</span>: <span class="token number">0</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"expected_collection_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"images_received"</span>: <span class="token number">0</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"elapsed_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"step_progress"</span>: <span class="token number">1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"steps"</span>: <button class="collapser" aria-label="collapse"></button><span class="token punctuation">[</span><span class="ellipsis"></span><ul class="array collapsible"><li><div class="hoverable collapsed"><button class="collapser" aria-label="expand"></button><span class="token punctuation">{</span><span class="ellipsis"></span><ul class="obj collapsible"><li><div class="hoverable collapsed"><span class="property token string">"title"</span>: <span class="token string">&quot;string&quot;</span><span class="token punctuation">,</span></div></li><li><div class="hoverable collapsed"><span class="property token string">"started_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable collapsed"><span class="property token string">"duration_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable collapsed"><span class="property token string">"notes"</span>: <button class="collapser" aria-label="expand"></button><span class="token punctuation">[</span><span class="ellipsis"></span><ul class="array collapsible"><li><div class="hoverable collapsed"><span class="token string">&quot;string&quot;</span></div></li></ul><span class="token punctuation">]</span></div></li></ul><span class="token punctuation">}</span></div></li></ul><span class="token punctuation">]</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"last_image_s"</span>: <span class="token number">0.1</span><span class="token punctuation">,</span></div></li><li><div class="hoverable "><span class="property token string">"cut"</span>: <spaLine truncated
<code>_model.cif</code> file written beside the data: the data&#39;s cell and space group, with the input model&#39;s own
metadata (entities, sequences, references, authors, software) kept. Kept as long as <code>/rugnux/status</code>.</p>
</div></div><div class="sc-jaXxmE eqYzvz"><div class="sc-iMTnTL fkPTul"><h5 class="sc-dAlyuH sc-hwdzOV jbQuod JjoyL">Authorizations:</h5><svg class="sc-cwHptR iZRiKW" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg></div><div class="sc-krNlru gkwgxc"><span class="sc-jMakVo bPNrjo"><span class="sc-ihgnxF jlNGfN"><i>bearerAuth</i></span></span></div></div><div><h3 class="sc-fMMURN egoxtT">Responses</h3><div><button class="sc-kzqdkY jgSwKD"><svg class="sc-cwHptR hWEnWx" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg><strong class="sc-dSIIpw bpEYcE">200<!-- --> </strong><div class="sc-eeDRCY sc-eBMEME sc-dCFHLb gTGgei kekOtA kCaLQd"><p>Placed model</p>
</div></button></div><div><button class="sc-kzqdkY bXzzUN"><svg class="sc-cwHptR cSkZlF" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg><strong class="sc-dSIIpw bpEYcE">401<!-- --> </strong><div class="sc-eeDRCY sc-eBMEME sc-dCFHLb gTGgei kekOtA kCaLQd"><p>The current dataset is protected and no valid bearer token was presented.</p>
</div></button></div><div><button class="sc-kzqdkY gnmLmb" disabled=""><strong class="sc-dSIIpw bpEYcE">404<!-- --> </strong><div class="sc-eeDRCY sc-eBMEME sc-dCFHLb gTGgei kekOtA kCaLQd"><p>The last rugnux live run placed no model</p>
@@ -1113,7 +1113,7 @@ then image might be replaced in the buffer between calling /images and /image.cb
</div></button></div></div></div><div class="sc-iGgWBj sc-gsFSXq ibAZSi eDeKjd"><div class="sc-ejfMa-d bNUPQg"><button class="sc-EgOXT cFEcOl"><span type="get" class="sc-eZYNyq qGfV http-verb get">get</span><span class="sc-iEXKAA gUNSDu">/version</span><svg class="sc-cwHptR iVFMLx" style="margin-right:-25px" version="1.1" viewBox="0 0 24 24" x="0" xmlns="http://www.w3.org/2000/svg" y="0" aria-hidden="true"><polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "></polygon></svg></button><div aria-hidden="true" class="sc-dlWCHZ kWacYr"><div class="sc-hHOBiw cndTNO"><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>Test Jungfraujoch system</p>
</div><div tabindex="0" role="button"><div class="sc-kWtpeL gEncxT"><span>http://localhost:5232</span>/version</div></div></div></div></div></div></div></div></div><div class="sc-dExYaf gZbfaQ"></div></div></div>
<script>
const __redoc_state = {"menu":{"activeItemIdx":-1},"spec":{"data":{"openapi":"3.0.3","info":{"title":"Jungfraujoch","description":"API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland).\nJungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU.\nJungfraujoch uses FPGA boards to acquire data at high data rates.\n\n# License Clarification\n\nWhile this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply**\nwhen this file is used solely to generate OpenAPI clients or when implementing applications that\ninteract with the API. Generated client code and applications using this API definition are not\nsubject to the GPL license requirements and may be distributed under terms of your choosing.\n\nThis exception is similar in spirit to the Linux Kernel's approach to userspace API headers and\nthe GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that\nuser programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not\nderivative works of the kernel and are not subject to the terms of the GPL.\n\nThis exception is intended to allow wider use of this API specification without imposing GPL\nrequirements on applications that merely interact with the API, regardless of whether they\ncommunicate through network calls or other mechanisms.\n","version":"1.0.0-rc.178","contact":{"name":"Filip Leonarski (Paul Scherrer Institute)","email":"filip.leonarski@psi.ch"},"license":{"name":"GPL-3.0","url":"https://www.gnu.org/licenses/gpl-3.0.html"}},"servers":[{"url":"http://localhost:5232","description":"Test Jungfraujoch system"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Per-dataset secret. `/start` may carry one or more equivalent `tokens`; while the current\ndataset has any, the operations marked with this scheme require `Authorization: Bearer <token>`\nand answer 401 otherwise. A dataset started without `tokens` is not protected and the header is ignored.\n"}},"parameters":{"binning":{"in":"query","name":"binning","required":false,"schema":{"default":1,"type":"integer"},"description":"Binning of frames for the plot (0 = default binning)"},"experimental_coord":{"in":"query","name":"experimental_coord","required":false,"schema":{"type":"boolean","default":false},"description":"If measurement has goniometer axis defined, plot X-axis will represent rotation angle\nIf measurement has grid scan defined, plot X-axis and Y-axis will represent grid position, Z will be used as the final value\nFor still measurement the number is ignored\n"},"azint_unit":{"in":"query","name":"azint_unit","required":false,"schema":{"type":"string","enum":["Q_recipA","d_A","two_theta_deg"],"default":"Q_recipA"},"description":"Unit used for azim int.\n"},"fill_value":{"in":"query","name":"fill","required":false,"schema":{"type":"number","format":"float"},"description":"Fill value for elements that were missed during data collection\n"},"plot_type":{"in":"query","name":"type","required":true,"description":"Type of requested plot","schema":{"type":"string","enum":["bkg_estimate","spindle_blind_fraction","azint","azint_1d","spot_count","spot_count_low_res","spot_count_indexed","spot_count_ice","indexing_rate","indexing_lattice_count","indexing_unit_cell_length","indexing_unit_cell_angle","profile_radius","mosaicity","b_factor","error_pixels","saturated_pixels","image_collection_efficiency","receiver_delay","receiver_free_send_buf","strong_pixels","roi_sum","roi_mean","roi_max_count","roi_pixels","roi_weighted_x","roi_weighted_y","packets_received","max_pixel_value","resolution_estimate","pixel_sum","processing_time","beam_center_x","beam_center_y","integrated_reflections","image_scale_factor","image_scale_cc","compression_ratio","ice_ring_score"]}},"roi":{"in":"query","name":"roi","required":false,"schema":{"type":"string","minLength":1},"description":"Name of ROI for which plot is requested"},"image_id":{"in":"query","name":"id","required":false,"schema":{"type":"integer","format":"int64","default":-1,"minimum":-2},"description":"Image ID in the image buffer. Special values: -1 - last image in the buffer, -2: last indexed image in the buffer"},"saturation":{"in":"query","name":"saturation","required":false,"schema":{"type":"number","format":"float","minimum":-32767,"maximum":32767},"description":"Saturation value to set contrast in the preview image; if not provided, then autocontrast procedure is used"},"background":{"in":"query","name":"background","required":false,"schema":{"type":"number","format":"float","default":0,"minimum":-32767,"maximum":32767},"description":"Background value to set contrast in the preview image"},"show_spots":{"in":"query","name":"show_spots","schema":{"type":"boolean","default":true},"description":"Show spot finding results on the image"},"show_predictions":{"in":"query","name":"show_predictions","schema":Line truncated
const __redoc_state = {"menu":{"activeItemIdx":-1},"spec":{"data":{"openapi":"3.0.3","info":{"title":"Jungfraujoch","description":"API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland).\nJungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU.\nJungfraujoch uses FPGA boards to acquire data at high data rates.\n\n# License Clarification\n\nWhile this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply**\nwhen this file is used solely to generate OpenAPI clients or when implementing applications that\ninteract with the API. Generated client code and applications using this API definition are not\nsubject to the GPL license requirements and may be distributed under terms of your choosing.\n\nThis exception is similar in spirit to the Linux Kernel's approach to userspace API headers and\nthe GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that\nuser programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not\nderivative works of the kernel and are not subject to the terms of the GPL.\n\nThis exception is intended to allow wider use of this API specification without imposing GPL\nrequirements on applications that merely interact with the API, regardless of whether they\ncommunicate through network calls or other mechanisms.\n","version":"1.0.0-rc.178","contact":{"name":"Filip Leonarski (Paul Scherrer Institute)","email":"filip.leonarski@psi.ch"},"license":{"name":"GPL-3.0","url":"https://www.gnu.org/licenses/gpl-3.0.html"}},"servers":[{"url":"http://localhost:5232","description":"Test Jungfraujoch system"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Per-dataset secret. `/start` may carry one or more equivalent `tokens`; while the current\ndataset has any, the operations marked with this scheme require `Authorization: Bearer <token>`\nand answer 401 otherwise. A dataset started without `tokens` is not protected and the header is ignored.\n"}},"parameters":{"binning":{"in":"query","name":"binning","required":false,"schema":{"default":1,"type":"integer"},"description":"Binning of frames for the plot (0 = default binning)"},"experimental_coord":{"in":"query","name":"experimental_coord","required":false,"schema":{"type":"boolean","default":false},"description":"If measurement has goniometer axis defined, plot X-axis will represent rotation angle\nIf measurement has grid scan defined, plot X-axis and Y-axis will represent grid position, Z will be used as the final value\nFor still measurement the number is ignored\n"},"azint_unit":{"in":"query","name":"azint_unit","required":false,"schema":{"type":"string","enum":["Q_recipA","d_A","two_theta_deg"],"default":"Q_recipA"},"description":"Unit used for azim int.\n"},"fill_value":{"in":"query","name":"fill","required":false,"schema":{"type":"number","format":"float"},"description":"Fill value for elements that were missed during data collection\n"},"plot_type":{"in":"query","name":"type","required":true,"description":"Type of requested plot","schema":{"type":"string","enum":["bkg_estimate","spindle_blind_fraction","azint","azint_1d","spot_count","spot_count_low_res","spot_count_indexed","spot_count_ice","indexing_rate","indexing_lattice_count","indexing_unit_cell_length","indexing_unit_cell_angle","profile_radius","mosaicity","b_factor","error_pixels","saturated_pixels","image_collection_efficiency","receiver_delay","receiver_free_send_buf","strong_pixels","roi_sum","roi_mean","roi_max_count","roi_pixels","roi_weighted_x","roi_weighted_y","packets_received","max_pixel_value","resolution_estimate","pixel_sum","processing_time","beam_center_x","beam_center_y","integrated_reflections","image_scale_factor","image_scale_cc","compression_ratio","ice_ring_score"]}},"roi":{"in":"query","name":"roi","required":false,"schema":{"type":"string","minLength":1},"description":"Name of ROI for which plot is requested"},"image_id":{"in":"query","name":"id","required":false,"schema":{"type":"integer","format":"int64","default":-1,"minimum":-2},"description":"Image ID in the image buffer. Special values: -1 - last image in the buffer, -2: last indexed image in the buffer"},"saturation":{"in":"query","name":"saturation","required":false,"schema":{"type":"number","format":"float","minimum":-32767,"maximum":32767},"description":"Saturation value to set contrast in the preview image; if not provided, then autocontrast procedure is used"},"background":{"in":"query","name":"background","required":false,"schema":{"type":"number","format":"float","default":0,"minimum":-32767,"maximum":32767},"description":"Background value to set contrast in the preview image"},"show_spots":{"in":"query","name":"show_spots","schema":{"type":"boolean","default":true},"description":"Show spot finding results on the image"},"show_predictions":{"in":"query","name":"show_predictions","schema":Line truncated
var container = document.getElementById('redoc');
Redoc.hydrate(__redoc_state, container);
+7
View File
@@ -396,6 +396,13 @@ struct PlacedModelMessage {
std::vector<double> x, y, z;
std::vector<float> b_iso, occupancy;
std::vector<float> aniso; // 6 per atom (u11 u22 u33 u12 u13 u23), empty when none has one
// mmCIF label_asym_id, label_entity_id and label_seq_id per atom (label_seq INT32_MIN where none); empty
// in a block from before they were carried
std::vector<std::string> subchain, entity_id;
std::vector<int32_t> label_seq;
// The input model's metadata - entities, sequences, references, authors, software, secondary structure -
// as the mmCIF categories gemmi writes for them, without coordinates; empty where there is none
std::string metadata_mmcif;
};
struct EndMessage {
+1 -1
View File
@@ -346,7 +346,7 @@ See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/s
| image_scale_cc | Array(float) | Scaling result: Image scale CC | |
| merged | object (optional) | Merged reflections of an in-broker analysis run, written by the writer as `<file_prefix>.mtz`. An MTZ table (layout below) | |
| map_coefficients | object (optional) | Map coefficients of an in-broker analysis run, written as `<file_prefix>_maps.mtz`. An MTZ table (layout below) | |
| placed_model | object (optional) | Atomic model placed against the data, written as `<file_prefix>_model.cif` and `<file_prefix>_model.pdb`. `name` (string), `unit_cell` (object, as above), `space_group_name` (the data's, gemmi extended H-M), `model_space_group_name` (the coordinates'), then one array per atom: `model_number`, `seq_num`, `charge` (int32), `chain`, `residue_name`, `atom_name`, `element` (Array(string)), `icode` (uint8, space where none), `het_flag` (uint8, `A`, `H` or 0 where unknown), `altloc` (uint8, 0 where none), `x`, `y`, `z` (double, Angstrom), `b_iso`, `occupancy` (float), `aniso` (float, 6 per atom: U11 U22 U33 U12 U13 U23; empty where no atom has one) | |
| placed_model | object (optional) | Atomic model placed against the data, written as `<file_prefix>_model.cif` and `<file_prefix>_model.pdb`. `name` (string), `unit_cell` (object, as above), `space_group_name` (the data's, gemmi extended H-M), `model_space_group_name` (the coordinates'), then one array per atom: `model_number`, `seq_num`, `charge` (int32), `chain`, `residue_name`, `atom_name`, `element` (Array(string)), `icode` (uint8, space where none), `het_flag` (uint8, `A`, `H` or 0 where unknown), `altloc` (uint8, 0 where none), `x`, `y`, `z` (double, Angstrom), `b_iso`, `occupancy` (float), `aniso` (float, 6 per atom: U11 U22 U33 U12 U13 U23; empty where no atom has one), `subchain`, `entity_id` (Array(string), mmCIF label_asym_id / label_entity_id), `label_seq` (int32, label_seq_id, INT32_MIN where none) - these three optional; `metadata_mmcif` (string, optional): the input model's metadata (entities, sequences, references, authors, software, secondary structure) as mmCIF categories without coordinates, parsed by the writer with gemmi | |
End-message vector fields are optional. When present, they provide master-file summary data so readers can inspect scan-level and per-image analysis results without opening every linked data file. Missing optional per-image values are encoded by the producer as zero unless otherwise noted.
+3 -3
View File
@@ -4164,9 +4164,9 @@ Name | Type | Description | Notes
Get the model as placed by the last rugnux live run
The model placed against the data by molecular replacement or validation, as mmCIF: the coordinates, cell
and space group of the `_model.cif` file written beside the data, with the input model's own metadata
(entities, citation) kept. Kept as long as `/rugnux/status`.
The model placed against the data by molecular replacement or validation, as mmCIF - the same text as the
`_model.cif` file written beside the data: the data's cell and space group, with the input model's own
metadata (entities, sequences, references, authors, software) kept. Kept as long as `/rugnux/status`.
### Example
@@ -1669,6 +1669,10 @@ namespace {
else if (key == "b_iso") GetCBORFloatArray(map_value, m.b_iso);
else if (key == "occupancy") GetCBORFloatArray(map_value, m.occupancy);
else if (key == "aniso") GetCBORFloatArray(map_value, m.aniso);
else if (key == "subchain") GetCBORStringArray(map_value, m.subchain);
else if (key == "entity_id") GetCBORStringArray(map_value, m.entity_id);
else if (key == "label_seq") GetCBORInt32Array(map_value, m.label_seq);
else if (key == "metadata_mmcif") m.metadata_mmcif = GetCBORString(map_value);
else SkipCBORValue(map_value);
}
cborErr(cbor_value_leave_container(&value, &map_value));
@@ -623,6 +623,10 @@ inline void CBOR_ENC(CborEncoder &encoder, const char* key, const PlacedModelMes
CBOR_ENC(mapEncoder, "b_iso", m.b_iso);
CBOR_ENC(mapEncoder, "occupancy", m.occupancy);
CBOR_ENC(mapEncoder, "aniso", m.aniso);
CBOR_ENC(mapEncoder, "subchain", m.subchain);
CBOR_ENC(mapEncoder, "entity_id", m.entity_id);
CBOR_ENC(mapEncoder, "label_seq", m.label_seq);
CBOR_ENC(mapEncoder, "metadata_mmcif", m.metadata_mmcif);
cborErr(cbor_encoder_close_container(&encoder, &mapEncoder));
}
@@ -790,9 +790,9 @@ export const getRugnuxModelCifQueryKey = (options?: Options<getRugnuxModelCifDat
/**
* Get the model as placed by the last rugnux live run
*
* The model placed against the data by molecular replacement or validation, as mmCIF: the coordinates, cell
* and space group of the `_model.cif` file written beside the data, with the input model's own metadata
* (entities, citation) kept. Kept as long as `/rugnux/status`.
* The model placed against the data by molecular replacement or validation, as mmCIF - the same text as the
* `_model.cif` file written beside the data: the data's cell and space group, with the input model's own
* metadata (entities, sequences, references, authors, software) kept. Kept as long as `/rugnux/status`.
*
*/
export const getRugnuxModelCifOptions = (options?: Options<getRugnuxModelCifData>) => queryOptions<getRugnuxModelCifResponse, getRugnuxModelCifError, getRugnuxModelCifResponse, ReturnType<typeof getRugnuxModelCifQueryKey>>({
+3 -3
View File
@@ -441,9 +441,9 @@ export const getRugnuxStatus = <ThrowOnError extends boolean = false>(options?:
/**
* Get the model as placed by the last rugnux live run
*
* The model placed against the data by molecular replacement or validation, as mmCIF: the coordinates, cell
* and space group of the `_model.cif` file written beside the data, with the input model's own metadata
* (entities, citation) kept. Kept as long as `/rugnux/status`.
* The model placed against the data by molecular replacement or validation, as mmCIF - the same text as the
* `_model.cif` file written beside the data: the data's cell and space group, with the input model's own
* metadata (entities, sequences, references, authors, software) kept. Kept as long as `/rugnux/status`.
*
*/
export const getRugnuxModelCif = <ThrowOnError extends boolean = false>(options?: Options<getRugnuxModelCifData, ThrowOnError>): RequestResult<getRugnuxModelCifResponses, getRugnuxModelCifErrors, ThrowOnError> => (options?.client ?? client).get<getRugnuxModelCifResponses, getRugnuxModelCifErrors, ThrowOnError>({
+1 -1
View File
@@ -17,7 +17,7 @@
enum class RugnuxMapType { TwoFoFc, FoFc };
// The placed model and the map coefficients of a live rugnux run, for the broker to serve to a viewer: the
// model as mmCIF, in the frame of <prefix>_model.cif (PlacedModelMmcif), and the maps as CCP4 map files - made in memory when
// model as mmCIF, the same text as <prefix>_model.cif (PlacedModelMmcif), and the maps as CCP4 map files - made in memory when
// first asked for and kept, never written to disk.
class RugnuxStructure {
std::shared_ptr<const gemmi::Structure> model;
+37
View File
@@ -270,3 +270,40 @@ TEST_CASE("RugnuxResultFiles: a placed model block that describes no model is re
CHECK(!std::filesystem::exists(prefix + "_model.cif"));
std::filesystem::remove_all(dir);
}
TEST_CASE("RugnuxResultFiles: the input model's metadata reaches _model.cif through the END block", "[rugnux_result_files]") {
Logger logger("RugnuxResultFiles");
const std::filesystem::path dir = std::filesystem::temp_directory_path() / "rugnux_result_files_metadata";
std::filesystem::remove_all(dir);
std::filesystem::create_directories(dir);
const std::string original = (dir / "rugnux").string();
const std::string written = (dir / "writer").string();
// A predicted model of a standard test protein (myoglobin), with entities, sequence references, authors and
// software, as a model given to rugnux carries them.
const gemmi::Structure st = gemmi::read_structure_gz("../../tools/battery/models/AF-P68082-F1-model_v6.cif");
const gemmi::SpaceGroup &p1 = *gemmi::find_spacegroup_by_name("P 1");
WritePlacedModel(st, TEST_CELL, p1, original, logger);
EndMessage end{};
end.placed_model = PlacedModelFromStructure(st, TEST_CELL, p1.xhm());
std::vector<uint8_t> buffer(MESSAGE_SIZE_FOR_START_END);
CBORStream2Serializer serializer(buffer.data(), buffer.size());
REQUIRE_NOTHROW(serializer.SerializeSequenceEnd(end));
auto deserialized = CBORStream2Deserialize(buffer.data(), serializer.GetBufferSize());
REQUIRE(deserialized);
REQUIRE(deserialized->end_message);
WriteRugnuxResultFiles(*deserialized->end_message, written, false, logger);
const std::string cif = ReadFile(written + "_model.cif");
CHECK(cif == ReadFile(original + "_model.cif"));
CHECK(cif == PlacedModelMmcif(st, TEST_CELL, p1)); // what the broker serves at /rugnux/model.cif
for (const char *category : {"_audit_author.", "_entity_poly_seq.", "_struct_ref.", "_software.", "_struct_conf."})
CHECK(cif.find(category) != std::string::npos);
CHECK(ReadFile(written + "_model.pdb") == ReadFile(original + "_model.pdb"));
const gemmi::Structure back = gemmi::read_structure_gz(written + "_model.cif");
CHECK(gemmi::count_atom_sites(back) == gemmi::count_atom_sites(st));
CHECK(back.entities.size() == st.entities.size());
std::filesystem::remove_all(dir);
}
+17
View File
@@ -3,6 +3,11 @@
#include "RugnuxResultBlocks.h"
#include <sstream>
#include <gemmi/to_cif.hpp> // write_cif_block_to_stream
#include <gemmi/to_mmcif.hpp> // make_mmcif_block
PlacedModelMessage PlacedModelFromStructure(const gemmi::Structure &st, const UnitCell &cell,
const std::string &data_space_group_name) {
PlacedModelMessage p;
@@ -21,6 +26,9 @@ PlacedModelMessage PlacedModelFromStructure(const gemmi::Structure &st, const Un
p.seq_num.push_back(*r.seqid.num);
p.icode.push_back(static_cast<uint8_t>(r.seqid.icode));
p.het_flag.push_back(static_cast<uint8_t>(r.het_flag));
p.subchain.push_back(r.subchain);
p.entity_id.push_back(r.entity_id);
p.label_seq.push_back(r.label_seq.value);
p.atom_name.push_back(a.name);
p.element.push_back(a.element.name());
p.altloc.push_back(static_cast<uint8_t>(a.altloc));
@@ -36,5 +44,14 @@ PlacedModelMessage PlacedModelFromStructure(const gemmi::Structure &st, const Un
}
if (!has_aniso)
p.aniso.clear();
// Everything of the model but its coordinates (and what goes with them: cell, symmetry, scale), as the mmCIF
// categories gemmi writes, for the writer to put back on _model.cif.
gemmi::MmcifOutputGroups groups(true);
groups.atoms = groups.cell = groups.symmetry = groups.scale = groups.atom_type = groups.origx = false;
// _entry.id would otherwise be made up from the name, which the block carries anyway.
groups.entry = st.info.count("_entry.id") > 0;
std::ostringstream os;
gemmi::cif::write_cif_block_to_stream(os, gemmi::make_mmcif_block(st, groups));
p.metadata_mmcif = os.str();
return p;
}
+30
View File
@@ -8,6 +8,8 @@
#include <filesystem>
#include <gemmi/mtz.hpp>
#include <gemmi/read_cif.hpp> // cif::read_string
#include <gemmi/mmcif.hpp> // make_structure_from_block
#include "WriteModel.h"
#include "../common/Logger.h"
@@ -70,6 +72,10 @@ gemmi::Structure PlacedModelStructure(const PlacedModelMessage &model) {
if (size != n)
throw std::runtime_error(fmt::format("placed model: a per-atom field has {} entries for {} atoms",
size, n));
for (const size_t size : {model.subchain.size(), model.entity_id.size(), model.label_seq.size()})
if (size != 0 && size != n)
throw std::runtime_error(fmt::format("placed model: a per-atom label field has {} entries for {} atoms",
size, n));
if (!model.aniso.empty() && model.aniso.size() != 6 * n)
throw std::runtime_error(fmt::format("placed model: {} anisotropic values for {} atoms",
model.aniso.size(), n));
@@ -95,6 +101,12 @@ gemmi::Structure PlacedModelStructure(const PlacedModelMessage &model) {
|| res->het_flag != static_cast<char>(model.het_flag[i])) {
res = &ch->residues.emplace_back(gemmi::ResidueId{seqid, {}, model.residue_name[i]});
res->het_flag = static_cast<char>(model.het_flag[i]);
if (!model.subchain.empty())
res->subchain = model.subchain[i];
if (!model.entity_id.empty())
res->entity_id = model.entity_id[i];
if (!model.label_seq.empty())
res->label_seq.value = model.label_seq[i];
}
gemmi::Atom &a = res->atoms.emplace_back();
a.name = model.atom_name[i];
@@ -108,6 +120,24 @@ gemmi::Structure PlacedModelStructure(const PlacedModelMessage &model) {
a.aniso = {model.aniso[6 * i], model.aniso[6 * i + 1], model.aniso[6 * i + 2],
model.aniso[6 * i + 3], model.aniso[6 * i + 4], model.aniso[6 * i + 5]};
}
if (!model.metadata_mmcif.empty()) {
// The input model's metadata, from the mmCIF categories it came as: parsed by gemmi, never copied.
gemmi::cif::Document doc = gemmi::cif::read_string(model.metadata_mmcif);
if (doc.blocks.empty())
throw std::runtime_error("placed model: the metadata block is empty");
gemmi::Structure meta = gemmi::make_structure_from_block(doc.blocks[0]);
st.entities = std::move(meta.entities);
st.connections = std::move(meta.connections);
st.cispeps = std::move(meta.cispeps);
st.mod_residues = std::move(meta.mod_residues);
st.helices = std::move(meta.helices);
st.sheets = std::move(meta.sheets);
st.assemblies = std::move(meta.assemblies);
st.ncs = std::move(meta.ncs);
st.meta = std::move(meta.meta);
st.info = std::move(meta.info);
st.resolution = meta.resolution;
}
st.setup_cell_images();
return st;
}