-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved efficiency wrf_remove_urban #71
Conversation
Hi @dargueso, Thanks for this. I have just tried this development on the d02 data from Ireland. The Yet the application was killed in a following step:
I am not sure what is causing this. I'll try to have a look later on. |
Hi @matthiasdemuzere, |
Mmm ... interesting that it works for you? |
I checked in two other machines using python 3.9 and 3.8. It all went fine. |
Correct selection of tiles when near borders
…ere/w2w into optmize_remove_urb
for more information, see https://pre-commit.ci
Good morning, |
for more information, see https://pre-commit.ci
for more information, see https://pre-commit.ci
Almost there ... Except that test |
FYI @dargueso I just tried the d02 Ireland domain on another machine with more memory, and there all works as well. So it seems indeed to be a memory problem with my (older) laptop. I wonder whether there is something we can do in this respect? Any ideas @theendlessriver13? |
I'm not sure about the test failure on 3.7, would have to look a bit deeper into that. Regarding the memory, I think there is not much we can do, this is caused by rasterio (as previous issues showed). However, testing this patch on my machine against our Zaragoza example, it turns out to be actually slower than the previous version. I did the benchmark like this: #! /usr/bin/env python3
import sys
import time
import subprocess
def main() -> int:
times = []
n = sys.argv[1]
for i in range(int(n)):
print(f'\033[0;32mrun {i + 1}...\033[0m')
start = time.monotonic()
subprocess.run(('w2w', './sample_data', 'lcz_zaragoza.tif', 'geo_em.d04.nc'))
times.append(time.monotonic() - start)
print(f'best of {n}: {min(times)}')
return 0
if __name__ == '__main__':
raise SystemExit(main()) A best of 10 test returned this result:
so it looks like it almost got 2x slower?! It is always an option to run such processes in parallel utilizing multiple cores. Not sure if this is feasible though (also it will use more memory). Work could be distributed by latitude and finally joined together. |
this was run on |
@theendlessriver13, I get the same thing on my new laptop too. I tested the optimisation over large domains, not small/standard ones. It looks like the modifications I introduced slow down the whole process. I need to check this. |
maybe this also helps a little: currently 77.84% is spent in I think we somehow have to avoid looping over each coordinate; I am no expert at all in xarray, but maybe |
@theendlessriver13 thanks for the insight, very helpful! |
Good morning, Looking into the code in detail, the _calc_distance_coord had some issues with precision. The algorithm is know to have large rounding errors when using low floating precision on small distances. Thus, the selection of the nearest K points was not accurate enough. It wasn't a big deal when looking for a large number of K nearest neighbours, but it can be an issue when K is small. Furthermore, the new green fraction was not exactly as expected, although the error was again small when using large K. This part of the code has been revamped. We no longer use brute force to calculate distances, but KD trees, which makes the calculation much faster for both large and small domains. We were calculating distances from a given grid points to all points in the domain, and this is clearly an overkill that slows down the process. According to my test, the sample data is now processed in ~12 seconds and the Ireland domain is processed in ~370s. This is faster than the original version even for small domains. I wasn't able to remove the loops, but it is more efficient now. I need to make some tests and clear up the code before committing. I removed a test on the function that calculate distance with the previous method, so I guess we need to add a test for the new function. |
Using kdtree instead of brute force to search for N nearest neighbours to replace urban pixels with surrounding natural landscape. Added option to specify area size where the nearest grid points are searched (for optimization). Adapted tests. Removed ununsed functions.
for more information, see https://pre-commit.ci
Hi, Sample data -> best of 5: 10.821772583 I couldn't avoid loops, but it know loops over a much smaller number of items. Also it only iterates over one dimension not two nested loops. Results are not exactly the same as before, because distances are calculated slightly differently (see comment above). There is also a change in how the urban fraction of landusef is transferred to the new landuse. Before, it was searching again for nearest grid points with a slightly different condition (landusef for urban must be zero) and calculates the mode again. It is now much simpler, because it just grabs the urban fraction and adds it to the new dominant landuse category. It has passed tests, except pre-commit.ci (@theendlessriver13, can you help with this?) I think we should consider to label this with a new version number, because the output values are not the same. Cheers, |
4b7f6f0
to
5a01850
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could even speed this up more:
diff --git a/w2w/w2w.py b/w2w/w2w.py
index 18d9b43..76c47e7 100644
--- a/w2w/w2w.py
+++ b/w2w/w2w.py
@@ -12,6 +12,7 @@ import scipy.spatial as spatial
import os, sys
import argparse
from argparse import RawTextHelpFormatter
+from multiprocessing import cpu_count
import traceback
from typing import Dict, Any
from typing import List
@@ -405,7 +406,11 @@ def using_kdtree(
data['y'] = R * np.cos(phi) * np.sin(theta)
data['z'] = R * np.sin(phi)
tree = spatial.KDTree(data[['x', 'y', 'z']])
- distance, index = tree.query(data[['x', 'y', 'z']], k=kpoints)
+ distance, index = tree.query(
+ data[['x', 'y', 'z']],
+ k=kpoints,
+ workers=cpu_count() - 1
+ )
return distance, index
This only takes 7 seconds for me with the sample data. However maybe this should also become an argument then, if you want to avoid using too many cores on a powerful machine. I was not able to test this on the ireland data, but this should also reduce this significantly.
Interesting. @theendlessriver13: in case you want to test out with the Ireland files: geo_em.d02.nc and LCZ file. |
I checked on my machine, for the large Ireland domain:
So not too much time gained. On the good side: I am now able to process the large domain on my laptop, which was not possible before. So it seems much of the memory usage came from the old verrsion of |
Set dkd to _, because we don't access it. Added some comments
for more information, see https://pre-commit.ci
Yep, similar here. I think we should keep it simple considering the limited impact, otherwise we would need to add new options and document them. |
Yes, @theendlessriver13 and myself agree not to have this multiprocessor function. |
Added documentation of searching area for NPIX_NLC
for more information, see https://pre-commit.ci
This all looks good to me. Perhaps @theendlessriver13 can have a last look, and then we can merge into main (and update version and pypi?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking all good now, I just found one typo (not in the new stuff) and simplified the variable unpacking a bit.
Before releasing a new version to PyPi I'd like to take a look at the import(s) (sorting). I noticed, that this isn't working as excepted and there might actually be some unused imports we can remove.
Maybe I should also update the JOSS paper already? To add the info that is now added in README? I'll do this now ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just found 3 more typos, maybe we need to regenerate the pdf again and then this is ready to go.
Co-authored-by: Jonas Kittner <54631600+theendlessriver13@users.noreply.github.com>
Co-authored-by: Jonas Kittner <54631600+theendlessriver13@users.noreply.github.com>
The function that removes urban areas and replace it with the dominant surrounding land use has been modified to improve efficiency. For each urban grid point, the function searches for most frequent land use category from the closest NPIX_NLC points that are not water or urban. To decide which grids are the closest, the function computes the distance over the entire domain. This slows down the process considerably. Now it looks over a tile of 2xNPIX_NLC by 2xNPIX_NLC grid points. The computation time is reduced considerably. This wasn't an issue over small domains, but in @matthiasdemuzere's example over Ireland, it was (partly due to missing values that shouldn't be there). Using that input files example, the time is reduced to a 10% of the original required time. Other minor changes are also introduced, mostly to define auxiliary variables in a cleaner way.