rugnux live: overall R-free of the final model; the writer writes _report.txt
/rugnux/status overall.r_free reported the model as placed (R_FREE of the report) even where rugnux had refined it, while model.refinement.r_free and the page's tile showed the refined one. The overall row now carries the R-free of the model the run ended with: the refined model's where the refinement ran (as model.refinement.r_free, REFINE_R_FREE of the report), else the placed model's. The shell rows stay the placed model's - the refinement measures no shell. Serial runs get the same through ShellRFree. The OpenAPI descriptions say so; C++ model, frontend client and Redoc regenerated. Live runs now write <file_prefix>_report.txt. The receiver already built the report document for the live status; its text (RenderReportText, the same rendering the CLI writes) goes to the writer as a new optional END message field `report`, written beside the other result files under a temporary name and renamed into place. No file I/O in the broker. The CBOR string decoder was capped at 16 kB; it now sizes the buffer to the string (a report is tens of kB, a model's metadata block can exceed the cap as well). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK
This commit is contained in:
1 parent
125003d693
commit
7fbbe449ff
20 files changed
+208
-43
No files matched your search
@@ -127,7 +127,7 @@ public:
|
||||
bool rSplitIsSet() const;
|
||||
void unsetR_split();
|
||||
/// <summary>
|
||||
/// R-free of the placed model (as given, not refined) over the shell's free reflections
|
||||
/// R-free of the placed model (as given, not refined) over the shell's free reflections. In `overall`, the R-free of the model the run ended with: the refined model's (`model.refinement.r_free`) where rugnux refined it, else the placed model's (`model.r_free`).
|
||||
/// </summary>
|
||||
double getRFree() const;
|
||||
void setRFree(double const value);
|
||||
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
bool shellsMergeIsSet() const;
|
||||
void unsetShells_merge();
|
||||
/// <summary>
|
||||
/// Merging statistics by resolution shell of the latest merge, coarse to fine, as the shell table of `_report.txt`. Rotation: once the run is over; serial: at each provisional merge and at the end. `overall` is the same table's \"Overall\" row (whole-merge completeness, multiplicity, I/sigma, CC1/2, R_meas or R_split, CCref, and the overall R-free of the model validated on it), present with `shells`.
|
||||
/// Merging statistics by resolution shell of the latest merge, coarse to fine, as the shell table of `_report.txt`. Rotation: once the run is over; serial: at each provisional merge and at the end. `overall` is the same table's \"Overall\" row (whole-merge completeness, multiplicity, I/sigma, CC1/2, R_meas or R_split, CCref, and the R-free of the final model - refined where rugnux refined it, as `model.refinement.r_free`, else as placed, as `model.r_free`), present with `shells`.
|
||||
/// </summary>
|
||||
std::vector<org::openapitools::server::model::Rugnux_live_shell> getShells() const;
|
||||
void setShells(std::vector<org::openapitools::server::model::Rugnux_live_shell> const& value);
|
||||
|
||||
@@ -3227,7 +3227,10 @@ components:
|
||||
r_free:
|
||||
type: number
|
||||
format: double
|
||||
description: R-free of the placed model (as given, not refined) over the shell's free reflections
|
||||
description: |
|
||||
R-free of the placed model (as given, not refined) over the shell's free reflections. In `overall`, the
|
||||
R-free of the model the run ended with: the refined model's (`model.refinement.r_free`) where rugnux
|
||||
refined it, else the placed model's (`model.r_free`).
|
||||
rugnux_live_frame_range:
|
||||
type: object
|
||||
description: |
|
||||
@@ -3313,7 +3316,8 @@ components:
|
||||
Merging statistics by resolution shell of the latest merge, coarse to fine, as the shell table of
|
||||
`_report.txt`. Rotation: once the run is over; serial: at each provisional merge and at the end.
|
||||
`overall` is the same table's "Overall" row (whole-merge completeness, multiplicity, I/sigma, CC1/2,
|
||||
R_meas or R_split, CCref, and the overall R-free of the model validated on it), present with `shells`.
|
||||
R_meas or R_split, CCref, and the R-free of the final model - refined where rugnux refined it, as
|
||||
`model.refinement.r_free`, else as placed, as `model.r_free`), present with `shells`.
|
||||
items:
|
||||
$ref: '#/components/schemas/rugnux_live_shell'
|
||||
overall:
|
||||
|
||||
@@ -553,7 +553,9 @@ If parameter is not provided calibration will be saved only if more than 4 image
|
||||
</div></div></div></td></tr><tr class=""><td class="sc-dLMFU sc-eldPxv sc-hCPjZK hZbWKe jXkfaY GCgeS" kind="field" title="smargon"><span class="sc-gFqAkR eRqgPI"></span><button aria-label="expand smargon"><span class="property-name">smargon</span><svg class="sc-cwHptR ivsSRP" 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></td><td class="sc-fPXMVe bAXEAa"><div><div><span class="sc-Nxspf sc-cfxfcM cXxAXt jrGEaR"></span><span class="sc-Nxspf sc-gFAWRd cXxAXt dYzdDr">object</span></div> <div><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>Geometry of Smargon goniometer at SLS 2.0 / MX beamlines.
|
||||
Assuming that Smargon is used as static positioner and not moving during the scan, allowing to reconstruct geometry.</p>
|
||||
</div></div></div></td></tr><tr class=""><td class="sc-dLMFU sc-eldPxv hZbWKe jXkfaY" kind="field" title="max_spot_count"><span class="sc-gFqAkR eRqgPI"></span><span class="property-name">max_spot_count</span></td><td class="sc-fPXMVe bAXEAa"><div><div><span class="sc-Nxspf sc-cfxfcM cXxAXt jrGEaR"></span><span class="sc-Nxspf sc-gFAWRd cXxAXt dYzdDr">integer</span><span> <span class="sc-Nxspf sc-fxwrCY cXxAXt eMVOei"> <!-- -->[ 10 .. 2000 ]<!-- --> </span></span></div><div><span class="sc-Nxspf cXxAXt"> <!-- -->Default:<!-- --> </span> <span class="sc-Nxspf sc-ddjGPC cXxAXt jZOCAW">250</span></div> <div><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>Maximum number of spots that are saved/used for indexing; spots with highest intensity are selected</p>
|
||||
</div></div></div></td></tr><tr class=""><td class="sc-dLMFU sc-eldPxv hZbWKe jXkfaY" kind="field" title="detect_ice_rings"><span class="sc-gFqAkR eRqgPI"></span><span class="property-name">detect_ice_rings</span></td><td class="sc-fPXMVe bAXEAa"><div><div><span class="sc-Nxspf sc-cfxfcM cXxAXt jrGEaR"></span><span class="sc-Nxspf sc-gFAWRd cXxAXt dYzdDr">boolean</span></div> <div><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>Flag spots as ice rings and reduce their effect on indexing</p>
|
||||
</div></div></div></td></tr><tr class=""><td class="sc-dLMFU sc-eldPxv hZbWKe jXkfaY" kind="field" title="detect_ice_rings"><span class="sc-gFqAkR eRqgPI"></span><span class="property-name">detect_ice_rings</span></td><td class="sc-fPXMVe bAXEAa"><div><div><span class="sc-Nxspf sc-cfxfcM cXxAXt jrGEaR"></span><span class="sc-Nxspf sc-gFAWRd cXxAXt dYzdDr">boolean</span></div> <div><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>Flag spots as ice rings and reduce their effect on indexing. Left out, the broker's own
|
||||
spot finding treats no spot as an ice ring, rugnux live decides as a bare <code>rugnux</code> would (on
|
||||
for a rotation sweep, off for stills), and the master file carries no <code>detect_ice_rings</code>.</p>
|
||||
</div></div></div></td></tr><tr class=""><td class="sc-dLMFU sc-eldPxv hZbWKe jXkfaY" kind="field" title="async_start"><span class="sc-gFqAkR eRqgPI"></span><span class="property-name">async_start</span></td><td class="sc-fPXMVe bAXEAa"><div><div><span class="sc-Nxspf sc-cfxfcM cXxAXt jrGEaR"></span><span class="sc-Nxspf sc-gFAWRd cXxAXt dYzdDr">boolean</span></div><div><span class="sc-Nxspf cXxAXt"> <!-- -->Default:<!-- --> </span> <span class="sc-Nxspf sc-ddjGPC cXxAXt jZOCAW">false</span></div> <div><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>When set to true, <code>/start</code> will not wait for detector and Jungfraujoch to be ready for the measurement.</p>
|
||||
</div></div></div></td></tr><tr class=""><td class="sc-dLMFU sc-eldPxv sc-hCPjZK hZbWKe jXkfaY GCgeS" kind="field" title="module_placement"><span class="sc-gFqAkR eRqgPI"></span><button aria-label="expand module_placement"><span class="property-name">module_placement</span><svg class="sc-cwHptR ivsSRP" 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></td><td class="sc-fPXMVe bAXEAa"><div><div><span class="sc-Nxspf sc-cfxfcM cXxAXt jrGEaR">Array of </span><span class="sc-Nxspf sc-gFAWRd cXxAXt dYzdDr">objects</span><span class="sc-Nxspf sc-gmPhUn cXxAXt hjnRRA"> (<!-- -->detector_module_placement<!-- -->) </span></div> <div><div class="sc-eeDRCY sc-eBMEME gTGgei fbXBig"><p>Where the detector's modules lie for this dataset, as rugnux measures them: rugnux writes
|
||||
<code><prefix>_module_placement.json</code>, which holds exactly this field, to be merged into the dataset settings.
|
||||
@@ -1144,7 +1146,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.186","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.186","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);
|
||||
|
||||
@@ -552,6 +552,8 @@ struct EndMessage {
|
||||
std::optional<PlacedModelMessage> refined_model;
|
||||
std::optional<MtzTable> refined_map_coefficients;
|
||||
std::optional<DensityBlobsMessage> blobs;
|
||||
// And the run's results report, for <file_prefix>_report.txt: the text `rugnux` writes (rugnux/ResultReport.h).
|
||||
std::optional<std::string> report;
|
||||
};
|
||||
|
||||
struct MetadataMessage {
|
||||
|
||||
@@ -331,6 +331,7 @@ Every scalar of the image message is optional: a quantity that was not computed
|
||||
| refined_model | object (optional) | The placed model after refinement, written as `<file_prefix>_refined_model.cif` and `<file_prefix>_refined_model.pdb`; same layout as `placed_model` | |
|
||||
| refined_map_coefficients | object (optional) | Map coefficients of the refined model, written as `<file_prefix>_refined_maps.mtz`. An MTZ table (layout below) | |
|
||||
| blobs | object (optional) | Unexplained blobs of the refined model's mFo-DFc map, strongest first, written as `<file_prefix>_blobs.json`. One array per field, one entry per blob: `kind` (Array(string): `WATER`, `ION`, `LIGAND`, `MODEL_ERROR`, `UNASSIGNED`), `x`, `y`, `z` (double, centroid, Angstrom), `peak_sigma` (double, map rms), `volume` (double, Angstrom^3), `score` (double, sigma Angstrom^3), `nearest_atom` (Array(string)), `nearest_atom_distance` (double, Angstrom), `polar_contacts`, `metal_contacts` (int32), `anomalous_sigma` (double, 0 where there is no anomalous map) | |
|
||||
| report | string (optional) | The in-broker analysis run's results report, written as `<file_prefix>_report.txt`: the text `rugnux` writes as its own `_report.txt` (see [RUGNUX_REPORT](RUGNUX_REPORT.md)) | |
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -255,7 +255,8 @@ The results - the merged data, the map coefficients and, with a model, the place
|
||||
refinement - travel to the writer in the end message, which writes them beside the master file as
|
||||
`<file_prefix>.mtz`, `<file_prefix>_maps.mtz`, `<file_prefix>_model.cif` and `<file_prefix>_model.pdb`,
|
||||
and `<file_prefix>_refined_model.cif` (and `.pdb`), `<file_prefix>_refined_maps.mtz` and
|
||||
`<file_prefix>_blobs.json`, as rugnux names them. These files name both programs as the software that
|
||||
`<file_prefix>_blobs.json`, as rugnux names them, and the results report `<file_prefix>_report.txt` - the
|
||||
report `rugnux` writes for its runs. These files name both programs as the software that
|
||||
made them: the MTZ files in a HISTORY record beside Rugnux's, the models in their `_software` loop
|
||||
(Rugnux, and Jungfraujoch for the data collection). An end message
|
||||
that would be too large for the pushers' buffer (256 MiB) goes without them, with an error in the log
|
||||
|
||||
@@ -12,8 +12,11 @@ namespace {
|
||||
if (!cbor_value_is_text_string(&value))
|
||||
throw JFJochException(JFJochExceptionCategory::CBORError, "String expected");
|
||||
|
||||
std::vector<char> s(16384);
|
||||
size_t len = s.size() - 1;
|
||||
// As long as the string is: a model's metadata or a results report runs to tens of kB
|
||||
size_t len;
|
||||
cborErr(cbor_value_calculate_string_length(&value, &len));
|
||||
std::vector<char> s(len + 1);
|
||||
len = s.size();
|
||||
cborErr(cbor_value_copy_text_string(&value, s.data(), &len, nullptr));
|
||||
s[len] = 0;
|
||||
cborErr(cbor_value_advance(&value));
|
||||
@@ -1823,6 +1826,8 @@ namespace {
|
||||
message.refined_map_coefficients = ProcessMtzTableElement(value);
|
||||
else if (key == "blobs")
|
||||
message.blobs = ProcessDensityBlobsElement(value);
|
||||
else if (key == "report")
|
||||
message.report = GetCBORString(value);
|
||||
else if (key == "rotation_lattice") {
|
||||
std::vector<float> tmp;
|
||||
GetCBORFloatArray(value, tmp);
|
||||
|
||||
@@ -1004,6 +1004,7 @@ void CBORStream2Serializer::SerializeSequenceEnd(const EndMessage& message) {
|
||||
CBOR_ENC(mapEncoder, "refined_model", message.refined_model);
|
||||
CBOR_ENC(mapEncoder, "refined_map_coefficients", message.refined_map_coefficients);
|
||||
CBOR_ENC(mapEncoder, "blobs", message.blobs);
|
||||
CBOR_ENC(mapEncoder, "report", message.report);
|
||||
|
||||
cborErr(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
|
||||
|
||||
@@ -386,7 +386,10 @@ export type dataset_settings = {
|
||||
*/
|
||||
max_spot_count?: number;
|
||||
/**
|
||||
* Flag spots as ice rings and reduce their effect on indexing
|
||||
* Flag spots as ice rings and reduce their effect on indexing. Left out, the broker's own
|
||||
* spot finding treats no spot as an ice ring, rugnux live decides as a bare `rugnux` would (on
|
||||
* for a rotation sweep, off for stills), and the master file carries no `detect_ice_rings`.
|
||||
*
|
||||
*/
|
||||
detect_ice_rings?: boolean;
|
||||
/**
|
||||
@@ -2356,7 +2359,10 @@ export type rugnux_live_shell = {
|
||||
*/
|
||||
r_split?: number;
|
||||
/**
|
||||
* R-free of the placed model (as given, not refined) over the shell's free reflections
|
||||
* R-free of the placed model (as given, not refined) over the shell's free reflections. In `overall`, the
|
||||
* R-free of the model the run ended with: the refined model's (`model.refinement.r_free`) where rugnux
|
||||
* refined it, else the placed model's (`model.r_free`).
|
||||
*
|
||||
*/
|
||||
r_free?: number;
|
||||
};
|
||||
@@ -2425,7 +2431,8 @@ export type rugnux_live_status = {
|
||||
* Merging statistics by resolution shell of the latest merge, coarse to fine, as the shell table of
|
||||
* `_report.txt`. Rotation: once the run is over; serial: at each provisional merge and at the end.
|
||||
* `overall` is the same table's "Overall" row (whole-merge completeness, multiplicity, I/sigma, CC1/2,
|
||||
* R_meas or R_split, CCref, and the overall R-free of the model validated on it), present with `shells`.
|
||||
* R_meas or R_split, CCref, and the R-free of the final model - refined where rugnux refined it, as
|
||||
* `model.refinement.r_free`, else as placed, as `model.r_free`), present with `shells`.
|
||||
*
|
||||
*/
|
||||
shells?: Array<rugnux_live_shell>;
|
||||
@@ -2457,11 +2464,11 @@ export type rugnux_live_status = {
|
||||
*/
|
||||
isa?: number;
|
||||
/**
|
||||
* Answer of the early lattice probe on the first 60 degrees; empty until it answers
|
||||
* Answer of the early lattice probe on the first 60 degrees; empty until it answers, and the run's own lattice once the run has indexed
|
||||
*/
|
||||
probe: string;
|
||||
/**
|
||||
* When the probe answered, since the first image
|
||||
* When the probe answered (or the run indexed), since the first image
|
||||
*/
|
||||
probe_s?: number;
|
||||
/**
|
||||
@@ -2795,7 +2802,10 @@ export type dataset_settingsWritable = {
|
||||
*/
|
||||
max_spot_count?: number;
|
||||
/**
|
||||
* Flag spots as ice rings and reduce their effect on indexing
|
||||
* Flag spots as ice rings and reduce their effect on indexing. Left out, the broker's own
|
||||
* spot finding treats no spot as an ice ring, rugnux live decides as a bare `rugnux` would (on
|
||||
* for a rotation sweep, off for stills), and the master file carries no `detect_ice_rings`.
|
||||
*
|
||||
*/
|
||||
detect_ice_rings?: boolean;
|
||||
/**
|
||||
|
||||
@@ -267,7 +267,7 @@ void JFJochReceiverRugnuxRotation::RugnuxThread() {
|
||||
std::optional<Reachable> reachable;
|
||||
reachable.emplace(rugnux_mutex, rugnux, *process, cut, cancelled);
|
||||
ProcessResult result = process->Run(&observer);
|
||||
// The verdict and summary of the report a `rugnux` run writes, without writing it.
|
||||
// The report a `rugnux` run writes, for the writer to write: its verdict and summary for the status as well.
|
||||
const ReportDocument doc = BuildReportDocument(experiment.GetFilePrefix(), experiment.GetFilePrefix(),
|
||||
setup.experiment, result);
|
||||
// A cancelled collection is no failed analysis, and the run stops at the images that came
|
||||
@@ -310,8 +310,9 @@ void JFJochReceiverRugnuxRotation::RugnuxThread() {
|
||||
live_status.Headline(result.space_group ? result.space_group->xhm() : "", d_min, isa);
|
||||
if (result.has_merge_statistics) {
|
||||
const ModelValidationResult *validation = result.model_validation ? &*result.model_validation : nullptr;
|
||||
const RefinementResult *refinement = result.refinement ? &*result.refinement : nullptr;
|
||||
live_status.Shells("the collection", LiveShells(result.merge_statistics, validation),
|
||||
LiveOverall(result.merge_statistics, validation));
|
||||
LiveOverall(result.merge_statistics, validation, refinement));
|
||||
live_status.Frames(result.end_message.image_scale_factor, LiveFrameRanges(result.merge_statistics.sweep_quality));
|
||||
}
|
||||
// For the broker's viewer routes; a copy of the map coefficients, as the end message takes the originals
|
||||
@@ -322,9 +323,11 @@ void JFJochReceiverRugnuxRotation::RugnuxThread() {
|
||||
}
|
||||
live_status.Finish(result.cancelled ? RugnuxLiveState::Cancelled : RugnuxLiveState::Done);
|
||||
reachable.reset();
|
||||
std::string report = RenderReportText(doc, false);
|
||||
{
|
||||
std::unique_lock ul(rugnux_mutex);
|
||||
rugnux_result = std::move(result);
|
||||
rugnux_report = std::move(report);
|
||||
}
|
||||
published = true;
|
||||
rugnux_published.set_value();
|
||||
@@ -407,6 +410,7 @@ void JFJochReceiverRugnuxRotation::AddToEndMessage(EndMessage &message) {
|
||||
message.refined_poni_rot2 = rugnux_end.refined_poni_rot2;
|
||||
message.refined_poni_rot3 = rugnux_end.refined_poni_rot3;
|
||||
message.refined_rotation_axis = rugnux_end.refined_rotation_axis;
|
||||
message.report = rugnux_report;
|
||||
// For the writer to write beside the master; moved, the result is not read again
|
||||
if (!rugnux_result->outputs)
|
||||
return;
|
||||
|
||||
@@ -29,6 +29,7 @@ class JFJochReceiverRugnuxRotation : public JFJochReceiverStream {
|
||||
Rugnux *rugnux = nullptr; // while it runs, for Cut
|
||||
Rugnux *probe = nullptr; // the same for the lattice probe
|
||||
std::optional<ProcessResult> rugnux_result;
|
||||
std::string rugnux_report; // its _report.txt, for the writer
|
||||
std::promise<void> rugnux_published; // rugnux_result is in place, or the run has failed (RugnuxThread)
|
||||
bool cut = false;
|
||||
|
||||
|
||||
@@ -17,16 +17,27 @@ namespace {
|
||||
out = v;
|
||||
}
|
||||
|
||||
// The refined model's R-work and R-free: after the restrained stage, else after the rigid body and grouped B
|
||||
double RefinedRWork(const RefinementResult &rf) {
|
||||
return rf.restrained ? rf.r_work : rf.r_work_placement;
|
||||
}
|
||||
|
||||
double RefinedRFree(const RefinementResult &rf) {
|
||||
return rf.restrained ? rf.r_free : rf.r_free_placement;
|
||||
}
|
||||
|
||||
// The validation's shells are the merge's, labelled by the same d_min (report_shell_d_min); its overall R-free
|
||||
// is the overall row's
|
||||
// is the overall row's - unless the model was refined, when the overall row is the refined model's
|
||||
void AddShellRFree(std::vector<RugnuxLiveShell> &shells, std::optional<RugnuxLiveShell> &overall,
|
||||
const ModelValidationResult &validation) {
|
||||
const ModelValidationResult &validation, const RefinementResult *refinement) {
|
||||
for (const auto &m : validation.cc_model_shells)
|
||||
for (auto &sh : shells)
|
||||
if (static_cast<float>(sh.d_min) == m.d_min && m.n_free > 0)
|
||||
SetIfFinite(sh.r_free, m.r_free);
|
||||
if (overall && validation.ok)
|
||||
SetIfFinite(overall->r_free, validation.r_free);
|
||||
if (overall && refinement && refinement->ok)
|
||||
SetIfFinite(overall->r_free, RefinedRFree(*refinement));
|
||||
}
|
||||
|
||||
// A row of the report's shell table - a shell, or the overall row
|
||||
@@ -193,10 +204,11 @@ void RugnuxLiveStatus::Shells(const std::string &merge, const std::vector<Rugnux
|
||||
snapshot.overall = overall;
|
||||
}
|
||||
|
||||
void RugnuxLiveStatus::ShellRFree(const std::string &merge, const ModelValidationResult &validation) {
|
||||
void RugnuxLiveStatus::ShellRFree(const std::string &merge, const ModelValidationResult &validation,
|
||||
const RefinementResult *refinement) {
|
||||
std::unique_lock ul(status_mutex);
|
||||
if (snapshot.shells_merge == merge)
|
||||
AddShellRFree(snapshot.shells, snapshot.overall, validation);
|
||||
AddShellRFree(snapshot.shells, snapshot.overall, validation, refinement);
|
||||
}
|
||||
|
||||
void RugnuxLiveStatus::Frames(const std::vector<float> &scales, const std::vector<RugnuxLiveFrameRange> &ranges) {
|
||||
@@ -287,15 +299,16 @@ std::vector<RugnuxLiveShell> LiveShells(const MergeStatistics &statistics, const
|
||||
ret.push_back(LiveShell(in, statistics.stills));
|
||||
std::optional<RugnuxLiveShell> no_overall;
|
||||
if (validation)
|
||||
AddShellRFree(ret, no_overall, *validation);
|
||||
AddShellRFree(ret, no_overall, *validation, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RugnuxLiveShell LiveOverall(const MergeStatistics &statistics, const ModelValidationResult *validation) {
|
||||
RugnuxLiveShell LiveOverall(const MergeStatistics &statistics, const ModelValidationResult *validation,
|
||||
const RefinementResult *refinement) {
|
||||
std::vector<RugnuxLiveShell> no_shells;
|
||||
std::optional<RugnuxLiveShell> ret = LiveShell(statistics.overall, statistics.stills);
|
||||
if (validation)
|
||||
AddShellRFree(no_shells, ret, *validation);
|
||||
AddShellRFree(no_shells, ret, *validation, refinement);
|
||||
return *ret;
|
||||
}
|
||||
|
||||
@@ -355,8 +368,8 @@ std::optional<RugnuxLiveModel> LiveModel(const ProcessResult &result) {
|
||||
r.on_gpu = rf.on_gpu;
|
||||
r.seconds = rf.seconds;
|
||||
if (rf.ok) {
|
||||
SetIfFinite(r.r_work, rf.restrained ? rf.r_work : rf.r_work_placement);
|
||||
SetIfFinite(r.r_free, rf.restrained ? rf.r_free : rf.r_free_placement);
|
||||
SetIfFinite(r.r_work, RefinedRWork(rf));
|
||||
SetIfFinite(r.r_free, RefinedRFree(rf));
|
||||
if (rf.restrained) {
|
||||
SetIfFinite(r.rmsd_bonds_A, rf.geometry.rmsd_bonds);
|
||||
SetIfFinite(r.rmsd_angles_deg, rf.geometry.rmsd_angles);
|
||||
|
||||
@@ -17,6 +17,7 @@ struct ReportDocument; // rugnux/ReportDocument.h
|
||||
struct MergeStatistics; // image_analysis/scale_merge/Merge.h
|
||||
struct SweepQuality; // image_analysis/scale_merge/Merge.h
|
||||
struct ModelValidationResult; // image_analysis/structure_refinement/ModelValidation.h
|
||||
struct RefinementResult; // image_analysis/structure_refinement/Refinement.h
|
||||
|
||||
// Where the live rugnux run of the last collection is.
|
||||
// Times come from the image puller (when a message came off the socket) and are in seconds since the first image arrived.
|
||||
@@ -104,7 +105,9 @@ struct RugnuxLiveShell {
|
||||
std::optional<double> cc_half;
|
||||
std::optional<double> cc_ref; // against a reference
|
||||
std::optional<double> r_split; // stills only
|
||||
std::optional<double> r_free; // of the placed model, where a model was given
|
||||
// Of the placed model, where a model was given. The overall row: of the model the run ended with - refined
|
||||
// where the refinement ran (RugnuxLiveRefinement's), else as placed.
|
||||
std::optional<double> r_free;
|
||||
};
|
||||
|
||||
// A stretch of a rotation sweep the merge downgraded or rejected (SweepQuality), with what was seen there
|
||||
@@ -214,8 +217,10 @@ public:
|
||||
void Stills(const RugnuxLiveStills &stills);
|
||||
void Shells(const std::string &merge, const std::vector<RugnuxLiveShell> &shells,
|
||||
const std::optional<RugnuxLiveShell> &overall);
|
||||
// The R-free by shell of a model placed on a merge, if that merge's table is the one shown
|
||||
void ShellRFree(const std::string &merge, const ModelValidationResult &validation);
|
||||
// The R-free by shell of a model placed on a merge, if that merge's table is the one shown; overall that of the
|
||||
// refined model where it was refined
|
||||
void ShellRFree(const std::string &merge, const ModelValidationResult &validation,
|
||||
const RefinementResult *refinement = nullptr);
|
||||
void Frames(const std::vector<float> &scales, const std::vector<RugnuxLiveFrameRange> &ranges);
|
||||
// Back to Idle with nothing of the last run, as before any collection.
|
||||
void Clear();
|
||||
@@ -232,8 +237,10 @@ std::optional<RugnuxLiveModel> LiveModel(const ProcessResult &result);
|
||||
std::vector<RugnuxLiveShell> LiveShells(const MergeStatistics &statistics,
|
||||
const ModelValidationResult *validation = nullptr);
|
||||
|
||||
// The overall row of the same table, with the overall R-free of the model validated on it
|
||||
RugnuxLiveShell LiveOverall(const MergeStatistics &statistics, const ModelValidationResult *validation = nullptr);
|
||||
// The overall row of the same table, with the overall R-free of the model validated on it - or of the model refined,
|
||||
// where it was
|
||||
RugnuxLiveShell LiveOverall(const MergeStatistics &statistics, const ModelValidationResult *validation = nullptr,
|
||||
const RefinementResult *refinement = nullptr);
|
||||
|
||||
// The stretches of the sweep that were not merged at face value: the runs of one disposition in the
|
||||
// per-image array of the sweep-quality diagnostic, each with the reason of the range it lies in
|
||||
|
||||
@@ -581,7 +581,7 @@ void StillsGPUAnalysis::PublishModel(const ModelStepResult &result, const std::s
|
||||
if (const auto live_model = LiveModel(ModelStepReport(result)))
|
||||
status.Model(*live_model);
|
||||
if (result.validation)
|
||||
status.ShellRFree(title, *result.validation);
|
||||
status.ShellRFree(title, *result.validation, result.refinement ? &*result.refinement : nullptr);
|
||||
if (!result.placed_model)
|
||||
return;
|
||||
std::optional<PlacedModelOutput> refined;
|
||||
|
||||
@@ -711,6 +711,12 @@ TEST_CASE("JFJochReceiverRugnux_Rotation", "[JFJochReceiverRugnux]") {
|
||||
CHECK(live.state != RugnuxLiveState::Running);
|
||||
// The report's checklist comes with its verdict
|
||||
CHECK(live.verdict.empty() == live.triage.empty());
|
||||
// And the report itself goes to the writer, for <file_prefix>_report.txt
|
||||
const auto end = pusher.End();
|
||||
REQUIRE(end);
|
||||
CHECK(end->report.has_value() == !live.verdict.empty());
|
||||
if (end->report)
|
||||
CHECK(end->report->find("\nVERDICT= " + live.verdict + "\n") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE("JFJochReceiverRugnux_Rotation_DoesNotFit", "[JFJochReceiverRugnux]") {
|
||||
|
||||
@@ -545,3 +545,51 @@ TEST_CASE("RugnuxLiveShells_AreTheReportTable", "[RugnuxLive]") {
|
||||
CHECK(!j.contains("overall"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RugnuxLiveStatus_OverallRFreeIsTheFinalModels", "[RugnuxLive]") {
|
||||
MergeStatistics ms;
|
||||
ms.shells = {TestShell(50.0, 4.0, 4000, 1000, 1010), TestShell(4.0, 2.2, 3000, 1000, 1050)};
|
||||
ms.overall = TestShell(50.0, 2.2, 7000, 2000, 2060);
|
||||
ModelValidationResult v;
|
||||
v.ok = true;
|
||||
v.r_work = 0.40;
|
||||
v.r_free = 0.42;
|
||||
v.cc_model_shells = {{4.0f, 0.9, 900, NAN, NAN, NAN, 0.35, 50}, {2.2f, 0.8, 900, NAN, NAN, NAN, 0.47, 45}};
|
||||
RefinementResult rf;
|
||||
rf.ok = true;
|
||||
rf.r_work_placement = 0.36;
|
||||
rf.r_free_placement = 0.39;
|
||||
rf.restrained = true;
|
||||
rf.r_work = 0.33;
|
||||
rf.r_free = 0.37;
|
||||
|
||||
// Not refined: the placed model's, as the report's R_FREE
|
||||
CHECK(LiveOverall(ms, &v).r_free.value() == Catch::Approx(0.42));
|
||||
RefinementResult failed;
|
||||
CHECK(LiveOverall(ms, &v, &failed).r_free.value() == Catch::Approx(0.42));
|
||||
// Refined: the refined model's, as model.refinement.r_free and the report's REFINE_R_FREE
|
||||
CHECK(LiveOverall(ms, &v, &rf).r_free.value() == Catch::Approx(0.37));
|
||||
rf.restrained = false;
|
||||
CHECK(LiveOverall(ms, &v, &rf).r_free.value() == Catch::Approx(0.39));
|
||||
rf.restrained = true;
|
||||
|
||||
ProcessResult r;
|
||||
r.model_validation = v;
|
||||
r.model_placement = "RIGID_BODY";
|
||||
r.refinement = rf;
|
||||
RugnuxLiveStatus status;
|
||||
status.Start(10, 1.0, std::chrono::steady_clock::now());
|
||||
status.Model(*LiveModel(r));
|
||||
status.Shells("the collection", LiveShells(ms, &v), LiveOverall(ms, &v, &rf));
|
||||
nlohmann::json j = Convert(status.Get());
|
||||
CHECK(j["overall"]["r_free"].get<double>() == j["model"]["refinement"]["r_free"].get<double>());
|
||||
CHECK(j["model"]["r_free"].get<double>() == Catch::Approx(0.42));
|
||||
// The shells stay the placed model's: the refinement measures no shell
|
||||
CHECK(j["shells"][0]["r_free"].get<double>() == Catch::Approx(0.35));
|
||||
|
||||
// Serial: the model placed on a merge and refined after its table was shown
|
||||
status.Shells("the collection", LiveShells(ms), LiveOverall(ms));
|
||||
status.ShellRFree("the collection", v, &rf);
|
||||
CHECK(status.Get().overall->r_free.value() == Catch::Approx(0.37));
|
||||
CHECK(status.Get().shells[1].r_free.value() == Catch::Approx(0.47));
|
||||
}
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "../writer/WriteModel.h"
|
||||
#include "../writer/WriteRugnuxFiles.h"
|
||||
#include "../writer/RugnuxResultBlocks.h"
|
||||
#include "../rugnux/ResultReport.h"
|
||||
#include "../rugnux/RugnuxOutputFiles.h"
|
||||
#include "SyntheticMergedReflections.h"
|
||||
|
||||
namespace {
|
||||
@@ -415,3 +417,52 @@ TEST_CASE("RugnuxResultFiles: the refinement's model, maps and blobs reach the w
|
||||
|
||||
std::filesystem::remove_all(dir);
|
||||
}
|
||||
|
||||
TEST_CASE("RugnuxResultFiles: the results report reaches the writer through the END block as rugnux writes it",
|
||||
"[rugnux_result_files]") {
|
||||
Logger logger("RugnuxResultFiles");
|
||||
const std::filesystem::path dir = std::filesystem::temp_directory_path() / "rugnux_result_files_report";
|
||||
std::filesystem::remove_all(dir);
|
||||
std::filesystem::create_directories(dir);
|
||||
const std::string original = (dir / "rugnux").string();
|
||||
const std::string written = (dir / "writer").string();
|
||||
|
||||
DiffractionExperiment x(DetJF(1));
|
||||
x.ImagesPerTrigger(600);
|
||||
ProcessResult result;
|
||||
result.images_processed = 600;
|
||||
result.consensus_cell = TEST_CELL;
|
||||
result.space_group = *gemmi::find_spacegroup_by_name("P 43 21 2");
|
||||
|
||||
// As rugnux writes it (WriteResultReport), and as the broker puts it in the END message
|
||||
// (JFJochReceiverRugnuxRotation): the text of the same document
|
||||
const ReportDocument doc = WriteResultReport(original, "in.h5", x, result, logger);
|
||||
EndMessage end{};
|
||||
end.report = RenderReportText(doc, false);
|
||||
std::vector<uint8_t> buffer(16 * 1024 * 1024);
|
||||
CBORStream2Serializer serializer(buffer.data(), buffer.size());
|
||||
REQUIRE_NOTHROW(serializer.SerializeSequenceEnd(end));
|
||||
auto deserialized = CBORStream2Deserialize(buffer.data(), serializer.GetBufferSize());
|
||||
REQUIRE(deserialized);
|
||||
REQUIRE(deserialized->end_message);
|
||||
REQUIRE(deserialized->end_message->report);
|
||||
WriteRugnuxResultFiles(*deserialized->end_message, written, false, logger);
|
||||
CHECK(ReadFile(written + "_report.txt") == ReadFile(original + "_report.txt"));
|
||||
CHECK(!std::filesystem::exists(written + "_report.txt.tmp"));
|
||||
CHECK(!std::filesystem::exists(written + ".mtz"));
|
||||
|
||||
// A report is tens of kB, longer than a string of the stream used to be read back
|
||||
end.report = std::string(100000, 'r') + "\n";
|
||||
CBORStream2Serializer long_serializer(buffer.data(), buffer.size());
|
||||
REQUIRE_NOTHROW(long_serializer.SerializeSequenceEnd(end));
|
||||
deserialized = CBORStream2Deserialize(buffer.data(), long_serializer.GetBufferSize());
|
||||
REQUIRE(deserialized->end_message->report);
|
||||
CHECK(*deserialized->end_message->report == *end.report);
|
||||
|
||||
// No report, no file
|
||||
EndMessage none{};
|
||||
WriteRugnuxResultFiles(none, written + "_none", false, logger);
|
||||
CHECK(!std::filesystem::exists(written + "_none_report.txt"));
|
||||
|
||||
std::filesystem::remove_all(dir);
|
||||
}
|
||||
@@ -184,6 +184,16 @@ void RenameIntoPlace(const std::string &tmp, const std::string &final_name, bool
|
||||
logger.Info("{} written, renamed into place at wall clock {:.3f} s", final_name, WallClockSeconds());
|
||||
}
|
||||
|
||||
// `text` as the file `filename`, written under a temporary name and renamed into place.
|
||||
void WriteTextFile(const std::string &text, const std::string &filename, bool overwrite, Logger &logger) {
|
||||
{
|
||||
std::ofstream f(filename + ".tmp", std::ofstream::out | std::ofstream::trunc);
|
||||
f.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
f << text;
|
||||
}
|
||||
RenameIntoPlace(filename + ".tmp", filename, overwrite, logger);
|
||||
}
|
||||
|
||||
// A file jfjoch_writer writes came out of two programs, and says so: Rugnux processed the data inside the
|
||||
// Jungfraujoch broker, which collected them. The reflection tables name Rugnux in their first HISTORY
|
||||
// record and the map tables carry none, so Rugnux is put first where nothing is; Jungfraujoch goes right
|
||||
@@ -260,16 +270,15 @@ void WriteRugnuxResultFiles(const EndMessage &end, const std::string &file_prefi
|
||||
logger.Error("Cannot write {}_refined_maps.mtz: {}", file_prefix, e.what());
|
||||
}
|
||||
try {
|
||||
if (end.blobs) {
|
||||
const std::string text = DensityBlobsJson(*end.blobs);
|
||||
{
|
||||
std::ofstream f(file_prefix + "_blobs.json.tmp", std::ofstream::out | std::ofstream::trunc);
|
||||
f.exceptions(std::ios::failbit | std::ios::badbit);
|
||||
f << text;
|
||||
}
|
||||
RenameIntoPlace(file_prefix + "_blobs.json.tmp", file_prefix + "_blobs.json", overwrite, logger);
|
||||
}
|
||||
if (end.blobs)
|
||||
WriteTextFile(DensityBlobsJson(*end.blobs), file_prefix + "_blobs.json", overwrite, logger);
|
||||
} catch (const std::exception &e) {
|
||||
logger.Error("Cannot write {}_blobs.json: {}", file_prefix, e.what());
|
||||
}
|
||||
try {
|
||||
if (end.report)
|
||||
WriteTextFile(*end.report, file_prefix + "_report.txt", overwrite, logger);
|
||||
} catch (const std::exception &e) {
|
||||
logger.Error("Cannot write {}_report.txt: {}", file_prefix, e.what());
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,8 @@ std::string DensityBlobsJson(const DensityBlobsMessage &blobs);
|
||||
|
||||
// The files above, from whichever blocks `end` carries, at <file_prefix>.mtz, _maps.mtz and _model.cif
|
||||
// (WritePlacedModel adds _model.pdb where a PDB can hold the cell), and the refinement's
|
||||
// _refined_model.cif (.pdb), _refined_maps.mtz and _blobs.json. Each is written under a temporary name and
|
||||
// renamed into place. An existing file is replaced only with `overwrite`; otherwise it is kept and the
|
||||
// _refined_model.cif (.pdb), _refined_maps.mtz and _blobs.json, and the results report _report.txt. Each is
|
||||
// written under a temporary name and renamed into place. An existing file is replaced only with `overwrite`; otherwise it is kept and the
|
||||
// new one is not written. Each names Jungfraujoch beside Rugnux as the software that made it: the MTZ
|
||||
// files in a HISTORY record, the models in their _software loop.
|
||||
void WriteRugnuxResultFiles(const EndMessage &end, const std::string &file_prefix, bool overwrite,
|
||||
|
||||
Reference in new issue
Block a user