Skip to content

Commit 8a16f51

Browse files
committed
Allow snapshots and restarts to be reused directly
1 parent 229bd4b commit 8a16f51

7 files changed

Lines changed: 128 additions & 17 deletions

File tree

examples/parameter_example.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ InitialConditions:
240240

241241
# Parameters controlling restarts
242242
Restarts:
243+
restart: 0 # (Optional) whether to continue from existing restart files. Equivalent to passing --restart on the command line.
243244
enable: 1 # (Optional) whether to enable dumping restarts at fixed intervals.
244245
save: 1 # (Optional) whether to save copies of the previous set of restart files (named .prev)
245246
onexit: 0 # (Optional) whether to dump restarts on exit (*needs enable*)

src/engine_config.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,41 @@ void engine_config(int restart, int fof, struct engine *e,
234234
/* Welcome message */
235235
if (e->nodeID == 0) message("Running simulation '%s'.", e->run_name);
236236

237+
/* Values that are safe to change between restart cycles. The integer
238+
* timeline itself is not changed here; it is part of the restart state. */
239+
if (restart && !fof) {
240+
if (!(e->policy & engine_policy_cosmology)) {
241+
const double time_begin =
242+
parser_get_param_double(params, "TimeIntegration:time_begin");
243+
const double time_end =
244+
parser_get_param_double(params, "TimeIntegration:time_end");
245+
246+
if (time_begin != e->time_begin || time_end != e->time_end) {
247+
if (e->nodeID == 0)
248+
message(
249+
"WARNING: ignoring changed TimeIntegration:time_begin/time_end "
250+
"on restart; the saved integer timeline is preserved.");
251+
}
252+
}
253+
254+
e->dt_min = parser_get_param_double(params, "TimeIntegration:dt_min");
255+
e->dt_max = parser_get_param_double(params, "TimeIntegration:dt_max");
256+
257+
e->a_first_snapshot = parser_get_opt_param_double(
258+
params, "Snapshots:scale_factor_first", e->a_first_snapshot);
259+
e->time_first_snapshot = parser_get_opt_param_double(
260+
params, "Snapshots:time_first", e->time_first_snapshot);
261+
e->delta_time_snapshot = parser_get_opt_param_double(
262+
params, "Snapshots:delta_time", e->delta_time_snapshot);
263+
264+
e->a_first_statistics = parser_get_opt_param_double(
265+
params, "Statistics:scale_factor_first", e->a_first_statistics);
266+
e->time_first_statistics = parser_get_opt_param_double(
267+
params, "Statistics:time_first", e->time_first_statistics);
268+
e->delta_time_statistics =
269+
parser_get_param_double(params, "Statistics:delta_time");
270+
}
271+
237272
/* Check-pointing properties */
238273

239274
e->restart_stop_steps =

src/io_properties.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,22 @@ INLINE static void safe_strcpy(char *restrict dst, const char *restrict src,
209209
dst[dst_len - 1] = '\0';
210210
}
211211

212+
/**
213+
* @brief Return a snapshot-output alias for legacy/singular IC field names.
214+
*
215+
* SWIFT follows GADGET-style singular names in ICs but writes plural field
216+
* names in snapshots. Accepting these aliases lets a snapshot be reused as an
217+
* IC without an external conversion step.
218+
*/
219+
INLINE static const char *io_get_input_field_alias(const char *name) {
220+
221+
if (strcmp(name, "SmoothingLength") == 0) return "SmoothingLengths";
222+
if (strcmp(name, "InternalEnergy") == 0) return "InternalEnergies";
223+
if (strcmp(name, "Density") == 0) return "Densities";
224+
225+
return NULL;
226+
}
227+
212228
/**
213229
* @brief Constructs an #io_props from its parameters
214230
*

src/parallel_io.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,28 @@ void read_array_parallel(hid_t grp, struct io_props props, size_t N,
256256
const size_t typeSize = io_sizeof_type(props.type);
257257
const size_t copySize = typeSize * props.dimension;
258258

259+
const char *dataset_name = props.name;
260+
259261
/* Check whether the dataspace exists or not */
260-
const htri_t exist = H5Lexists(grp, props.name, 0);
262+
htri_t exist = H5Lexists(grp, dataset_name, 0);
261263
if (exist < 0) {
262-
error("Error while checking the existence of data set '%s'.", props.name);
263-
} else if (exist == 0) {
264+
error("Error while checking the existence of data set '%s'.", dataset_name);
265+
}
266+
267+
if (exist == 0) {
268+
const char *alias = io_get_input_field_alias(props.name);
269+
if (alias != NULL) {
270+
const htri_t alias_exist = H5Lexists(grp, alias, 0);
271+
if (alias_exist < 0) {
272+
error("Error while checking the existence of data set '%s'.", alias);
273+
} else if (alias_exist > 0) {
274+
dataset_name = alias;
275+
exist = alias_exist;
276+
}
277+
}
278+
}
279+
280+
if (exist == 0) {
264281
if (props.importance == COMPULSORY) {
265282
error("Compulsory data set '%s' not present in the file.", props.name);
266283
} else {
@@ -279,8 +296,8 @@ void read_array_parallel(hid_t grp, struct io_props props, size_t N,
279296
}
280297

281298
/* Open data space in file */
282-
const hid_t h_data = H5Dopen2(grp, props.name, H5P_DEFAULT);
283-
if (h_data < 0) error("Error while opening data space '%s'.", props.name);
299+
const hid_t h_data = H5Dopen2(grp, dataset_name, H5P_DEFAULT);
300+
if (h_data < 0) error("Error while opening data space '%s'.", dataset_name);
284301

285302
/* Parallel-HDF5 1.10.2 incorrectly reads data that was compressed */
286303
/* We detect this here and crash with an error message instead of */

src/serial_io.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,28 @@ void read_array_serial(hid_t grp, const struct io_props props, size_t N,
9595
const size_t copySize = typeSize * props.dimension;
9696
const size_t num_elements = N * props.dimension;
9797

98+
const char *dataset_name = props.name;
99+
98100
/* Check whether the dataspace exists or not */
99-
const htri_t exist = H5Lexists(grp, props.name, 0);
101+
htri_t exist = H5Lexists(grp, dataset_name, 0);
100102
if (exist < 0) {
101-
error("Error while checking the existence of data set '%s'.", props.name);
102-
} else if (exist == 0) {
103+
error("Error while checking the existence of data set '%s'.", dataset_name);
104+
}
105+
106+
if (exist == 0) {
107+
const char *alias = io_get_input_field_alias(props.name);
108+
if (alias != NULL) {
109+
const htri_t alias_exist = H5Lexists(grp, alias, 0);
110+
if (alias_exist < 0) {
111+
error("Error while checking the existence of data set '%s'.", alias);
112+
} else if (alias_exist > 0) {
113+
dataset_name = alias;
114+
exist = alias_exist;
115+
}
116+
}
117+
}
118+
119+
if (exist == 0) {
103120
if (props.importance == COMPULSORY) {
104121
error("Compulsory data set '%s' not present in the file.", props.name);
105122
} else {
@@ -122,8 +139,8 @@ void read_array_serial(hid_t grp, const struct io_props props, size_t N,
122139
/* fflush(stdout); */
123140

124141
/* Open data space */
125-
const hid_t h_data = H5Dopen(grp, props.name, H5P_DEFAULT);
126-
if (h_data < 0) error("Error while opening data space '%s'.", props.name);
142+
const hid_t h_data = H5Dopen(grp, dataset_name, H5P_DEFAULT);
143+
if (h_data < 0) error("Error while opening data space '%s'.", dataset_name);
127144

128145
/* Allocate temporary buffer */
129146
void *temp = malloc(num_elements * typeSize);
@@ -158,7 +175,7 @@ void read_array_serial(hid_t grp, const struct io_props props, size_t N,
158175
/* Using HDF5 dataspaces would be better */
159176
const hid_t h_err = H5Dread(h_data, io_hdf5_type(props.type), h_memspace,
160177
h_filespace, H5P_DEFAULT, temp);
161-
if (h_err < 0) error("Error while reading data array '%s'.", props.name);
178+
if (h_err < 0) error("Error while reading data array '%s'.", dataset_name);
162179

163180
/* Unit conversion if necessary */
164181
const double factor =

src/single_io.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,28 @@ void read_array_single(hid_t h_grp, const struct io_props props, size_t N,
9292
const size_t copySize = typeSize * props.dimension;
9393
const size_t num_elements = N * props.dimension;
9494

95+
const char *dataset_name = props.name;
96+
9597
/* Check whether the dataspace exists or not */
96-
const htri_t exist = H5Lexists(h_grp, props.name, 0);
98+
htri_t exist = H5Lexists(h_grp, dataset_name, 0);
9799
if (exist < 0) {
98-
error("Error while checking the existence of data set '%s'.", props.name);
99-
} else if (exist == 0) {
100+
error("Error while checking the existence of data set '%s'.", dataset_name);
101+
}
102+
103+
if (exist == 0) {
104+
const char *alias = io_get_input_field_alias(props.name);
105+
if (alias != NULL) {
106+
const htri_t alias_exist = H5Lexists(h_grp, alias, 0);
107+
if (alias_exist < 0) {
108+
error("Error while checking the existence of data set '%s'.", alias);
109+
} else if (alias_exist > 0) {
110+
dataset_name = alias;
111+
exist = alias_exist;
112+
}
113+
}
114+
}
115+
116+
if (exist == 0) {
100117
if (props.importance == COMPULSORY) {
101118
error("Compulsory data set '%s' not present in the file.", props.name);
102119
} else {
@@ -119,8 +136,8 @@ void read_array_single(hid_t h_grp, const struct io_props props, size_t N,
119136
/* props.name); */
120137

121138
/* Open data space */
122-
const hid_t h_data = H5Dopen(h_grp, props.name, H5P_DEFAULT);
123-
if (h_data < 0) error("Error while opening data space '%s'.", props.name);
139+
const hid_t h_data = H5Dopen(h_grp, dataset_name, H5P_DEFAULT);
140+
if (h_data < 0) error("Error while opening data space '%s'.", dataset_name);
124141

125142
/* Allocate temporary buffer */
126143
void *temp = malloc(num_elements * typeSize);
@@ -131,7 +148,7 @@ void read_array_single(hid_t h_grp, const struct io_props props, size_t N,
131148
/* Using HDF5 dataspaces would be better */
132149
const hid_t h_err = H5Dread(h_data, io_hdf5_type(props.type), H5S_ALL,
133150
H5S_ALL, H5P_DEFAULT, temp);
134-
if (h_err < 0) error("Error while reading data array '%s'.", props.name);
151+
if (h_err < 0) error("Error while reading data array '%s'.", dataset_name);
135152

136153
/* Unit conversion if necessary */
137154
const double unit_factor =

swift.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,14 @@ int main(int argc, char *argv[]) {
843843
MPI_Bcast(params, sizeof(struct swift_params), MPI_BYTE, 0, MPI_COMM_WORLD);
844844
#endif
845845

846+
/* Allow restarts to be requested directly from the parameter file. */
847+
const int restart_from_params =
848+
parser_get_opt_param_int(params, "Restarts:restart", 0);
849+
if (restart_from_params) {
850+
if (myrank == 0) message("Restart requested by parameter file.");
851+
restart = 1;
852+
}
853+
846854
/* Read the provided output selection file, if available. Best to
847855
* do this after broadcasting the parameters as there may be code in this
848856
* function that is repeated on each node based on the parameter file. */

0 commit comments

Comments
 (0)