|
1 | 1 | from pySDC.core.Hooks import hooks |
| 2 | +import pickle |
| 3 | +import os |
| 4 | +import numpy as np |
2 | 5 |
|
3 | 6 |
|
4 | 7 | class LogSolution(hooks): |
@@ -63,3 +66,79 @@ def post_iteration(self, step, level_number): |
63 | 66 | type='u', |
64 | 67 | value=L.uend, |
65 | 68 | ) |
| 69 | + |
| 70 | + |
| 71 | +class LogToFile(hooks): |
| 72 | + r""" |
| 73 | + Hook for logging the solution to file after the step using pickle. |
| 74 | +
|
| 75 | + Please configure the hook to your liking by manipulating class attributes. |
| 76 | + You must set a custom path to a directory like so: |
| 77 | +
|
| 78 | + ``` |
| 79 | + LogToFile.path = '/my/directory/' |
| 80 | + ``` |
| 81 | +
|
| 82 | + Keep in mind that the hook will overwrite files without warning! |
| 83 | + You can give a custom file name by setting the ``file_name`` class attribute and give a custom way of rendering the |
| 84 | + index associated with individual files by giving a different lambda function ``format_index`` class attribute. This |
| 85 | + lambda should accept one index and return one string. |
| 86 | +
|
| 87 | + You can also give a custom ``logging_condition`` lambda, accepting the current level if you want to log selectively. |
| 88 | +
|
| 89 | + Importantly, you may need to change ``process_solution``. By default, this will return a numpy view of the solution. |
| 90 | + Of course, if you are not using numpy, you need to change this. Again, this is a lambda accepting the level. |
| 91 | +
|
| 92 | + After the fact, you can use the classmethod `get_path` to get the path to a certain data or the `load` function to |
| 93 | + directly load the solution at a given index. Just configure the hook like you did when you recorded the data |
| 94 | + beforehand. |
| 95 | +
|
| 96 | + Finally, be aware that using this hook with MPI parallel runs may lead to different tasks overwriting files. Make |
| 97 | + sure to give a different `file_name` for each task that writes files. |
| 98 | + """ |
| 99 | + |
| 100 | + path = None |
| 101 | + file_name = 'solution' |
| 102 | + logging_condition = lambda L: True |
| 103 | + process_solution = lambda L: {'t': L.time + L.dt, 'u': L.uend.view(np.ndarray)} |
| 104 | + format_index = lambda index: f'{index:06d}' |
| 105 | + |
| 106 | + def __init__(self): |
| 107 | + super().__init__() |
| 108 | + self.counter = 0 |
| 109 | + |
| 110 | + if self.path is None: |
| 111 | + raise ValueError('Please set a path for logging as the class attribute `LogToFile.path`!') |
| 112 | + |
| 113 | + if os.path.isfile(self.path): |
| 114 | + raise ValueError( |
| 115 | + f'{self.path!r} is not a valid path to log to because a file of the same name exists. Please supply a directory' |
| 116 | + ) |
| 117 | + |
| 118 | + if not os.path.isdir(self.path): |
| 119 | + os.mkdir(self.path) |
| 120 | + |
| 121 | + def post_step(self, step, level_number): |
| 122 | + if level_number > 0: |
| 123 | + return None |
| 124 | + |
| 125 | + L = step.levels[level_number] |
| 126 | + |
| 127 | + if type(self).logging_condition(L): |
| 128 | + path = self.get_path(self.counter) |
| 129 | + data = type(self).process_solution(L) |
| 130 | + |
| 131 | + with open(path, 'wb') as file: |
| 132 | + pickle.dump(data, file) |
| 133 | + |
| 134 | + self.counter += 1 |
| 135 | + |
| 136 | + @classmethod |
| 137 | + def get_path(cls, index): |
| 138 | + return f'{cls.path}/{cls.file_name}_{cls.format_index(index)}.pickle' |
| 139 | + |
| 140 | + @classmethod |
| 141 | + def load(cls, index): |
| 142 | + path = cls.get_path(index) |
| 143 | + with open(path, 'rb') as file: |
| 144 | + return pickle.load(file) |
0 commit comments