diff --git a/README.md b/README.md index bf430f7ef..e94ef59aa 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Full test](https://github.com/DHI/mikeio/actions/workflows/full_test.yml/badge.svg)](https://github.com/DHI/mikeio/actions/workflows/full_test.yml) [![PyPI version](https://badge.fury.io/py/mikeio.svg)](https://badge.fury.io/py/mikeio) ![OS](https://img.shields.io/badge/OS-Windows%20%7C%20Linux-blue) +[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://github.com/DHI/mikeio/blob/main/License.txt) Read, write and manipulate dfs0, dfs1, dfs2, dfs3, dfsu and mesh files. diff --git a/docs/index.qmd b/docs/index.qmd index cdd29ba39..5222fcbf8 100644 --- a/docs/index.qmd +++ b/docs/index.qmd @@ -4,6 +4,7 @@ # MIKE IO: input/output of MIKE files in Python ![Python version](https://img.shields.io/pypi/pyversions/mikeio.svg) [![PyPI version](https://badge.fury.io/py/mikeio.svg)](https://badge.fury.io/py/mikeio) +[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://github.com/DHI/mikeio/blob/main/License.txt) Read, write and manipulate dfs0, dfs1, dfs2, dfs3, dfsu and mesh files. diff --git a/mikeio/generic.py b/mikeio/generic.py index 555467435..338eb17b2 100644 --- a/mikeio/generic.py +++ b/mikeio/generic.py @@ -526,8 +526,8 @@ def concat( darray = d.astype(np.float32) dfs_o.WriteItemTimeStepNext(0, darray) - end_time = ( - start_time + timedelta(seconds=timestep * dt) + end_time = start_time + timedelta( + seconds=timestep * dt ) # reuse last timestep since there is no EndDateTime attribute in t_axis. dfs_i.Close() @@ -552,6 +552,82 @@ def concat( ) # get end time from current file dfs_i.Close() + if keep == "average": + if i == 0: + # For first file, write all timesteps normally + for timestep in range(n_time_steps): + current_time = start_time + timedelta(seconds=timestep * dt) + + for item in range(n_items): + itemdata = dfs_i.ReadItemTimeStep(int(item + 1), int(timestep)) + darray = itemdata.Data.astype(np.float32) + dfs_o.WriteItemTimeStepNext(0, darray) + end_time = start_time + timedelta(seconds=(n_time_steps - 1) * dt) + first_start_time = start_time # Store the start time of first file + dfs_i.Close() + else: + # For subsequent files, we need to handle overlapping periods + # Calculate overlap in timesteps + if start_time <= end_time: + + overlap_end = int((end_time - start_time).total_seconds() / dt) + 1 + + # Read current file data + for timestep in range(n_time_steps): + current_time = start_time + timedelta(seconds=timestep * dt) + + for item in range(n_items): + itemdata = dfs_i.ReadItemTimeStep( + int(item + 1), int(timestep) + ) + current_data = itemdata.Data.astype(np.float32) + + if ( + int(timestep) <= overlap_end + ): # Convert back to int for comparison + # In overlapping period + existing_pos = int( + (current_time - first_start_time).total_seconds() + / dt + - 1 + ) + if existing_pos >= 0: + # Read existing data + dfs_o.Flush() + temp_dfs = DfsFileFactory.DfsGenericOpen( + str(outfilename) + ) + existing_data = temp_dfs.ReadItemTimeStep( + int(item + 1), int(existing_pos) + ).Data + temp_dfs.Close() + + # Calculate average + averaged_data = (existing_data + current_data) / 2 + # Write averaged data + # dfs_o.WriteItemTimeStep( + # item + 1, existing_pos, 0, averaged_data + # ) # This is what we need, but it does not wo + dfs_o.WriteItemTimeStepNext(0, averaged_data) + + else: + # After overlap period - write new data + dfs_o.WriteItemTimeStepNext(0, current_data) + + else: + # No overlap - write all timesteps + for timestep in range(n_time_steps): + current_time = start_time + timedelta(seconds=timestep * dt) + for item in range(n_items): + itemdata = dfs_i.ReadItemTimeStep( + str(item + 1), str(timestep) + ) + darray = itemdata.Data.astype(np.float32) + dfs_o.WriteItemTimeStepNext(0, darray) + + end_time = start_time + timedelta(seconds=(n_time_steps - 1) * dt) + dfs_i.Close() + dfs_o.Close() diff --git a/tests/test_generic.py b/tests/test_generic.py index aa3112923..32b0a95c1 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -235,6 +235,17 @@ def test_concat_overlapping(tmp_path): assert len(ds.time) == 145 +def test_concat_average(tmp_path): + infilename_a = "generic_concat_file1.dfs2" + infilename_b = "generic_concat_file2.dfs2" + fp = tmp_path / "concat.dfs1" + + mikeio.generic.concat([infilename_a, infilename_b], fp, keep="average") + + ds = mikeio.read(fp) + assert ds[0][2].values[0][0] == 51.5 + + def test_concat_files_gap_fail(tmp_path): infilename_a = "tests/testdata/tide1.dfs1" infilename_b = "tests/testdata/tide4.dfs1" diff --git a/tests/testdata/generic_concat_file1.dfs2 b/tests/testdata/generic_concat_file1.dfs2 new file mode 100644 index 000000000..04b02c01c Binary files /dev/null and b/tests/testdata/generic_concat_file1.dfs2 differ diff --git a/tests/testdata/generic_concat_file2.dfs2 b/tests/testdata/generic_concat_file2.dfs2 new file mode 100644 index 000000000..29e1fd676 Binary files /dev/null and b/tests/testdata/generic_concat_file2.dfs2 differ