@@ -1950,6 +1950,17 @@
Loading
1950 1950
1951 1951
            procedure = service.output
1952 1952
            procedure.__dict__["id"] = service.procedure_id
1953 +
1954 +
            # Copy the stdout/error from the service itself to its procedure
1955 +
            if service.stdout:
1956 +
                stdout = KVStore(data=service.stdout)
1957 +
                stdout_id = self.add_kvstore([stdout])["data"][0]
1958 +
                procedure.__dict__["stdout"] = stdout_id
1959 +
            if service.error:
1960 +
                error = KVStore(data=service.error)
1961 +
                error_id = self.add_kvstore([error])["data"][0]
1962 +
                procedure.__dict__["error"] = error_id
1963 +
1953 1964
            self.update_procedures([procedure])
1954 1965
1955 1966
            updated_count += 1

@@ -49,13 +49,13 @@
Loading
49 49
                if x["status"] != "ERROR":
50 50
                    continue
51 51
52 -
            self.logger.error("Error in service compute as follows:")
52 +
            self.logger.debug("Error in service compute as follows:")
53 53
            tasks = self.storage_socket.get_queue()["data"]
54 54
            for x in tasks:
55 55
                if "error" not in x:
56 56
                    continue
57 57
58 -
                self.logger.error(x["error"]["error_message"])
58 +
                self.logger.debug(x["error"]["error_message"])
59 59
60 60
            raise KeyError("All tasks did not execute successfully.")
61 61
        else:
@@ -127,6 +127,7 @@
Loading
127 127
128 128
    status: str = "WAITING"
129 129
    error: Optional[ComputeError] = None
130 +
    stdout: str = ""
130 131
    tag: Optional[str] = None
131 132
132 133
    # Sorting and priority

@@ -2,8 +2,10 @@
Loading
2 2
Wraps geometric procedures
3 3
"""
4 4
5 +
import io
5 6
import copy
6 7
import json
8 +
import contextlib
7 9
from typing import Any, Dict, List
8 10
9 11
import numpy as np
@@ -79,15 +81,21 @@
Loading
79 81
        meta["molecule_template"] = json.dumps(molecule_template)
80 82
81 83
        # Initiate torsiondrive meta
82 -
        meta["torsiondrive_state"] = td_api.create_initial_state(
83 -
            dihedrals=output.keywords.dihedrals,
84 -
            grid_spacing=output.keywords.grid_spacing,
85 -
            elements=molecule_template["symbols"],
86 -
            init_coords=[x.geometry for x in service_input.initial_molecule],
87 -
            dihedral_ranges=output.keywords.dihedral_ranges,
88 -
            energy_decrease_thresh=output.keywords.energy_decrease_thresh,
89 -
            energy_upper_limit=output.keywords.energy_upper_limit,
90 -
        )
84 +
        # The torsiondrive package uses print, so capture that using
85 +
        # contextlib
86 +
        td_stdout = io.StringIO()
87 +
        with contextlib.redirect_stdout(td_stdout):
88 +
            meta["torsiondrive_state"] = td_api.create_initial_state(
89 +
                dihedrals=output.keywords.dihedrals,
90 +
                grid_spacing=output.keywords.grid_spacing,
91 +
                elements=molecule_template["symbols"],
92 +
                init_coords=[x.geometry for x in service_input.initial_molecule],
93 +
                dihedral_ranges=output.keywords.dihedral_ranges,
94 +
                energy_decrease_thresh=output.keywords.energy_decrease_thresh,
95 +
                energy_upper_limit=output.keywords.energy_upper_limit,
96 +
            )
97 +
98 +
        meta["stdout"] = td_stdout.getvalue()
91 99
92 100
        # Build dihedral template
93 101
        dihedral_template = []
@@ -141,10 +149,16 @@
Loading
141 149
142 150
                task_results[key].append((mol_keys[0].geometry, mol_keys[1].geometry, ret["energies"][-1]))
143 151
144 -
        td_api.update_state(self.torsiondrive_state, task_results)
152 +
        # The torsiondrive package uses print, so capture that using
153 +
        # contextlib
154 +
        td_stdout = io.StringIO()
155 +
        with contextlib.redirect_stdout(td_stdout):
156 +
            td_api.update_state(self.torsiondrive_state, task_results)
157 +
158 +
            # Create new tasks from the current state
159 +
            next_tasks = td_api.next_jobs_from_state(self.torsiondrive_state, verbose=True)
145 160
146 -
        # Create new tasks from the current state
147 -
        next_tasks = td_api.next_jobs_from_state(self.torsiondrive_state, verbose=True)
161 +
        self.stdout += "\n" + td_stdout.getvalue()
148 162
149 163
        # All done
150 164
        if len(next_tasks) == 0:
@@ -205,7 +219,7 @@
Loading
205 219
206 220
    def update_output(self):
207 221
        """
208 -
        Finishes adding data to the TorsionDriveRecord object
222 +
        Adds data to the TorsionDriveRecord object
209 223
        """
210 224
        _check_td()
211 225
        from torsiondrive import td_api
@@ -229,4 +243,5 @@
Loading
229 243
                "optimization_history": history,
230 244
            }
231 245
        )
246 +
232 247
        return True

@@ -27,6 +27,9 @@
Loading
27 27
    # Output
28 28
    output: GridOptimizationRecord
29 29
30 +
    # Storage of the stdout of the torsiondrive package
31 +
    stdout: str = ""
32 +
30 33
    # Temporaries
31 34
    grid_optimizations: Dict[str, str] = {}
32 35
    seeds: Set[tuple] = set()
Files Coverage
qcfractal 77.32%
Project Totals (69 files) 77.32%
1
coverage:
2
  ignore:
3
    - */tests/*
4
    - qcfractal/dashboard/* # early state
5
    - qcfractal/alembic/* # difficult to test
6
    - qcfractal/_version.py
7
    - setup.py
8
  status:
9
    patch: false
10
    project:
11
      default:
12
        threshold: 80%
13
comment:
14
  layout: "header"
15
  require_changes: false
16
  branches: null
17
  behavior: once
18
  flags: null
19
  paths: null
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading